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 | 70 | virtual void write(unsigned char* data, size_t len) = 0; |
| 71 | 71 | QPDF_DLL |
| 72 | 72 | virtual void finish() = 0; |
| 73 | + QPDF_DLL | |
| 74 | + std::string getIdentifier() const; | |
| 73 | 75 | |
| 74 | 76 | protected: |
| 75 | 77 | Pipeline* getNext(bool allow_null = false); | ... | ... |
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -473,6 +473,34 @@ class QPDFWriter |
| 473 | 473 | |
| 474 | 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 | 504 | unsigned int bytesNeeded(long long n); |
| 477 | 505 | void writeBinary(unsigned long long val, unsigned int bytes); |
| 478 | 506 | void writeString(std::string const& str); |
| ... | ... | @@ -560,24 +588,17 @@ class QPDFWriter |
| 560 | 588 | int calculateXrefStreamPadding(qpdf_offset_t xref_bytes); |
| 561 | 589 | |
| 562 | 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 | 594 | Pipeline* pushPipeline(Pipeline*); |
| 567 | - void activatePipelineStack(); | |
| 595 | + void activatePipelineStack(PipelinePopper&); | |
| 568 | 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 | 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 | 602 | void computeDeterministicIDData(); |
| 582 | 603 | |
| 583 | 604 | void discardGeneration(std::map<QPDFObjGen, int> const& in, |
| ... | ... | @@ -654,6 +675,7 @@ class QPDFWriter |
| 654 | 675 | std::map<QPDFObjGen, int> object_to_object_stream; |
| 655 | 676 | std::map<int, std::set<QPDFObjGen> > object_stream_to_objects; |
| 656 | 677 | std::list<Pipeline*> pipeline_stack; |
| 678 | + unsigned long long next_stack_id; | |
| 657 | 679 | bool deterministic_id; |
| 658 | 680 | Pl_MD5* md5_pipeline; |
| 659 | 681 | std::string deterministic_id_data; | ... | ... |
libqpdf/Pipeline.cc
libqpdf/QPDFWriter.cc
| ... | ... | @@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF& pdf) : |
| 63 | 63 | cur_stream_length(0), |
| 64 | 64 | added_newline(false), |
| 65 | 65 | max_ostream_index(0), |
| 66 | + next_stack_id(0), | |
| 66 | 67 | deterministic_id(false), |
| 67 | 68 | md5_pipeline(0), |
| 68 | 69 | did_write_setup(false), |
| ... | ... | @@ -1049,36 +1050,51 @@ QPDFWriter::pushPipeline(Pipeline* p) |
| 1049 | 1050 | void |
| 1050 | 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 | 1054 | this->m->to_delete.push_back(this->m->pipeline); |
| 1054 | 1055 | this->m->pipeline_stack.push_back(this->m->pipeline); |
| 1055 | 1056 | } |
| 1056 | 1057 | |
| 1057 | 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 | 1066 | this->m->pipeline_stack.push_back(c); |
| 1062 | 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 | 1098 | Pl_Buffer* buf = dynamic_cast<Pl_Buffer*>(p); |
| 1083 | 1099 | if (bp && buf) |
| 1084 | 1100 | { |
| ... | ... | @@ -1086,7 +1102,7 @@ QPDFWriter::popPipelineStack(PointerHolder<Buffer>* bp) |
| 1086 | 1102 | } |
| 1087 | 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 | 1108 | void |
| ... | ... | @@ -1103,7 +1119,7 @@ QPDFWriter::adjustAESStreamLength(size_t& length) |
| 1103 | 1119 | } |
| 1104 | 1120 | |
| 1105 | 1121 | void |
| 1106 | -QPDFWriter::pushEncryptionFilter() | |
| 1122 | +QPDFWriter::pushEncryptionFilter(PipelinePopper& pp) | |
| 1107 | 1123 | { |
| 1108 | 1124 | if (this->m->encrypted && (! this->m->cur_data_key.empty())) |
| 1109 | 1125 | { |
| ... | ... | @@ -1125,18 +1141,18 @@ QPDFWriter::pushEncryptionFilter() |
| 1125 | 1141 | } |
| 1126 | 1142 | // Must call this unconditionally so we can call popPipelineStack |
| 1127 | 1143 | // to balance pushEncryptionFilter(). |
| 1128 | - activatePipelineStack(); | |
| 1144 | + activatePipelineStack(pp); | |
| 1129 | 1145 | } |
| 1130 | 1146 | |
| 1131 | 1147 | void |
| 1132 | -QPDFWriter::pushDiscardFilter() | |
| 1148 | +QPDFWriter::pushDiscardFilter(PipelinePopper& pp) | |
| 1133 | 1149 | { |
| 1134 | 1150 | pushPipeline(new Pl_Discard()); |
| 1135 | - activatePipelineStack(); | |
| 1151 | + activatePipelineStack(pp); | |
| 1136 | 1152 | } |
| 1137 | 1153 | |
| 1138 | 1154 | void |
| 1139 | -QPDFWriter::pushMD5Pipeline() | |
| 1155 | +QPDFWriter::pushMD5Pipeline(PipelinePopper& pp) | |
| 1140 | 1156 | { |
| 1141 | 1157 | if (! this->m->id2.empty()) |
| 1142 | 1158 | { |
| ... | ... | @@ -1153,7 +1169,7 @@ QPDFWriter::pushMD5Pipeline() |
| 1153 | 1169 | // Special case code in popPipelineStack clears this->m->md5_pipeline |
| 1154 | 1170 | // upon deletion. |
| 1155 | 1171 | pushPipeline(this->m->md5_pipeline); |
| 1156 | - activatePipelineStack(); | |
| 1172 | + activatePipelineStack(pp); | |
| 1157 | 1173 | } |
| 1158 | 1174 | |
| 1159 | 1175 | void |
| ... | ... | @@ -1769,8 +1785,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, |
| 1769 | 1785 | for (int attempt = 1; attempt <= 2; ++attempt) |
| 1770 | 1786 | { |
| 1771 | 1787 | pushPipeline(new Pl_Buffer("stream data")); |
| 1772 | - activatePipelineStack(); | |
| 1773 | - | |
| 1788 | + PipelinePopper pp_stream_data(this, &stream_data); | |
| 1789 | + activatePipelineStack(pp_stream_data); | |
| 1774 | 1790 | filtered = |
| 1775 | 1791 | object.pipeStreamData( |
| 1776 | 1792 | this->m->pipeline, |
| ... | ... | @@ -1779,7 +1795,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, |
| 1779 | 1795 | (filter |
| 1780 | 1796 | ? (uncompress ? qpdf_dl_all : this->m->stream_decode_level) |
| 1781 | 1797 | : qpdf_dl_none), false, (attempt == 1)); |
| 1782 | - popPipelineStack(&stream_data); | |
| 1783 | 1798 | if (filter && (! filtered)) |
| 1784 | 1799 | { |
| 1785 | 1800 | // Try again |
| ... | ... | @@ -1808,11 +1823,14 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, |
| 1808 | 1823 | adjustAESStreamLength(this->m->cur_stream_length); |
| 1809 | 1824 | unparseObject(stream_dict, 0, flags, |
| 1810 | 1825 | this->m->cur_stream_length, compress); |
| 1826 | + unsigned char last_char = '\0'; | |
| 1811 | 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 | 1835 | if (this->m->newline_before_endstream || |
| 1818 | 1836 | (this->m->qdf_mode && (last_char != '\n'))) |
| ... | ... | @@ -1911,9 +1929,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1911 | 1929 | bool compressed = false; |
| 1912 | 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 | 1934 | if (pass == 1) |
| 1915 | 1935 | { |
| 1916 | - pushDiscardFilter(); | |
| 1936 | + pushDiscardFilter(pp_ostream); | |
| 1917 | 1937 | } |
| 1918 | 1938 | else |
| 1919 | 1939 | { |
| ... | ... | @@ -1928,10 +1948,12 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1928 | 1948 | |
| 1929 | 1949 | // Take one pass at writing pairs of numbers so we can get |
| 1930 | 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 | 1958 | // Set up a stream to write the stream data into a buffer. |
| 1937 | 1959 | Pipeline* next = pushPipeline(new Pl_Buffer("object stream")); |
| ... | ... | @@ -1944,7 +1966,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1944 | 1966 | new Pl_Flate("compress object stream", next, |
| 1945 | 1967 | Pl_Flate::a_deflate)); |
| 1946 | 1968 | } |
| 1947 | - activatePipelineStack(); | |
| 1969 | + activatePipelineStack(pp_ostream); | |
| 1948 | 1970 | writeObjectStreamOffsets(offsets, first_obj); |
| 1949 | 1971 | } |
| 1950 | 1972 | |
| ... | ... | @@ -1994,9 +2016,6 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1994 | 2016 | |
| 1995 | 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 | 2021 | // Write the object |
| ... | ... | @@ -2037,9 +2056,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 2037 | 2056 | { |
| 2038 | 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 | 2064 | if (this->m->newline_before_endstream) |
| 2044 | 2065 | { |
| 2045 | 2066 | writeString("\n"); |
| ... | ... | @@ -2779,10 +2800,13 @@ QPDFWriter::writeHintStream(int hint_id) |
| 2779 | 2800 | { |
| 2780 | 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 | 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 | 2920 | new Pl_PNGFilter( |
| 2897 | 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 | 2966 | openObject(xref_id); |
| 2941 | 2967 | writeString("<<"); |
| ... | ... | @@ -3156,6 +3182,8 @@ QPDFWriter::writeLinearized() |
| 3156 | 3182 | // Write file in two passes. Part numbers refer to PDF spec 1.4. |
| 3157 | 3183 | |
| 3158 | 3184 | FILE* lin_pass1_file = 0; |
| 3185 | + PointerHolder<PipelinePopper> pp_pass1 = new PipelinePopper(this); | |
| 3186 | + PointerHolder<PipelinePopper> pp_md5 = new PipelinePopper(this); | |
| 3159 | 3187 | for (int pass = 1; pass <= 2; ++pass) |
| 3160 | 3188 | { |
| 3161 | 3189 | if (pass == 1) |
| ... | ... | @@ -3167,15 +3195,15 @@ QPDFWriter::writeLinearized() |
| 3167 | 3195 | this->m->lin_pass1_filename.c_str(), "wb"); |
| 3168 | 3196 | pushPipeline( |
| 3169 | 3197 | new Pl_StdioFile("linearization pass1", lin_pass1_file)); |
| 3170 | - activatePipelineStack(); | |
| 3198 | + activatePipelineStack(*pp_pass1); | |
| 3171 | 3199 | } |
| 3172 | 3200 | else |
| 3173 | 3201 | { |
| 3174 | - pushDiscardFilter(); | |
| 3202 | + pushDiscardFilter(*pp_pass1); | |
| 3175 | 3203 | } |
| 3176 | 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 | 3420 | QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", |
| 3393 | 3421 | need_xref_stream ? 0 : 1); |
| 3394 | 3422 | computeDeterministicIDData(); |
| 3395 | - popPipelineStack(); | |
| 3423 | + pp_md5 = 0; | |
| 3396 | 3424 | assert(this->m->md5_pipeline == 0); |
| 3397 | 3425 | } |
| 3398 | 3426 | |
| 3399 | 3427 | // Close first pass pipeline |
| 3400 | 3428 | file_size = this->m->pipeline->getCount(); |
| 3401 | - popPipelineStack(); | |
| 3429 | + pp_pass1 = 0; | |
| 3402 | 3430 | |
| 3403 | 3431 | // Save hint offset since it will be set to zero by |
| 3404 | 3432 | // calling openObject. |
| 3405 | 3433 | qpdf_offset_t hint_offset = this->m->xref[hint_id].getOffset(); |
| 3406 | 3434 | |
| 3407 | 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 | 3442 | hint_length = QIntC::to_offset(hint_buffer->getSize()); |
| 3413 | 3443 | |
| 3414 | 3444 | // Restore hint offset |
| ... | ... | @@ -3541,9 +3571,10 @@ QPDFWriter::registerProgressReporter(PointerHolder<ProgressReporter> pr) |
| 3541 | 3571 | void |
| 3542 | 3572 | QPDFWriter::writeStandard() |
| 3543 | 3573 | { |
| 3574 | + PointerHolder<PipelinePopper> pp_md5 = new PipelinePopper(this); | |
| 3544 | 3575 | if (this->m->deterministic_id) |
| 3545 | 3576 | { |
| 3546 | - pushMD5Pipeline(); | |
| 3577 | + pushMD5Pipeline(*pp_md5); | |
| 3547 | 3578 | } |
| 3548 | 3579 | |
| 3549 | 3580 | // Start writing |
| ... | ... | @@ -3597,7 +3628,7 @@ QPDFWriter::writeStandard() |
| 3597 | 3628 | { |
| 3598 | 3629 | QTC::TC("qpdf", "QPDFWriter standard deterministic ID", |
| 3599 | 3630 | this->m->object_stream_to_objects.empty() ? 0 : 1); |
| 3600 | - popPipelineStack(); | |
| 3631 | + pp_md5 = 0; | |
| 3601 | 3632 | assert(this->m->md5_pipeline == 0); |
| 3602 | 3633 | } |
| 3603 | 3634 | } | ... | ... |