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 | 879 | void generateHintStream( |
| 880 | 880 | QPDFWriter::NewObjTable const& new_obj, |
| 881 | 881 | QPDFWriter::ObjTable const& obj, |
| 882 | - std::shared_ptr<Buffer>& hint_stream, | |
| 882 | + std::string& hint_stream, | |
| 883 | 883 | int& S, |
| 884 | 884 | int& O, |
| 885 | 885 | bool compressed); | ... | ... |
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -43,6 +43,11 @@ |
| 43 | 43 | #include <qpdf/QPDFObjectHandle.hh> |
| 44 | 44 | #include <qpdf/QPDFXRefEntry.hh> |
| 45 | 45 | |
| 46 | +namespace qpdf::pl | |
| 47 | +{ | |
| 48 | + struct Link; | |
| 49 | +} | |
| 50 | + | |
| 46 | 51 | class QPDF; |
| 47 | 52 | class Pl_Count; |
| 48 | 53 | class Pl_MD5; |
| ... | ... | @@ -462,23 +467,20 @@ class QPDFWriter |
| 462 | 467 | friend class QPDFWriter; |
| 463 | 468 | |
| 464 | 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 | 474 | ~PipelinePopper(); |
| 471 | 475 | |
| 472 | 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 | 481 | unsigned int bytesNeeded(long long n); |
| 479 | 482 | void writeBinary(unsigned long long val, unsigned int bytes); |
| 480 | 483 | void writeString(std::string_view str); |
| 481 | - void writeBuffer(std::shared_ptr<Buffer>&); | |
| 482 | 484 | void writeStringQDF(std::string_view str); |
| 483 | 485 | void writeStringNoQDF(std::string_view str); |
| 484 | 486 | void writePad(size_t nspaces); |
| ... | ... | @@ -493,7 +495,7 @@ class QPDFWriter |
| 493 | 495 | QPDFObjectHandle stream, |
| 494 | 496 | bool& compress_stream, |
| 495 | 497 | bool& is_metadata, |
| 496 | - std::shared_ptr<Buffer>* stream_data); | |
| 498 | + std::string* stream_data); | |
| 497 | 499 | void unparseObject( |
| 498 | 500 | QPDFObjectHandle object, |
| 499 | 501 | int level, |
| ... | ... | @@ -600,12 +602,17 @@ class QPDFWriter |
| 600 | 602 | // activate the pipeline stack. When the passed in PipelinePopper goes out of scope, the stack |
| 601 | 603 | // is popped. |
| 602 | 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 | 612 | void initializePipelineStack(Pipeline*); |
| 605 | 613 | |
| 606 | 614 | void adjustAESStreamLength(size_t& length); |
| 607 | 615 | void pushEncryptionFilter(PipelinePopper&); |
| 608 | - void pushDiscardFilter(PipelinePopper&); | |
| 609 | 616 | void pushMD5Pipeline(PipelinePopper&); |
| 610 | 617 | void computeDeterministicIDData(); |
| 611 | 618 | ... | ... |
libqpdf/QPDFWriter.cc
| ... | ... | @@ -6,13 +6,12 @@ |
| 6 | 6 | |
| 7 | 7 | #include <qpdf/MD5.hh> |
| 8 | 8 | #include <qpdf/Pl_AES_PDF.hh> |
| 9 | -#include <qpdf/Pl_Count.hh> | |
| 10 | -#include <qpdf/Pl_Discard.hh> | |
| 11 | 9 | #include <qpdf/Pl_Flate.hh> |
| 12 | 10 | #include <qpdf/Pl_MD5.hh> |
| 13 | 11 | #include <qpdf/Pl_PNGFilter.hh> |
| 14 | 12 | #include <qpdf/Pl_RC4.hh> |
| 15 | 13 | #include <qpdf/Pl_StdioFile.hh> |
| 14 | +#include <qpdf/Pl_String.hh> | |
| 16 | 15 | #include <qpdf/QIntC.hh> |
| 17 | 16 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 18 | 17 | #include <qpdf/QPDFObject_private.hh> |
| ... | ... | @@ -880,12 +879,6 @@ QPDFWriter::writeString(std::string_view str) |
| 880 | 879 | } |
| 881 | 880 | |
| 882 | 881 | void |
| 883 | -QPDFWriter::writeBuffer(std::shared_ptr<Buffer>& b) | |
| 884 | -{ | |
| 885 | - m->pipeline->write(b->getBuffer(), b->getSize()); | |
| 886 | -} | |
| 887 | - | |
| 888 | -void | |
| 889 | 882 | QPDFWriter::writeStringQDF(std::string_view str) |
| 890 | 883 | { |
| 891 | 884 | if (m->qdf_mode) { |
| ... | ... | @@ -910,58 +903,76 @@ QPDFWriter::writePad(size_t nspaces) |
| 910 | 903 | Pipeline* |
| 911 | 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 | 908 | return p; |
| 916 | 909 | } |
| 917 | 910 | |
| 918 | 911 | void |
| 919 | 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 | 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 | 948 | m->pipeline = c; |
| 934 | - pp.stack_id = stack_id; | |
| 949 | + ++m->next_stack_id; | |
| 935 | 950 | } |
| 936 | 951 | |
| 937 | 952 | QPDFWriter::PipelinePopper::~PipelinePopper() |
| 938 | 953 | { |
| 939 | - if (stack_id.empty()) { | |
| 954 | + if (!stack_id) { | |
| 940 | 955 | return; |
| 941 | 956 | } |
| 942 | 957 | qpdf_assert_debug(qw->m->pipeline_stack.size() >= 2); |
| 943 | 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 | 960 | // It might be possible for this assertion to fail if writeLinearized exits by exception when |
| 946 | 961 | // deterministic ID, but I don't think so. As of this writing, this is the only case in which |
| 947 | 962 | // two dynamically allocated PipelinePopper objects ever exist at the same time, so the |
| 948 | 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 | 965 | delete qw->m->pipeline_stack.back(); |
| 951 | 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 | 968 | Pipeline* p = qw->m->pipeline_stack.back(); |
| 954 | 969 | if (dynamic_cast<Pl_MD5*>(p) == qw->m->md5_pipeline) { |
| 955 | 970 | qw->m->md5_pipeline = nullptr; |
| 956 | 971 | } |
| 957 | 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 | 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 | 978 | void |
| ... | ... | @@ -1001,13 +1012,6 @@ QPDFWriter::pushEncryptionFilter(PipelinePopper& pp) |
| 1001 | 1012 | } |
| 1002 | 1013 | |
| 1003 | 1014 | void |
| 1004 | -QPDFWriter::pushDiscardFilter(PipelinePopper& pp) | |
| 1005 | -{ | |
| 1006 | - pushPipeline(new Pl_Discard()); | |
| 1007 | - activatePipelineStack(pp); | |
| 1008 | -} | |
| 1009 | - | |
| 1010 | -void | |
| 1011 | 1015 | QPDFWriter::pushMD5Pipeline(PipelinePopper& pp) |
| 1012 | 1016 | { |
| 1013 | 1017 | if (!m->id2.empty()) { |
| ... | ... | @@ -1244,7 +1248,7 @@ QPDFWriter::willFilterStream( |
| 1244 | 1248 | QPDFObjectHandle stream, |
| 1245 | 1249 | bool& compress_stream, // out only |
| 1246 | 1250 | bool& is_metadata, // out only |
| 1247 | - std::shared_ptr<Buffer>* stream_data) | |
| 1251 | + std::string* stream_data) | |
| 1248 | 1252 | { |
| 1249 | 1253 | compress_stream = false; |
| 1250 | 1254 | is_metadata = false; |
| ... | ... | @@ -1290,9 +1294,12 @@ QPDFWriter::willFilterStream( |
| 1290 | 1294 | |
| 1291 | 1295 | bool filtered = false; |
| 1292 | 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 | 1303 | try { |
| 1297 | 1304 | filtered = stream.pipeStreamData( |
| 1298 | 1305 | m->pipeline, |
| ... | ... | @@ -1320,6 +1327,9 @@ QPDFWriter::willFilterStream( |
| 1320 | 1327 | throw std::runtime_error( |
| 1321 | 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 | 1334 | if (!filtered) { |
| 1325 | 1335 | compress_stream = false; |
| ... | ... | @@ -1545,29 +1555,28 @@ QPDFWriter::unparseObject( |
| 1545 | 1555 | flags |= f_stream; |
| 1546 | 1556 | bool compress_stream = false; |
| 1547 | 1557 | bool is_metadata = false; |
| 1548 | - std::shared_ptr<Buffer> stream_data; | |
| 1558 | + std::string stream_data; | |
| 1549 | 1559 | if (willFilterStream(object, compress_stream, is_metadata, &stream_data)) { |
| 1550 | 1560 | flags |= f_filtered; |
| 1551 | 1561 | } |
| 1552 | 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 | 1565 | if (is_metadata && m->encrypted && (!m->encrypt_metadata)) { |
| 1556 | 1566 | // Don't encrypt stream data for the metadata stream |
| 1557 | 1567 | m->cur_data_key.clear(); |
| 1558 | 1568 | } |
| 1559 | 1569 | adjustAESStreamLength(m->cur_stream_length); |
| 1560 | 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 | 1572 | writeString("\nstream\n"); |
| 1563 | 1573 | { |
| 1564 | 1574 | PipelinePopper pp_enc(this); |
| 1565 | 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 | 1580 | writeString("\n"); |
| 1572 | 1581 | m->added_newline = true; |
| 1573 | 1582 | } else { |
| ... | ... | @@ -1643,15 +1652,14 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1643 | 1652 | |
| 1644 | 1653 | // Generate stream itself. We have to do this in two passes so we can calculate offsets in the |
| 1645 | 1654 | // first pass. |
| 1646 | - std::shared_ptr<Buffer> stream_buffer; | |
| 1655 | + std::string stream_buffer_pass1; | |
| 1656 | + std::string stream_buffer_pass2; | |
| 1647 | 1657 | int first_obj = -1; |
| 1648 | - bool compressed = false; | |
| 1658 | + const bool compressed = m->compress_streams && !m->qdf_mode; | |
| 1649 | 1659 | { |
| 1650 | 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 | 1664 | int count = -1; |
| 1657 | 1665 | for (auto const& obj: m->object_stream_to_objects[old_id]) { |
| ... | ... | @@ -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 | 1707 | // Adjust offsets to skip over comment before first object |
| 1700 | 1708 | first = offsets.at(0); |
| 1701 | 1709 | for (auto& iter: offsets) { |
| ... | ... | @@ -1705,20 +1713,24 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1705 | 1713 | // Take one pass at writing pairs of numbers so we can get their size information |
| 1706 | 1714 | { |
| 1707 | 1715 | PipelinePopper pp_discard(this); |
| 1708 | - pushDiscardFilter(pp_discard); | |
| 1716 | + activatePipelineStack(pp_discard, true); | |
| 1709 | 1717 | writeObjectStreamOffsets(offsets, first_obj); |
| 1710 | 1718 | first += m->pipeline->getCount(); |
| 1711 | 1719 | } |
| 1712 | 1720 | |
| 1713 | 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 | 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 | 1736 | // Write the object |
| ... | ... | @@ -1728,7 +1740,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1728 | 1740 | writeStringQDF("\n "); |
| 1729 | 1741 | writeString(" /Type /ObjStm"); |
| 1730 | 1742 | writeStringQDF("\n "); |
| 1731 | - size_t length = stream_buffer->getSize(); | |
| 1743 | + size_t length = stream_buffer_pass2.size(); | |
| 1732 | 1744 | adjustAESStreamLength(length); |
| 1733 | 1745 | writeString(" /Length " + std::to_string(length)); |
| 1734 | 1746 | writeStringQDF("\n "); |
| ... | ... | @@ -1758,7 +1770,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1758 | 1770 | { |
| 1759 | 1771 | PipelinePopper pp_enc(this); |
| 1760 | 1772 | pushEncryptionFilter(pp_enc); |
| 1761 | - writeBuffer(stream_buffer); | |
| 1773 | + writeString(stream_buffer_pass2); | |
| 1762 | 1774 | } |
| 1763 | 1775 | if (m->newline_before_endstream) { |
| 1764 | 1776 | writeString("\n"); |
| ... | ... | @@ -2331,7 +2343,7 @@ QPDFWriter::writeHeader() |
| 2331 | 2343 | void |
| 2332 | 2344 | QPDFWriter::writeHintStream(int hint_id) |
| 2333 | 2345 | { |
| 2334 | - std::shared_ptr<Buffer> hint_buffer; | |
| 2346 | + std::string hint_buffer; | |
| 2335 | 2347 | int S = 0; |
| 2336 | 2348 | int O = 0; |
| 2337 | 2349 | bool compressed = (m->compress_streams && !m->qdf_mode); |
| ... | ... | @@ -2340,7 +2352,7 @@ QPDFWriter::writeHintStream(int hint_id) |
| 2340 | 2352 | openObject(hint_id); |
| 2341 | 2353 | setDataKey(hint_id); |
| 2342 | 2354 | |
| 2343 | - size_t hlen = hint_buffer->getSize(); | |
| 2355 | + size_t hlen = hint_buffer.size(); | |
| 2344 | 2356 | |
| 2345 | 2357 | writeString("<< "); |
| 2346 | 2358 | if (compressed) { |
| ... | ... | @@ -2360,12 +2372,11 @@ QPDFWriter::writeHintStream(int hint_id) |
| 2360 | 2372 | if (m->encrypted) { |
| 2361 | 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 | 2377 | PipelinePopper pp_enc(this); |
| 2366 | 2378 | pushEncryptionFilter(pp_enc); |
| 2367 | - writeBuffer(hint_buffer); | |
| 2368 | - last_char = m->pipeline->getLastChar(); | |
| 2379 | + writeString(hint_buffer); | |
| 2369 | 2380 | } |
| 2370 | 2381 | |
| 2371 | 2382 | if (last_char != '\n') { |
| ... | ... | @@ -2463,21 +2474,24 @@ QPDFWriter::writeXRefStream( |
| 2463 | 2474 | // openObject to do it. |
| 2464 | 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 | 2495 | for (int i = first; i <= last; ++i) { |
| 2482 | 2496 | QPDFXRefEntry& e = m->new_obj[i].xref; |
| 2483 | 2497 | switch (e.getType()) { |
| ... | ... | @@ -2517,7 +2531,7 @@ QPDFWriter::writeXRefStream( |
| 2517 | 2531 | writeStringQDF("\n "); |
| 2518 | 2532 | writeString(" /Type /XRef"); |
| 2519 | 2533 | writeStringQDF("\n "); |
| 2520 | - writeString(" /Length " + std::to_string(xref_data->getSize())); | |
| 2534 | + writeString(" /Length " + std::to_string(xref_data.size())); | |
| 2521 | 2535 | if (compressed) { |
| 2522 | 2536 | writeStringQDF("\n "); |
| 2523 | 2537 | writeString(" /Filter /FlateDecode"); |
| ... | ... | @@ -2532,7 +2546,7 @@ QPDFWriter::writeXRefStream( |
| 2532 | 2546 | } |
| 2533 | 2547 | writeTrailer(which, size, true, prev, linearization_pass); |
| 2534 | 2548 | writeString("\nstream\n"); |
| 2535 | - writeBuffer(xref_data); | |
| 2549 | + writeString(xref_data); | |
| 2536 | 2550 | writeString("\nendstream"); |
| 2537 | 2551 | closeObject(xref_id); |
| 2538 | 2552 | return space_before_zero; |
| ... | ... | @@ -2674,21 +2688,21 @@ QPDFWriter::writeLinearized() |
| 2674 | 2688 | } |
| 2675 | 2689 | |
| 2676 | 2690 | qpdf_offset_t hint_length = 0; |
| 2677 | - std::shared_ptr<Buffer> hint_buffer; | |
| 2691 | + std::string hint_buffer; | |
| 2678 | 2692 | |
| 2679 | 2693 | // Write file in two passes. Part numbers refer to PDF spec 1.4. |
| 2680 | 2694 | |
| 2681 | 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 | 2699 | if (pass == 1) { |
| 2686 | 2700 | if (!m->lin_pass1_filename.empty()) { |
| 2687 | 2701 | lin_pass1_file = QUtil::safe_fopen(m->lin_pass1_filename.c_str(), "wb"); |
| 2688 | 2702 | pushPipeline(new Pl_StdioFile("linearization pass1", lin_pass1_file)); |
| 2689 | 2703 | activatePipelineStack(*pp_pass1); |
| 2690 | 2704 | } else { |
| 2691 | - pushDiscardFilter(*pp_pass1); | |
| 2705 | + activatePipelineStack(*pp_pass1, true); | |
| 2692 | 2706 | } |
| 2693 | 2707 | if (m->deterministic_id) { |
| 2694 | 2708 | pushMD5Pipeline(*pp_md5); |
| ... | ... | @@ -2818,7 +2832,7 @@ QPDFWriter::writeLinearized() |
| 2818 | 2832 | m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount()); |
| 2819 | 2833 | } else { |
| 2820 | 2834 | // Part 5: hint stream |
| 2821 | - writeBuffer(hint_buffer); | |
| 2835 | + writeString(hint_buffer); | |
| 2822 | 2836 | } |
| 2823 | 2837 | } |
| 2824 | 2838 | if (cur_object.getObjectID() == part6_end_marker) { |
| ... | ... | @@ -2892,12 +2906,11 @@ QPDFWriter::writeLinearized() |
| 2892 | 2906 | |
| 2893 | 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 | 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 | 2915 | // Restore hint offset |
| 2903 | 2916 | m->new_obj[hint_id].xref = QPDFXRefEntry(hint_offset1); |
| ... | ... | @@ -3012,9 +3025,9 @@ QPDFWriter::registerProgressReporter(std::shared_ptr<ProgressReporter> pr) |
| 3012 | 3025 | void |
| 3013 | 3026 | QPDFWriter::writeStandard() |
| 3014 | 3027 | { |
| 3015 | - auto pp_md5 = std::make_shared<PipelinePopper>(this); | |
| 3028 | + auto pp_md5 = PipelinePopper(this); | |
| 3016 | 3029 | if (m->deterministic_id) { |
| 3017 | - pushMD5Pipeline(*pp_md5); | |
| 3030 | + pushMD5Pipeline(pp_md5); | |
| 3018 | 3031 | } |
| 3019 | 3032 | |
| 3020 | 3033 | // Start writing |
| ... | ... | @@ -3060,7 +3073,5 @@ QPDFWriter::writeStandard() |
| 3060 | 3073 | "qpdf", |
| 3061 | 3074 | "QPDFWriter standard deterministic ID", |
| 3062 | 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 | 5 | #include <qpdf/BitStream.hh> |
| 6 | 6 | #include <qpdf/BitWriter.hh> |
| 7 | 7 | #include <qpdf/InputSource_private.hh> |
| 8 | +#include <qpdf/Pipeline_private.hh> | |
| 8 | 9 | #include <qpdf/Pl_Buffer.hh> |
| 9 | -#include <qpdf/Pl_Count.hh> | |
| 10 | 10 | #include <qpdf/Pl_Flate.hh> |
| 11 | +#include <qpdf/Pl_String.hh> | |
| 11 | 12 | #include <qpdf/QPDFExc.hh> |
| 12 | 13 | #include <qpdf/QPDFLogger.hh> |
| 13 | 14 | #include <qpdf/QPDFWriter_private.hh> |
| ... | ... | @@ -1742,7 +1743,7 @@ void |
| 1742 | 1743 | QPDF::generateHintStream( |
| 1743 | 1744 | QPDFWriter::NewObjTable const& new_obj, |
| 1744 | 1745 | QPDFWriter::ObjTable const& obj, |
| 1745 | - std::shared_ptr<Buffer>& hint_buffer, | |
| 1746 | + std::string& hint_buffer, | |
| 1746 | 1747 | int& S, |
| 1747 | 1748 | int& O, |
| 1748 | 1749 | bool compressed) |
| ... | ... | @@ -1754,26 +1755,21 @@ QPDF::generateHintStream( |
| 1754 | 1755 | |
| 1755 | 1756 | // Write the hint stream itself into a compressed memory buffer. Write through a counter so we |
| 1756 | 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 | 1766 | writeHPageOffset(w); |
| 1769 | - S = toI(c.getCount()); | |
| 1767 | + S = toI(c->getCount()); | |
| 1770 | 1768 | writeHSharedObject(w); |
| 1771 | 1769 | O = 0; |
| 1772 | 1770 | if (m->outline_hints.nobjects > 0) { |
| 1773 | - O = toI(c.getCount()); | |
| 1771 | + O = toI(c->getCount()); | |
| 1774 | 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 | 4 | #include <qpdf/QPDFWriter.hh> |
| 5 | 5 | |
| 6 | 6 | #include <qpdf/ObjTable.hh> |
| 7 | +#include <qpdf/Pipeline_private.hh> | |
| 7 | 8 | |
| 8 | 9 | // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization |
| 9 | 10 | // only. |
| ... | ... | @@ -98,7 +99,7 @@ class QPDFWriter::Members |
| 98 | 99 | int encryption_dict_objid{0}; |
| 99 | 100 | std::string cur_data_key; |
| 100 | 101 | std::list<std::shared_ptr<Pipeline>> to_delete; |
| 101 | - Pl_Count* pipeline{nullptr}; | |
| 102 | + qpdf::pl::Count* pipeline{nullptr}; | |
| 102 | 103 | std::vector<QPDFObjectHandle> object_queue; |
| 103 | 104 | size_t object_queue_front{0}; |
| 104 | 105 | QPDFWriter::ObjTable obj; |
| ... | ... | @@ -112,8 +113,9 @@ class QPDFWriter::Members |
| 112 | 113 | std::map<QPDFObjGen, int> page_object_to_seq; |
| 113 | 114 | std::map<QPDFObjGen, int> contents_to_page_seq; |
| 114 | 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 | 119 | bool deterministic_id{false}; |
| 118 | 120 | Pl_MD5* md5_pipeline{nullptr}; |
| 119 | 121 | std::string deterministic_id_data; | ... | ... |
libqpdf/qpdf/QPDF_private.hh
manual/release-notes.rst
| ... | ... | @@ -21,7 +21,7 @@ more detail. |
| 21 | 21 | integer object. Previously the method returned false if the first |
| 22 | 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 | 25 | white-space. Pre-2020 editions of the PDF specification incorrectly |
| 26 | 26 | stated that white-space was required between objects. qpdf relied on this |
| 27 | 27 | when parsing object streams. |
| ... | ... | @@ -40,8 +40,8 @@ more detail. |
| 40 | 40 | messages and object descriptions has been refactored with some |
| 41 | 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 | 46 | .. cSpell:ignore substract |
| 47 | 47 | ... | ... |