Commit 314bd3ebe03280a2cc22eba7527e09b2a179373d

Authored by m-holger
Committed by GitHub
2 parents 65e7e3db 2033b376

Merge pull request #1406 from m-holger/count

Tune QPDFWriter writes to Pl_Count
include/qpdf/QPDF.hh
@@ -879,7 +879,7 @@ class QPDF @@ -879,7 +879,7 @@ class QPDF
879 void generateHintStream( 879 void generateHintStream(
880 QPDFWriter::NewObjTable const& new_obj, 880 QPDFWriter::NewObjTable const& new_obj,
881 QPDFWriter::ObjTable const& obj, 881 QPDFWriter::ObjTable const& obj,
882 - std::shared_ptr<Buffer>& hint_stream, 882 + std::string& hint_stream,
883 int& S, 883 int& S,
884 int& O, 884 int& O,
885 bool compressed); 885 bool compressed);
include/qpdf/QPDFWriter.hh
@@ -43,6 +43,11 @@ @@ -43,6 +43,11 @@
43 #include <qpdf/QPDFObjectHandle.hh> 43 #include <qpdf/QPDFObjectHandle.hh>
44 #include <qpdf/QPDFXRefEntry.hh> 44 #include <qpdf/QPDFXRefEntry.hh>
45 45
  46 +namespace qpdf::pl
  47 +{
  48 + struct Link;
  49 +}
  50 +
46 class QPDF; 51 class QPDF;
47 class Pl_Count; 52 class Pl_Count;
48 class Pl_MD5; 53 class Pl_MD5;
@@ -462,23 +467,20 @@ class QPDFWriter @@ -462,23 +467,20 @@ class QPDFWriter
462 friend class QPDFWriter; 467 friend class QPDFWriter;
463 468
464 public: 469 public:
465 - PipelinePopper(QPDFWriter* qw, std::shared_ptr<Buffer>* bp = nullptr) :  
466 - qw(qw),  
467 - bp(bp) 470 + PipelinePopper(QPDFWriter* qw) :
  471 + qw(qw)
468 { 472 {
469 } 473 }
470 ~PipelinePopper(); 474 ~PipelinePopper();
471 475
472 private: 476 private:
473 - QPDFWriter* qw;  
474 - std::shared_ptr<Buffer>* bp;  
475 - std::string stack_id; 477 + QPDFWriter* qw{nullptr};
  478 + unsigned long stack_id{0};
