Commit 314bd3ebe03280a2cc22eba7527e09b2a179373d
Committed by
GitHub
Merge pull request #1406 from m-holger/count
Tune QPDFWriter writes to Pl_Count
Showing
8 changed files
with
295 additions
and
131 deletions
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& pp) | @@ -1001,13 +1012,6 @@ QPDFWriter::pushEncryptionFilter(PipelinePopper& 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<ProgressReporter> pr) | @@ -3012,9 +3025,9 @@ QPDFWriter::registerProgressReporter(std::shared_ptr<ProgressReporter> 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 |