476 }; 479 };
477 480
478 unsigned int bytesNeeded(long long n); 481 unsigned int bytesNeeded(long long n);
479 void writeBinary(unsigned long long val, unsigned int bytes); 482 void writeBinary(unsigned long long val, unsigned int bytes);
480 void writeString(std::string_view str); 483 void writeString(std::string_view str);
481 - void writeBuffer(std::shared_ptr<Buffer>&);  
482 void writeStringQDF(std::string_view str); 484 void writeStringQDF(std::string_view str);
483 void writeStringNoQDF(std::string_view str); 485 void writeStringNoQDF(std::string_view str);
484 void writePad(size_t nspaces); 486 void writePad(size_t nspaces);
@@ -493,7 +495,7 @@ class QPDFWriter @@ -493,7 +495,7 @@ class QPDFWriter
493 QPDFObjectHandle stream, 495 QPDFObjectHandle stream,
494 bool& compress_stream, 496 bool& compress_stream,
495 bool& is_metadata, 497 bool& is_metadata,
496 - std::shared_ptr<Buffer>* stream_data); 498 + std::string* stream_data);
497 void unparseObject( 499 void unparseObject(
498 QPDFObjectHandle object, 500 QPDFObjectHandle object,
499 int level, 501 int level,
@@ -600,12 +602,17 @@ class QPDFWriter @@ -600,12 +602,17 @@ class QPDFWriter
600 // activate the pipeline stack. When the passed in PipelinePopper goes out of scope, the stack 602 // activate the pipeline stack. When the passed in PipelinePopper goes out of scope, the stack
601 // is popped. 603 // is popped.
602 Pipeline* pushPipeline(Pipeline*); 604 Pipeline* pushPipeline(Pipeline*);
603 - void activatePipelineStack(PipelinePopper&); 605 + void activatePipelineStack(PipelinePopper& pp, std::string& str);
  606 + void activatePipelineStack(PipelinePopper& pp, std::unique_ptr<qpdf::pl::Link> link);
  607 + void activatePipelineStack(
  608 + PipelinePopper& pp,
  609 + bool discard = false,
  610 + std::string* str = nullptr,
  611 + std::unique_ptr<qpdf::pl::Link> link = nullptr);
604 void initializePipelineStack(Pipeline*); 612 void initializePipelineStack(Pipeline*);
605 613
606 void adjustAESStreamLength(size_t& length); 614 void adjustAESStreamLength(size_t& length);
607 void pushEncryptionFilter(PipelinePopper&); 615 void pushEncryptionFilter(PipelinePopper&);
608 - void pushDiscardFilter(PipelinePopper&);  
609 void pushMD5Pipeline(PipelinePopper&); 616 void pushMD5Pipeline(PipelinePopper&);
610 void computeDeterministicIDData(); 617 void computeDeterministicIDData();
611 618
libqpdf/QPDFWriter.cc
@@ -6,13 +6,12 @@ @@ -6,13 +6,12 @@
6 6
7 #include <qpdf/MD5.hh> 7 #include <qpdf/MD5.hh>
8 #include <qpdf/Pl_AES_PDF.hh> 8 #include <qpdf/Pl_AES_PDF.hh>
9 -#include <qpdf/Pl_Count.hh>  
10 -#include <qpdf/Pl_Discard.hh>  
11 #include <qpdf/Pl_Flate.hh> 9 #include <qpdf/Pl_Flate.hh>
12 #include <qpdf/Pl_MD5.hh> 10 #include <qpdf/Pl_MD5.hh>
13 #include <qpdf/Pl_PNGFilter.hh> 11 #include <qpdf/Pl_PNGFilter.hh>
14 #include <qpdf/Pl_RC4.hh> 12 #include <qpdf/Pl_RC4.hh>
15 #include <qpdf/Pl_StdioFile.hh> 13 #include <qpdf/Pl_StdioFile.hh>
  14 +#include <qpdf/Pl_String.hh>
16 #include <qpdf/QIntC.hh> 15 #include <qpdf/QIntC.hh>
17 #include <qpdf/QPDFObjectHandle_private.hh> 16 #include <qpdf/QPDFObjectHandle_private.hh>
18 #include <qpdf/QPDFObject_private.hh> 17 #include <qpdf/QPDFObject_private.hh>
@@ -880,12 +879,6 @@ QPDFWriter::writeString(std::string_view str) @@ -880,12 +879,6 @@ QPDFWriter::writeString(std::string_view str)
880 } 879 }
881 880
882 void 881 void
883 -QPDFWriter::writeBuffer(std::shared_ptr<Buffer>& b)  
884 -{  
885 - m->pipeline->write(b->getBuffer(), b->getSize());  
886 -}  
887 -  
888 -void  
889 QPDFWriter::writeStringQDF(std::string_view str) 882 QPDFWriter::writeStringQDF(std::string_view str)
890 { 883 {
891 if (m->qdf_mode) { 884 if (m->qdf_mode) {
@@ -910,58 +903,76 @@ QPDFWriter::writePad(size_t nspaces) @@ -910,58 +903,76 @@ QPDFWriter::writePad(size_t nspaces)
910 Pipeline* 903 Pipeline*
911 QPDFWriter::pushPipeline(Pipeline* p) 904 QPDFWriter::pushPipeline(Pipeline* p)
912 { 905 {
913 - qpdf_assert_debug(dynamic_cast<Pl_Count*>(p) == nullptr);  
914 - m->pipeline_stack.push_back(p); 906 + qpdf_assert_debug(!dynamic_cast<pl::Count*>(p));
  907 + m->pipeline_stack.emplace_back(p);
915 return p; 908 return p;
916 } 909 }
917 910
918 void 911 void
919 QPDFWriter::initializePipelineStack(Pipeline* p) 912 QPDFWriter::initializePipelineStack(Pipeline* p)
920 { 913 {
921 - m->pipeline = new Pl_Count("pipeline stack base", p);  
922 - m->to_delete.push_back(std::shared_ptr<Pipeline>(m->pipeline));  
923 - m->pipeline_stack.push_back(m->pipeline); 914 + m->pipeline = new pl::Count(1, p);
  915 + m->to_delete.emplace_back(std::shared_ptr<Pipeline>(m->pipeline));
  916 + m->pipeline_stack.emplace_back(m->pipeline);
924 } 917 }
925 918
926 void 919 void
927 -QPDFWriter::activatePipelineStack(PipelinePopper& pp) 920 +QPDFWriter::activatePipelineStack(PipelinePopper& pp, std::string& str)
928 { 921 {
929 - std::string stack_id("stack " + std::to_string(m->next_stack_id));  
930 - auto* c = new Pl_Count(stack_id.c_str(), m->pipeline_stack.back());  
931 - ++m->next_stack_id;  
932 - m->pipeline_stack.push_back(c); 922 + activatePipelineStack(pp, false, &str, nullptr);
  923 +}
  924 +
  925 +void
  926 +QPDFWriter::activatePipelineStack(PipelinePopper& pp, std::unique_ptr<pl::Link> link)
  927 +{
  928 + m->count_buffer.clear();
  929 + activatePipelineStack(pp, false, &m->count_buffer, std::move(link));
  930 +}
  931 +
  932 +void
  933 +QPDFWriter::activatePipelineStack(
  934 + PipelinePopper& pp, bool discard, std::string* str, std::unique_ptr<pl::Link> link)
  935 +{
  936 + pl::Count* c;
  937 + if (link) {
  938 + c = new pl::Count(m->next_stack_id, m->count_buffer, std::move(link));
  939 + } else if (discard) {
  940 + c = new pl::Count(m->next_stack_id, nullptr);
  941 + } else if (!str) {
  942 + c = new pl::Count(m->next_stack_id, m->pipeline_stack.back());
  943 + } else {
  944 + c = new pl::Count(m->next_stack_id, *str);
  945 + }
  946 + pp.stack_id = m->next_stack_id;
  947 + m->pipeline_stack.emplace_back(c);
933 m->pipeline = c; 948 m->pipeline = c;
934 - pp.stack_id = stack_id; 949 + ++m->next_stack_id;
935 } 950 }
936 951
937 QPDFWriter::PipelinePopper::~PipelinePopper() 952 QPDFWriter::PipelinePopper::~PipelinePopper()
938 { 953 {
939 - if (stack_id.empty()) { 954 + if (!stack_id) {
940 return; 955 return;
941 } 956 }
942 qpdf_assert_debug(qw->m->pipeline_stack.size() >= 2); 957 qpdf_assert_debug(qw->m->pipeline_stack.size() >= 2);
943 qw->m->pipeline->finish(); 958 qw->m->pipeline->finish();
944 - qpdf_assert_debug(dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back()) == qw->m->pipeline); 959 + qpdf_assert_debug(dynamic_cast<pl::Count*>(qw->m->pipeline_stack.back()) == qw->m->pipeline);
945 // It might be possible for this assertion to fail if writeLinearized exits by exception when 960 // It might be possible for this assertion to fail if writeLinearized exits by exception when
946 // deterministic ID, but I don't think so. As of this writing, this is the only case in which 961 // deterministic ID, but I don't think so. As of this writing, this is the only case in which
947 // two dynamically allocated PipelinePopper objects ever exist at the same time, so the 962 // two dynamically allocated PipelinePopper objects ever exist at the same time, so the
948 // assertion will fail if they get popped out of order from automatic destruction. 963 // assertion will fail if they get popped out of order from automatic destruction.
949 - qpdf_assert_debug(qw->m->pipeline->getIdentifier() == stack_id); 964 + qpdf_assert_debug(qw->m->pipeline->id() == stack_id);
950 delete qw->m->pipeline_stack.back(); 965 delete qw->m->pipeline_stack.back();
951 qw->m->pipeline_stack.pop_back(); 966 qw->m->pipeline_stack.pop_back();
952 - while (dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back()) == nullptr) { 967 + while (!dynamic_cast<pl::Count*>(qw->m->pipeline_stack.back())) {
953 Pipeline* p = qw->m->pipeline_stack.back(); 968 Pipeline* p = qw->m->pipeline_stack.back();
954 if (dynamic_cast<Pl_MD5*>(p) == qw->m->md5_pipeline) { 969 if (dynamic_cast<Pl_MD5*>(p) == qw->m->md5_pipeline) {
955 qw->m->md5_pipeline = nullptr; 970 qw->m->md5_pipeline = nullptr;
956 } 971 }
957 qw->m->pipeline_stack.pop_back(); 972 qw->m->pipeline_stack.pop_back();
958 - auto* buf = dynamic_cast<Pl_Buffer*>(p);  
959 - if (bp && buf) {  
960 - *bp = buf->getBufferSharedPointer();  
961 - }  
962 delete p; 973 delete p;
963 } 974 }
964 - qw->m->pipeline = dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back()); 975 + qw->m->pipeline = dynamic_cast<pl::Count*>(qw->m->pipeline_stack.back());
965 } 976 }
966 977
967 void 978 void
@@ -1001,13 +1012,6 @@ QPDFWriter::pushEncryptionFilter(PipelinePopper&amp; pp) @@ -1001,13 +1012,6 @@ QPDFWriter::pushEncryptionFilter(PipelinePopper&amp; pp)
1001 } 1012 }
1002 1013
1003 void 1014 void
1004 -QPDFWriter::pushDiscardFilter(PipelinePopper& pp)  
1005 -{  
1006 - pushPipeline(new Pl_Discard());  
1007 - activatePipelineStack(pp);  
1008 -}  
1009 -  
1010 -void  
1011 QPDFWriter::pushMD5Pipeline(PipelinePopper& pp) 1015 QPDFWriter::pushMD5Pipeline(PipelinePopper& pp)
1012 { 1016 {
1013 if (!m->id2.empty()) { 1017 if (!m->id2.empty()) {
@@ -1244,7 +1248,7 @@ QPDFWriter::willFilterStream( @@ -1244,7 +1248,7 @@ QPDFWriter::willFilterStream(
1244 QPDFObjectHandle stream, 1248 QPDFObjectHandle stream,
1245 bool& compress_stream, // out only 1249 bool& compress_stream, // out only
1246 bool& is_metadata, // out only 1250 bool& is_metadata, // out only
1247 - std::shared_ptr<Buffer>* stream_data) 1251 + std::string* stream_data)
1248 { 1252 {
1249 compress_stream = false; 1253 compress_stream = false;
1250 is_metadata = false; 1254 is_metadata = false;
@@ -1290,9 +1294,12 @@ QPDFWriter::willFilterStream( @@ -1290,9 +1294,12 @@ QPDFWriter::willFilterStream(
1290 1294
1291 bool filtered = false; 1295 bool filtered = false;
1292 for (bool first_attempt: {true, false}) { 1296 for (bool first_attempt: {true, false}) {
1293 - pushPipeline(new Pl_Buffer("stream data"));  
1294 - PipelinePopper pp_stream_data(this, stream_data);  
1295 - activatePipelineStack(pp_stream_data); 1297 + PipelinePopper pp_stream_data(this);
  1298 + if (stream_data != nullptr) {
  1299 + activatePipelineStack(pp_stream_data, *stream_data);
  1300 + } else {
  1301 + activatePipelineStack(pp_stream_data, true);
  1302 + }
1296 try { 1303 try {
1297 filtered = stream.pipeStreamData( 1304 filtered = stream.pipeStreamData(
1298 m->pipeline, 1305 m->pipeline,
@@ -1320,6 +1327,9 @@ QPDFWriter::willFilterStream( @@ -1320,6 +1327,9 @@ QPDFWriter::willFilterStream(
1320 throw std::runtime_error( 1327 throw std::runtime_error(
1321 "error while getting stream data for " + stream.unparse() + ": " + e.what()); 1328 "error while getting stream data for " + stream.unparse() + ": " + e.what());
1322 } 1329 }
  1330 + if (stream_data) {
  1331 + stream_data->clear();
  1332 + }
1323 } 1333 }
1324 if (!filtered) { 1334 if (!filtered) {
1325 compress_stream = false; 1335 compress_stream = false;
@@ -1545,29 +1555,28 @@ QPDFWriter::unparseObject( @@ -1545,29 +1555,28 @@ QPDFWriter::unparseObject(
1545 flags |= f_stream; 1555 flags |= f_stream;
1546 bool compress_stream = false; 1556 bool compress_stream = false;
1547 bool is_metadata = false; 1557 bool is_metadata = false;
1548 - std::shared_ptr<Buffer> stream_data; 1558 + std::string stream_data;
1549 if (willFilterStream(object, compress_stream, is_metadata, &stream_data)) { 1559 if (willFilterStream(object, compress_stream, is_metadata, &stream_data)) {
1550 flags |= f_filtered; 1560 flags |= f_filtered;
1551 } 1561 }
1552 QPDFObjectHandle stream_dict = object.getDict(); 1562 QPDFObjectHandle stream_dict = object.getDict();
1553 1563
1554 - m->cur_stream_length = stream_data->getSize(); 1564 + m->cur_stream_length = stream_data.size();
1555 if (is_metadata && m->encrypted && (!m->encrypt_metadata)) { 1565 if (is_metadata && m->encrypted && (!m->encrypt_metadata)) {
1556 // Don't encrypt stream data for the metadata stream 1566 // Don't encrypt stream data for the metadata stream
1557 m->cur_data_key.clear(); 1567 m->cur_data_key.clear();
1558 } 1568 }
1559 adjustAESStreamLength(m->cur_stream_length); 1569 adjustAESStreamLength(m->cur_stream_length);
1560 unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream); 1570 unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream);
1561 - unsigned char last_char = '\0'; 1571 + char last_char = stream_data.empty() ? '\0' : stream_data.back();
1562 writeString("\nstream\n"); 1572 writeString("\nstream\n");
1563 { 1573 {
1564 PipelinePopper pp_enc(this); 1574 PipelinePopper pp_enc(this);
1565 pushEncryptionFilter(pp_enc); 1575 pushEncryptionFilter(pp_enc);
1566 - writeBuffer(stream_data);  
1567 - last_char = m->pipeline->getLastChar(); 1576 + writeString(stream_data);
1568 } 1577 }
1569 1578
1570 - if (m->newline_before_endstream || (m->qdf_mode && (last_char != '\n'))) { 1579 + if (m->newline_before_endstream || (m->qdf_mode && last_char != '\n')) {
1571 writeString("\n"); 1580 writeString("\n");
1572 m->added_newline = true; 1581 m->added_newline = true;
1573 } else { 1582 } else {
@@ -1643,15 +1652,14 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1643,15 +1652,14 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1643 1652
1644 // Generate stream itself. We have to do this in two passes so we can calculate offsets in the 1653 // Generate stream itself. We have to do this in two passes so we can calculate offsets in the
1645 // first pass. 1654 // first pass.
1646 - std::shared_ptr<Buffer> stream_buffer; 1655 + std::string stream_buffer_pass1;
  1656 + std::string stream_buffer_pass2;
1647 int first_obj = -1; 1657 int first_obj = -1;
1648 - bool compressed = false; 1658 + const bool compressed = m->compress_streams && !m->qdf_mode;
1649 { 1659 {
1650 // Pass 1 1660 // Pass 1
1651 - PipelinePopper pp_ostream_pass1(this, &stream_buffer);  
1652 -  
1653 - pushPipeline(new Pl_Buffer("object stream"));  
1654 - activatePipelineStack(pp_ostream_pass1); 1661 + PipelinePopper pp_ostream_pass1(this);
  1662 + activatePipelineStack(pp_ostream_pass1, stream_buffer_pass1);
1655 1663
1656 int count = -1; 1664 int count = -1;
1657 for (auto const& obj: m->object_stream_to_objects[old_id]) { 1665 for (auto const& obj: m->object_stream_to_objects[old_id]) {
@@ -1695,7 +1703,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1695,7 +1703,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1695 } 1703 }
1696 } 1704 }
1697 { 1705 {
1698 - PipelinePopper pp_ostream(this, &stream_buffer); 1706 + PipelinePopper pp_ostream(this);
1699 // Adjust offsets to skip over comment before first object 1707 // Adjust offsets to skip over comment before first object
1700 first = offsets.at(0); 1708 first = offsets.at(0);
1701 for (auto& iter: offsets) { 1709 for (auto& iter: offsets) {
@@ -1705,20 +1713,24 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1705,20 +1713,24 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1705 // Take one pass at writing pairs of numbers so we can get their size information 1713 // Take one pass at writing pairs of numbers so we can get their size information
1706 { 1714 {
1707 PipelinePopper pp_discard(this); 1715 PipelinePopper pp_discard(this);
1708 - pushDiscardFilter(pp_discard); 1716 + activatePipelineStack(pp_discard, true);
1709 writeObjectStreamOffsets(offsets, first_obj); 1717 writeObjectStreamOffsets(offsets, first_obj);
1710 first += m->pipeline->getCount(); 1718 first += m->pipeline->getCount();
1711 } 1719 }
1712 1720
1713 // Set up a stream to write the stream data into a buffer. 1721 // Set up a stream to write the stream data into a buffer.
1714 - Pipeline* next = pushPipeline(new Pl_Buffer("object stream"));  
1715 - if (m->compress_streams && !m->qdf_mode) {  
1716 - compressed = true;  
1717 - next = pushPipeline(new Pl_Flate("compress object stream", next, Pl_Flate::a_deflate)); 1722 + if (compressed) {
  1723 + activatePipelineStack(
  1724 + pp_ostream,
  1725 + pl::create<Pl_Flate>(
  1726 + pl::create<pl::String>(stream_buffer_pass2), Pl_Flate::a_deflate));
  1727 + } else {
  1728 + activatePipelineStack(pp_ostream, stream_buffer_pass2);
1718 } 1729 }
1719 - activatePipelineStack(pp_ostream);  
1720 writeObjectStreamOffsets(offsets, first_obj); 1730 writeObjectStreamOffsets(offsets, first_obj);
1721 - writeBuffer(stream_buffer); 1731 + writeString(stream_buffer_pass1);
  1732 + stream_buffer_pass1.clear();
  1733 + stream_buffer_pass1.shrink_to_fit();
1722 } 1734 }
1723 1735
1724 // Write the object 1736 // Write the object
@@ -1728,7 +1740,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1728,7 +1740,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1728 writeStringQDF("\n "); 1740 writeStringQDF("\n ");
1729 writeString(" /Type /ObjStm"); 1741 writeString(" /Type /ObjStm");
1730 writeStringQDF("\n "); 1742 writeStringQDF("\n ");
1731 - size_t length = stream_buffer->getSize(); 1743 + size_t length = stream_buffer_pass2.size();
1732 adjustAESStreamLength(length); 1744 adjustAESStreamLength(length);
1733 writeString(" /Length " + std::to_string(length)); 1745 writeString(" /Length " + std::to_string(length));
1734 writeStringQDF("\n "); 1746 writeStringQDF("\n ");
@@ -1758,7 +1770,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1758,7 +1770,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1758 { 1770 {
1759 PipelinePopper pp_enc(this); 1771 PipelinePopper pp_enc(this);
1760 pushEncryptionFilter(pp_enc); 1772 pushEncryptionFilter(pp_enc);
1761 - writeBuffer(stream_buffer); 1773 + writeString(stream_buffer_pass2);
1762 } 1774 }
1763 if (m->newline_before_endstream) { 1775 if (m->newline_before_endstream) {
1764 writeString("\n"); 1776 writeString("\n");
@@ -2331,7 +2343,7 @@ QPDFWriter::writeHeader() @@ -2331,7 +2343,7 @@ QPDFWriter::writeHeader()
2331 void 2343 void
2332 QPDFWriter::writeHintStream(int hint_id) 2344 QPDFWriter::writeHintStream(int hint_id)
2333 { 2345 {
2334 - std::shared_ptr<Buffer> hint_buffer; 2346 + std::string hint_buffer;
2335 int S = 0; 2347 int S = 0;
2336 int O = 0; 2348 int O = 0;
2337 bool compressed = (m->compress_streams && !m->qdf_mode); 2349 bool compressed = (m->compress_streams && !m->qdf_mode);
@@ -2340,7 +2352,7 @@ QPDFWriter::writeHintStream(int hint_id) @@ -2340,7 +2352,7 @@ QPDFWriter::writeHintStream(int hint_id)
2340 openObject(hint_id); 2352 openObject(hint_id);
2341 setDataKey(hint_id); 2353 setDataKey(hint_id);
2342 2354
2343 - size_t hlen = hint_buffer->getSize(); 2355 + size_t hlen = hint_buffer.size();
2344 2356
2345 writeString("<< "); 2357 writeString("<< ");
2346 if (compressed) { 2358 if (compressed) {
@@ -2360,12 +2372,11 @@ QPDFWriter::writeHintStream(int hint_id) @@ -2360,12 +2372,11 @@ QPDFWriter::writeHintStream(int hint_id)
2360 if (m->encrypted) { 2372 if (m->encrypted) {
2361 QTC::TC("qpdf", "QPDFWriter encrypted hint stream"); 2373 QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
2362 } 2374 }
2363 - unsigned char last_char = '\0'; 2375 + char last_char = hint_buffer.empty() ? '\0' : hint_buffer.back();
2364 { 2376 {
2365 PipelinePopper pp_enc(this); 2377 PipelinePopper pp_enc(this);
2366 pushEncryptionFilter(pp_enc); 2378 pushEncryptionFilter(pp_enc);
2367 - writeBuffer(hint_buffer);  
2368 - last_char = m->pipeline->getLastChar(); 2379 + writeString(hint_buffer);
2369 } 2380 }
2370 2381
2371 if (last_char != '\n') { 2382 if (last_char != '\n') {
@@ -2463,21 +2474,24 @@ QPDFWriter::writeXRefStream( @@ -2463,21 +2474,24 @@ QPDFWriter::writeXRefStream(
2463 // openObject to do it. 2474 // openObject to do it.
2464 m->new_obj[xref_id].xref = QPDFXRefEntry(m->pipeline->getCount()); 2475 m->new_obj[xref_id].xref = QPDFXRefEntry(m->pipeline->getCount());
2465 2476
2466 - Pipeline* p = pushPipeline(new Pl_Buffer("xref stream"));  
2467 - bool compressed = false;  
2468 - if (m->compress_streams && !m->qdf_mode) {  
2469 - compressed = true;  
2470 - if (!skip_compression) {  
2471 - // Write the stream dictionary for compression but don't actually compress. This helps  
2472 - // us with computation of padding for pass 1 of linearization.  
2473 - p = pushPipeline(new Pl_Flate("compress xref", p, Pl_Flate::a_deflate));  
2474 - }  
2475 - p = pushPipeline(new Pl_PNGFilter("pngify xref", p, Pl_PNGFilter::a_encode, esize));  
2476 - }  
2477 - std::shared_ptr<Buffer> xref_data; 2477 + std::string xref_data;
  2478 + const bool compressed = m->compress_streams && !m->qdf_mode;
2478 { 2479 {
2479 - PipelinePopper pp_xref(this, &xref_data);  
2480 - activatePipelineStack(pp_xref); 2480 + PipelinePopper pp_xref(this);
  2481 + if (compressed) {
  2482 + m->count_buffer.clear();
  2483 + auto link = pl::create<pl::String>(xref_data);
  2484 + if (!skip_compression) {
  2485 + // Write the stream dictionary for compression but don't actually compress. This
  2486 + // helps us with computation of padding for pass 1 of linearization.
  2487 + link = pl::create<Pl_Flate>(std::move(link), Pl_Flate::a_deflate);
  2488 + }
  2489 + activatePipelineStack(
  2490 + pp_xref, pl::create<Pl_PNGFilter>(std::move(link), Pl_PNGFilter::a_encode, esize));
  2491 + } else {
  2492 + activatePipelineStack(pp_xref, xref_data);
  2493 + }
  2494 +
2481 for (int i = first; i <= last; ++i) { 2495 for (int i = first; i <= last; ++i) {
2482 QPDFXRefEntry& e = m->new_obj[i].xref; 2496 QPDFXRefEntry& e = m->new_obj[i].xref;
2483 switch (e.getType()) { 2497 switch (e.getType()) {
@@ -2517,7 +2531,7 @@ QPDFWriter::writeXRefStream( @@ -2517,7 +2531,7 @@ QPDFWriter::writeXRefStream(
2517 writeStringQDF("\n "); 2531 writeStringQDF("\n ");
2518 writeString(" /Type /XRef"); 2532 writeString(" /Type /XRef");
2519 writeStringQDF("\n "); 2533 writeStringQDF("\n ");
2520 - writeString(" /Length " + std::to_string(xref_data->getSize())); 2534 + writeString(" /Length " + std::to_string(xref_data.size()));
2521 if (compressed) { 2535 if (compressed) {
2522 writeStringQDF("\n "); 2536 writeStringQDF("\n ");
2523 writeString(" /Filter /FlateDecode"); 2537 writeString(" /Filter /FlateDecode");
@@ -2532,7 +2546,7 @@ QPDFWriter::writeXRefStream( @@ -2532,7 +2546,7 @@ QPDFWriter::writeXRefStream(
2532 } 2546 }
2533 writeTrailer(which, size, true, prev, linearization_pass); 2547 writeTrailer(which, size, true, prev, linearization_pass);
2534 writeString("\nstream\n"); 2548 writeString("\nstream\n");
2535 - writeBuffer(xref_data); 2549 + writeString(xref_data);
2536 writeString("\nendstream"); 2550 writeString("\nendstream");
2537 closeObject(xref_id); 2551 closeObject(xref_id);
2538 return space_before_zero; 2552 return space_before_zero;
@@ -2674,21 +2688,21 @@ QPDFWriter::writeLinearized() @@ -2674,21 +2688,21 @@ QPDFWriter::writeLinearized()
2674 } 2688 }
2675 2689
2676 qpdf_offset_t hint_length = 0; 2690 qpdf_offset_t hint_length = 0;
2677 - std::shared_ptr<Buffer> hint_buffer; 2691 + std::string hint_buffer;
2678 2692
2679 // Write file in two passes. Part numbers refer to PDF spec 1.4. 2693 // Write file in two passes. Part numbers refer to PDF spec 1.4.
2680 2694
2681 FILE* lin_pass1_file = nullptr; 2695 FILE* lin_pass1_file = nullptr;
2682 - auto pp_pass1 = std::make_shared<PipelinePopper>(this);  
2683 - auto pp_md5 = std::make_shared<PipelinePopper>(this);  
2684 - for (int pass = 1; pass <= 2; ++pass) { 2696 + auto pp_pass1 = std::make_unique<PipelinePopper>(this);
  2697 + auto pp_md5 = std::make_unique<PipelinePopper>(this);
  2698 + for (int pass: {1, 2}) {
2685 if (pass == 1) { 2699 if (pass == 1) {
2686 if (!m->lin_pass1_filename.empty()) { 2700 if (!m->lin_pass1_filename.empty()) {
2687 lin_pass1_file = QUtil::safe_fopen(m->lin_pass1_filename.c_str(), "wb"); 2701 lin_pass1_file = QUtil::safe_fopen(m->lin_pass1_filename.c_str(), "wb");
2688 pushPipeline(new Pl_StdioFile("linearization pass1", lin_pass1_file)); 2702 pushPipeline(new Pl_StdioFile("linearization pass1", lin_pass1_file));
2689 activatePipelineStack(*pp_pass1); 2703 activatePipelineStack(*pp_pass1);
2690 } else { 2704 } else {
2691 - pushDiscardFilter(*pp_pass1); 2705 + activatePipelineStack(*pp_pass1, true);
2692 } 2706 }
2693 if (m->deterministic_id) { 2707 if (m->deterministic_id) {
2694 pushMD5Pipeline(*pp_md5); 2708 pushMD5Pipeline(*pp_md5);
@@ -2818,7 +2832,7 @@ QPDFWriter::writeLinearized() @@ -2818,7 +2832,7 @@ QPDFWriter::writeLinearized()
2818 m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount()); 2832 m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount());
2819 } else { 2833 } else {
2820 // Part 5: hint stream 2834 // Part 5: hint stream
2821 - writeBuffer(hint_buffer); 2835 + writeString(hint_buffer);
2822 } 2836 }
2823 } 2837 }
2824 if (cur_object.getObjectID() == part6_end_marker) { 2838 if (cur_object.getObjectID() == part6_end_marker) {
@@ -2892,12 +2906,11 @@ QPDFWriter::writeLinearized() @@ -2892,12 +2906,11 @@ QPDFWriter::writeLinearized()
2892 2906
2893 // Write hint stream to a buffer 2907 // Write hint stream to a buffer
2894 { 2908 {
2895 - pushPipeline(new Pl_Buffer("hint buffer"));  
2896 - PipelinePopper pp_hint(this, &hint_buffer);  
2897 - activatePipelineStack(pp_hint); 2909 + PipelinePopper pp_hint(this);
  2910 + activatePipelineStack(pp_hint, hint_buffer);
2898 writeHintStream(hint_id); 2911 writeHintStream(hint_id);
2899 } 2912 }
2900 - hint_length = QIntC::to_offset(hint_buffer->getSize()); 2913 + hint_length = QIntC::to_offset(hint_buffer.size());
2901 2914
2902 // Restore hint offset 2915 // Restore hint offset
2903 m->new_obj[hint_id].xref = QPDFXRefEntry(hint_offset1); 2916 m->new_obj[hint_id].xref = QPDFXRefEntry(hint_offset1);
@@ -3012,9 +3025,9 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr) @@ -3012,9 +3025,9 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr)
3012 void 3025 void
3013 QPDFWriter::writeStandard() 3026 QPDFWriter::writeStandard()
3014 { 3027 {
3015 - auto pp_md5 = std::make_shared<PipelinePopper>(this); 3028 + auto pp_md5 = PipelinePopper(this);
3016 if (m->deterministic_id) { 3029 if (m->deterministic_id) {
3017 - pushMD5Pipeline(*pp_md5); 3030 + pushMD5Pipeline(pp_md5);
3018 } 3031 }
3019 3032
3020 // Start writing 3033 // Start writing
@@ -3060,7 +3073,5 @@ QPDFWriter::writeStandard() @@ -3060,7 +3073,5 @@ QPDFWriter::writeStandard()
3060 "qpdf", 3073 "qpdf",
3061 "QPDFWriter standard deterministic ID", 3074 "QPDFWriter standard deterministic ID",
3062 m->object_stream_to_objects.empty() ? 0 : 1); 3075 m->object_stream_to_objects.empty() ? 0 : 1);
3063 - pp_md5 = nullptr;  
3064 - qpdf_assert_debug(m->md5_pipeline == nullptr);  
3065 } 3076 }
3066 } 3077 }
libqpdf/QPDF_linearization.cc
@@ -5,9 +5,10 @@ @@ -5,9 +5,10 @@
5 #include <qpdf/BitStream.hh> 5 #include <qpdf/BitStream.hh>
6 #include <qpdf/BitWriter.hh> 6 #include <qpdf/BitWriter.hh>
7 #include <qpdf/InputSource_private.hh> 7 #include <qpdf/InputSource_private.hh>
  8 +#include <qpdf/Pipeline_private.hh>
8 #include <qpdf/Pl_Buffer.hh> 9 #include <qpdf/Pl_Buffer.hh>
9 -#include <qpdf/Pl_Count.hh>  
10 #include <qpdf/Pl_Flate.hh> 10 #include <qpdf/Pl_Flate.hh>
  11 +#include <qpdf/Pl_String.hh>
11 #include <qpdf/QPDFExc.hh> 12 #include <qpdf/QPDFExc.hh>
12 #include <qpdf/QPDFLogger.hh> 13 #include <qpdf/QPDFLogger.hh>
13 #include <qpdf/QPDFWriter_private.hh> 14 #include <qpdf/QPDFWriter_private.hh>
@@ -1742,7 +1743,7 @@ void @@ -1742,7 +1743,7 @@ void
1742 QPDF::generateHintStream( 1743 QPDF::generateHintStream(
1743 QPDFWriter::NewObjTable const& new_obj, 1744 QPDFWriter::NewObjTable const& new_obj,
1744 QPDFWriter::ObjTable const& obj, 1745 QPDFWriter::ObjTable const& obj,
1745 - std::shared_ptr<Buffer>& hint_buffer, 1746 + std::string& hint_buffer,
1746 int& S, 1747 int& S,
1747 int& O, 1748 int& O,
1748 bool compressed) 1749 bool compressed)
@@ -1754,26 +1755,21 @@ QPDF::generateHintStream( @@ -1754,26 +1755,21 @@ QPDF::generateHintStream(
1754 1755
1755 // Write the hint stream itself into a compressed memory buffer. Write through a counter so we 1756 // Write the hint stream itself into a compressed memory buffer. Write through a counter so we
1756 // can get offsets. 1757 // can get offsets.
1757 - Pl_Buffer hint_stream("hint stream");  
1758 - Pipeline* next = &hint_stream;  
1759 - std::shared_ptr<Pipeline> flate;  
1760 - if (compressed) {  
1761 - flate =  
1762 - std::make_shared<Pl_Flate>("compress hint stream", &hint_stream, Pl_Flate::a_deflate);  
1763 - next = flate.get();  
1764 - }  
1765 - Pl_Count c("count", next);  
1766 - BitWriter w(&c); 1758 + std::string b;
  1759 + auto c = compressed
  1760 + ? std::make_unique<pl::Count>(
  1761 + 0, b, pl::create<Pl_Flate>(pl::create<pl::String>(hint_buffer), Pl_Flate::a_deflate))
  1762 + : std::make_unique<pl::Count>(0, hint_buffer);
  1763 +
  1764 + BitWriter w(c.get());
1767 1765
1768 writeHPageOffset(w); 1766 writeHPageOffset(w);
1769 - S = toI(c.getCount()); 1767 + S = toI(c->getCount());
1770 writeHSharedObject(w); 1768 writeHSharedObject(w);
1771 O = 0; 1769 O = 0;
1772 if (m->outline_hints.nobjects > 0) { 1770 if (m->outline_hints.nobjects > 0) {
1773 - O = toI(c.getCount()); 1771 + O = toI(c->getCount());
1774 writeHGeneric(w, m->outline_hints); 1772 writeHGeneric(w, m->outline_hints);
1775 } 1773 }
1776 - c.finish();  
1777 -  
1778 - hint_buffer = hint_stream.getBufferSharedPointer(); 1774 + c->finish();
1779 } 1775 }
libqpdf/qpdf/Pipeline_private.hh 0 โ†’ 100644
  1 +#ifndef PIPELINE_PRIVATE_HH
  2 +#define PIPELINE_PRIVATE_HH
  3 +
  4 +#include <qpdf/Pipeline.hh>
  5 +
  6 +#include <qpdf/Pl_Flate.hh>
  7 +
  8 +namespace qpdf::pl
  9 +{
  10 + struct Link
  11 + {
  12 + Link(std::unique_ptr<Link> next_link, std::unique_ptr<Pipeline> next_pl) :
  13 + next_link(std::move(next_link)),
  14 + next_pl(std::move(next_pl))
  15 + {
  16 + }
  17 +
  18 + std::unique_ptr<Link> next_link{nullptr};
  19 + std::unique_ptr<Pipeline> next_pl{nullptr};
  20 + };
  21 +
  22 + template <typename P, typename... Args>
  23 + std::unique_ptr<Link>
  24 + create(Args&&... args)
  25 + {
  26 + return std::make_unique<Link>(
  27 + nullptr, std::make_unique<P>("", nullptr, std::forward<Args>(args)...));
  28 + }
  29 +
  30 + template <typename P, typename... Args>
  31 + std::unique_ptr<Link>
  32 + create(std::unique_ptr<Link> link, Args&&... args)
  33 + {
  34 + auto* next = link->next_pl.get();
  35 + return std::make_unique<Link>(
  36 + std::move(link), std::make_unique<P>("", next, std::forward<Args>(args)...));
  37 + }
  38 +
  39 + class String final: public Pipeline
  40 + {
  41 + public:
  42 + String(char const* identifier, Pipeline*, std::string& str) :
  43 + Pipeline(identifier, nullptr),
  44 + str(str)
  45 + {
  46 + }
  47 +
  48 + ~String() final = default;
  49 +
  50 + void
  51 + write(unsigned char const* buf, size_t len) final
  52 + {
  53 + if (len) {
  54 + str.append(reinterpret_cast<char const*>(buf), len);
  55 + }
  56 + }
  57 +
  58 + void
  59 + finish() final
  60 + {
  61 + }
  62 +
  63 + private:
  64 + std::string& str;
  65 + };
  66 +
  67 + class Count final: public Pipeline
  68 + {
  69 + public:
  70 + // Count the number of characters written. If 'next' is not set, the content written will be
  71 + // discarded.
  72 + Count(unsigned long id, Pipeline* next = nullptr) :
  73 + Pipeline("", next),
  74 + id_(id),
  75 + pass_immediately_to_next(next)
  76 + {
  77 + }
  78 +
  79 + // Count the number of characters written. If 'next' is not set, the content written will be
  80 + // discarded.
  81 + Count(unsigned long id, std::unique_ptr<Link> link) :
  82 + Pipeline("", link ? link->next_pl.get() : nullptr),
  83 + link(std::move(link)),
  84 + id_(id),
  85 + pass_immediately_to_next(link)
  86 + {
  87 + }
  88 +
  89 + // Write to 'str'. If 'next' is set, 'str' will be written to 'next' when 'finish' is
  90 + // called.
  91 + Count(unsigned long id, std::string& str, std::unique_ptr<Link> link = nullptr) :
  92 + Pipeline("", link ? link->next_pl.get() : nullptr),
  93 + str(&str),
  94 + link(std::move(link)),
  95 + id_(id)
  96 + {
  97 + }
  98 +
  99 + ~Count() final = default;
  100 +
  101 + void
  102 + write(unsigned char const* buf, size_t len) final
  103 + {
  104 + if (len) {
  105 + if (str) {
  106 + str->append(reinterpret_cast<char const*>(buf), len);
  107 + return;
  108 + }
  109 + count += static_cast<qpdf_offset_t>(len);
  110 + if (pass_immediately_to_next) {
  111 + next()->write(buf, len);
  112 + }
  113 + }
  114 + }
  115 +
  116 + void
  117 + finish() final
  118 + {
  119 + if (next()) {
  120 + if (!pass_immediately_to_next) {
  121 + next()->write(reinterpret_cast<unsigned char const*>(str->data()), str->size());
  122 + }
  123 + next()->finish();
  124 + }
  125 + }
  126 +
  127 + qpdf_offset_t
  128 + getCount() const
  129 + {
  130 + return str ? static_cast<qpdf_offset_t>(str->size()) : count;
  131 + }
  132 +
  133 + unsigned long long
  134 + id() const
  135 + {
  136 + return id_;
  137 + }
  138 +
  139 + private:
  140 + qpdf_offset_t count{0};
  141 + std::string* str{nullptr};
  142 + std::unique_ptr<Link> link{nullptr};
  143 + unsigned long id_{0};
  144 + bool pass_immediately_to_next{false};
  145 + };
  146 +} // namespace qpdf::pl
  147 +
  148 +#endif // PIPELINE_PRIVATE_HH
libqpdf/qpdf/QPDFWriter_private.hh
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 #include <qpdf/QPDFWriter.hh> 4 #include <qpdf/QPDFWriter.hh>
5 5
6 #include <qpdf/ObjTable.hh> 6 #include <qpdf/ObjTable.hh>
  7 +#include <qpdf/Pipeline_private.hh>
7 8
8 // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization 9 // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization
9 // only. 10 // only.
@@ -98,7 +99,7 @@ class QPDFWriter::Members @@ -98,7 +99,7 @@ class QPDFWriter::Members
98 int encryption_dict_objid{0}; 99 int encryption_dict_objid{0};
99 std::string cur_data_key; 100 std::string cur_data_key;
100 std::list<std::shared_ptr<Pipeline>> to_delete; 101 std::list<std::shared_ptr<Pipeline>> to_delete;
101 - Pl_Count* pipeline{nullptr}; 102 + qpdf::pl::Count* pipeline{nullptr};
102 std::vector<QPDFObjectHandle> object_queue; 103 std::vector<QPDFObjectHandle> object_queue;
103 size_t object_queue_front{0}; 104 size_t object_queue_front{0};
104 QPDFWriter::ObjTable obj; 105 QPDFWriter::ObjTable obj;
@@ -112,8 +113,9 @@ class QPDFWriter::Members @@ -112,8 +113,9 @@ class QPDFWriter::Members
112 std::map<QPDFObjGen, int> page_object_to_seq; 113 std::map<QPDFObjGen, int> page_object_to_seq;
113 std::map<QPDFObjGen, int> contents_to_page_seq; 114 std::map<QPDFObjGen, int> contents_to_page_seq;
114 std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects; 115 std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects;
115 - std::list<Pipeline*> pipeline_stack;  
116 - unsigned long long next_stack_id{0}; 116 + std::vector<Pipeline*> pipeline_stack;
  117 + unsigned long next_stack_id{2};
  118 + std::string count_buffer;
117 bool deterministic_id{false}; 119 bool deterministic_id{false};
118 Pl_MD5* md5_pipeline{nullptr}; 120 Pl_MD5* md5_pipeline{nullptr};
119 std::string deterministic_id_data; 121 std::string deterministic_id_data;
libqpdf/qpdf/QPDF_private.hh
@@ -39,7 +39,7 @@ class QPDF::Writer @@ -39,7 +39,7 @@ class QPDF::Writer
39 QPDF& qpdf, 39 QPDF& qpdf,
40 QPDFWriter::NewObjTable const& new_obj, 40 QPDFWriter::NewObjTable const& new_obj,
41 QPDFWriter::ObjTable const& obj, 41 QPDFWriter::ObjTable const& obj,
42 - std::shared_ptr<Buffer>& hint_stream, 42 + std::string& hint_stream,
43 int& S, 43 int& S,
44 int& O, 44 int& O,
45 bool compressed) 45 bool compressed)
manual/release-notes.rst
@@ -21,7 +21,7 @@ more detail. @@ -21,7 +21,7 @@ more detail.
21 integer object. Previously the method returned false if the first 21 integer object. Previously the method returned false if the first
22 dictionary object was not a linearization parameter dictionary. 22 dictionary object was not a linearization parameter dictionary.
23 23
24 - = Fix parsing of object streams containing objects not seperated by 24 + - Fix parsing of object streams containing objects not seperated by
25 white-space. Pre-2020 editions of the PDF specification incorrectly 25 white-space. Pre-2020 editions of the PDF specification incorrectly
26 stated that white-space was required between objects. qpdf relied on this 26 stated that white-space was required between objects. qpdf relied on this
27 when parsing object streams. 27 when parsing object streams.
@@ -40,8 +40,8 @@ more detail. @@ -40,8 +40,8 @@ more detail.
40 messages and object descriptions has been refactored with some 40 messages and object descriptions has been refactored with some
41 improvement both in runtime and memory usage. 41 improvement both in runtime and memory usage.
42 42
43 - - There has been some refactoring of how object streams are written with  
44 - some performance improvement. 43 + - There has been some refactoring of QPDFWriter including how object
  44 + streams are written with some performance improvement.
45 45
46 .. cSpell:ignore substract 46 .. cSpell:ignore substract
47 47