Commit 88a62f78fd2e7f30409ded65eb9565c28c18388f

Authored by m-holger
Committed by GitHub
2 parents 885674ab c97da2df

Merge pull request #1550 from m-holger/writer

Refactor QPDFWriter
libqpdf/QPDFWriter.cc
@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 #include <concepts> 22 #include <concepts>
23 #include <cstdlib> 23 #include <cstdlib>
24 #include <stdexcept> 24 #include <stdexcept>
  25 +#include <tuple>
25 26
26 using namespace std::literals; 27 using namespace std::literals;
27 using namespace qpdf; 28 using namespace qpdf;
@@ -258,7 +259,76 @@ Pl_stack::Popper::pop() @@ -258,7 +259,76 @@ Pl_stack::Popper::pop()
258 stack = nullptr; 259 stack = nullptr;
259 } 260 }
260 261
261 -class QPDFWriter::Members 262 +// Writer class is restricted to QPDFWriter so that only it can call certain methods.
  263 +class QPDF::Writer
  264 +{
  265 + friend class QPDFWriter;
  266 + Writer(QPDF& pdf) :
  267 + pdf(pdf)
  268 + {
  269 + }
  270 +
  271 + protected:
  272 + void
  273 + optimize(
  274 + QPDFWriter::ObjTable const& obj,
  275 + std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
  276 + {
  277 + pdf.optimize(obj, skip_stream_parameters);
  278 + }
  279 +
  280 + void
  281 + getLinearizedParts(
  282 + QPDFWriter::ObjTable const& obj,
  283 + std::vector<QPDFObjectHandle>& part4,
  284 + std::vector<QPDFObjectHandle>& part6,
  285 + std::vector<QPDFObjectHandle>& part7,
  286 + std::vector<QPDFObjectHandle>& part8,
  287 + std::vector<QPDFObjectHandle>& part9)
  288 + {
  289 + pdf.getLinearizedParts(obj, part4, part6, part7, part8, part9);
  290 + }
  291 +
  292 + void
  293 + generateHintStream(
  294 + QPDFWriter::NewObjTable const& new_obj,
  295 + QPDFWriter::ObjTable const& obj,
  296 + std::string& hint_stream,
  297 + int& S,
  298 + int& O,
  299 + bool compressed)
  300 + {
  301 + pdf.generateHintStream(new_obj, obj, hint_stream, S, O, compressed);
  302 + }
  303 +
  304 + std::vector<QPDFObjGen>
  305 + getCompressibleObjGens()
  306 + {
  307 + return pdf.getCompressibleObjVector();
  308 + }
  309 +
  310 + std::vector<bool>
  311 + getCompressibleObjSet()
  312 + {
  313 + return pdf.getCompressibleObjSet();
  314 + }
  315 +
  316 + std::map<QPDFObjGen, QPDFXRefEntry> const&
  317 + getXRefTable()
  318 + {
  319 + return pdf.getXRefTableInternal();
  320 + }
  321 +
  322 + size_t
  323 + tableSize()
  324 + {
  325 + return pdf.tableSize();
  326 + }
  327 +
  328 + QPDF& pdf;
  329 +};
  330 +
  331 +class QPDFWriter::Members: QPDF::Writer
262 { 332 {
263 friend class QPDFWriter; 333 friend class QPDFWriter;
264 334
@@ -273,8 +343,8 @@ class QPDFWriter::Members @@ -273,8 +343,8 @@ class QPDFWriter::Members
273 enum trailer_e { t_normal, t_lin_first, t_lin_second }; 343 enum trailer_e { t_normal, t_lin_first, t_lin_second };
274 344
275 Members(QPDFWriter& w, QPDF& pdf) : 345 Members(QPDFWriter& w, QPDF& pdf) :
  346 + QPDF::Writer(pdf),
276 w(w), 347 w(w),
277 - pdf(pdf),  
278 root_og( 348 root_og(
279 pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0)), 349 pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0)),
280 pipeline_stack(pipeline) 350 pipeline_stack(pipeline)
@@ -323,13 +393,14 @@ class QPDFWriter::Members @@ -323,13 +393,14 @@ class QPDFWriter::Members
323 void enqueueObjectsPCLm(); 393 void enqueueObjectsPCLm();
324 void enqueuePart(std::vector<QPDFObjectHandle>& part); 394 void enqueuePart(std::vector<QPDFObjectHandle>& part);
325 void assignCompressedObjectNumbers(QPDFObjGen og); 395 void assignCompressedObjectNumbers(QPDFObjGen og);
326 - QPDFObjectHandle getTrimmedTrailer(); 396 + Dictionary trimmed_trailer();
327 397
328 - bool willFilterStream(  
329 - QPDFObjectHandle stream,  
330 - bool& compress_stream,  
331 - bool& is_metadata,  
332 - std::string* stream_data); 398 + // Returns tuple<filter, compress_stream, is_root_metadata>
  399 + std::tuple<const bool, const bool, const bool>
  400 + will_filter_stream(QPDFObjectHandle stream, std::string* stream_data);
  401 +
  402 + // Test whether stream would be filtered if it were written.
  403 + bool will_filter_stream(QPDFObjectHandle stream);
333 unsigned int bytesNeeded(long long n); 404 unsigned int bytesNeeded(long long n);
334 void writeBinary(unsigned long long val, unsigned int bytes); 405 void writeBinary(unsigned long long val, unsigned int bytes);
335 Members& write(std::string_view str); 406 Members& write(std::string_view str);
@@ -409,7 +480,6 @@ class QPDFWriter::Members @@ -409,7 +480,6 @@ class QPDFWriter::Members
409 480
410 private: 481 private:
411 QPDFWriter& w; 482 QPDFWriter& w;
412 - QPDF& pdf;  
413 QPDFObjGen root_og{-1, 0}; 483 QPDFObjGen root_og{-1, 0};
414 char const* filename{"unspecified"}; 484 char const* filename{"unspecified"};
415 FILE* file{nullptr}; 485 FILE* file{nullptr};
@@ -1375,7 +1445,7 @@ void @@ -1375,7 +1445,7 @@ void
1375 QPDFWriter::Members::writeTrailer( 1445 QPDFWriter::Members::writeTrailer(
1376 trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass) 1446 trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass)
1377 { 1447 {
1378 - QPDFObjectHandle trailer = getTrimmedTrailer(); 1448 + auto trailer = trimmed_trailer();
1379 if (xref_stream) { 1449 if (xref_stream) {
1380 cur_data_key.clear(); 1450 cur_data_key.clear();
1381 } else { 1451 } else {
@@ -1385,7 +1455,7 @@ QPDFWriter::Members::writeTrailer( @@ -1385,7 +1455,7 @@ QPDFWriter::Members::writeTrailer(
1385 if (which == t_lin_second) { 1455 if (which == t_lin_second) {
1386 write(" /Size ").write(size); 1456 write(" /Size ").write(size);
1387 } else { 1457 } else {
1388 - for (auto const& [key, value]: trailer.as_dictionary()) { 1458 + for (auto const& [key, value]: trailer) {
1389 if (value.null()) { 1459 if (value.null()) {
1390 continue; 1460 continue;
1391 } 1461 }
@@ -1439,97 +1509,84 @@ QPDFWriter::Members::writeTrailer( @@ -1439,97 +1509,84 @@ QPDFWriter::Members::writeTrailer(
1439 } 1509 }
1440 1510
1441 bool 1511 bool
1442 -QPDFWriter::Members::willFilterStream(  
1443 - QPDFObjectHandle stream,  
1444 - bool& compress_stream, // out only  
1445 - bool& is_root_metadata, // out only  
1446 - std::string* stream_data)  
1447 -{  
1448 - compress_stream = false;  
1449 - is_root_metadata = false;  
1450 -  
1451 - QPDFObjGen old_og = stream.getObjGen();  
1452 - QPDFObjectHandle stream_dict = stream.getDict();  
1453 -  
1454 - if (stream.isRootMetadata()) {  
1455 - is_root_metadata = true;  
1456 - }  
1457 - bool filter = stream.isDataModified() || compress_streams || stream_decode_level;  
1458 - bool filter_on_write = stream.getFilterOnWrite();  
1459 - if (!filter_on_write) {  
1460 - filter = false;  
1461 - }  
1462 - if (filter_on_write && compress_streams) {  
1463 - // Don't filter if the stream is already compressed with FlateDecode. This way we don't make  
1464 - // it worse if the original file used a better Flate algorithm, and we don't spend time and  
1465 - // CPU cycles uncompressing and recompressing stuff. This can be overridden with  
1466 - // setRecompressFlate(true).  
1467 - QPDFObjectHandle filter_obj = stream_dict.getKey("/Filter");  
1468 - if (!recompress_flate && !stream.isDataModified() && filter_obj.isName() &&  
1469 - (filter_obj.getName() == "/FlateDecode" || filter_obj.getName() == "/Fl")) {  
1470 - filter = false; 1512 +QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream)
  1513 +{
  1514 + std::string s;
  1515 + [[maybe_unused]] auto [filter, ignore1, ignore2] = will_filter_stream(stream, &s);
  1516 + return filter;
  1517 +}
  1518 +
  1519 +std::tuple<const bool, const bool, const bool>
  1520 +QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* stream_data)
  1521 +{
  1522 + const bool is_root_metadata = stream.isRootMetadata();
  1523 + bool filter = false;
  1524 + auto decode_level = stream_decode_level;
  1525 + int encode_flags = 0;
  1526 + Dictionary stream_dict = stream.getDict();
  1527 +
  1528 + if (stream.getFilterOnWrite()) {
  1529 + filter = stream.isDataModified() || compress_streams || decode_level != qpdf_dl_none;
  1530 + if (compress_streams) {
  1531 + // Don't filter if the stream is already compressed with FlateDecode. This way we don't
  1532 + // make it worse if the original file used a better Flate algorithm, and we don't spend
  1533 + // time and CPU cycles uncompressing and recompressing stuff. This can be overridden
  1534 + // with setRecompressFlate(true).
  1535 + Name Filter = stream_dict["/Filter"];
  1536 + if (Filter && !recompress_flate && !stream.isDataModified() &&
  1537 + (Filter == "/FlateDecode" || Filter == "/Fl")) {
  1538 + filter = false;
  1539 + }
  1540 + }
  1541 + if (is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) {
  1542 + filter = true;
  1543 + decode_level = qpdf_dl_all;
  1544 + } else if (normalize_content && normalized_streams.contains(stream)) {
  1545 + encode_flags = qpdf_ef_normalize;
  1546 + filter = true;
  1547 + } else if (filter && compress_streams) {
  1548 + encode_flags = qpdf_ef_compress;
1471 } 1549 }
1472 - }  
1473 - bool normalize = false;  
1474 - bool uncompress = false;  
1475 - if (filter_on_write && is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) {  
1476 - filter = true;  
1477 - compress_stream = false;  
1478 - uncompress = true;  
1479 - } else if (filter_on_write && normalize_content && normalized_streams.contains(old_og)) {  
1480 - normalize = true;  
1481 - filter = true;  
1482 - } else if (filter_on_write && filter && compress_streams) {  
1483 - compress_stream = true;  
1484 } 1550 }
1485 1551
1486 // Disable compression for empty streams to improve compatibility 1552 // Disable compression for empty streams to improve compatibility
1487 - if (stream_dict.getKey("/Length").isInteger() &&  
1488 - stream_dict.getKey("/Length").getIntValue() == 0) { 1553 + if (Integer(stream_dict["/Length"]) == 0) {
1489 filter = true; 1554 filter = true;
1490 - compress_stream = false; 1555 + encode_flags = 0;
1491 } 1556 }
1492 1557
1493 - bool filtered = false;  
1494 for (bool first_attempt: {true, false}) { 1558 for (bool first_attempt: {true, false}) {
1495 auto pp_stream_data = 1559 auto pp_stream_data =
1496 stream_data ? pipeline_stack.activate(*stream_data) : pipeline_stack.activate(true); 1560 stream_data ? pipeline_stack.activate(*stream_data) : pipeline_stack.activate(true);
1497 1561
1498 try { 1562 try {
1499 - filtered = stream.pipeStreamData(  
1500 - pipeline,  
1501 - !filter ? 0  
1502 - : ((normalize ? qpdf_ef_normalize : 0) |  
1503 - (compress_stream ? qpdf_ef_compress : 0)),  
1504 - !filter ? qpdf_dl_none : (uncompress ? qpdf_dl_all : stream_decode_level),  
1505 - false,  
1506 - first_attempt);  
1507 - if (filter && !filtered) {  
1508 - // Try again  
1509 - filter = false;  
1510 - stream.setFilterOnWrite(false);  
1511 - } else { 1563 + if (stream.pipeStreamData(
  1564 + pipeline,
  1565 + filter ? encode_flags : 0,
  1566 + filter ? decode_level : qpdf_dl_none,
  1567 + false,
  1568 + first_attempt)) {
  1569 + return {true, encode_flags & qpdf_ef_compress, is_root_metadata};
  1570 + }
  1571 + if (!filter) {
1512 break; 1572 break;
1513 } 1573 }
1514 } catch (std::runtime_error& e) { 1574 } catch (std::runtime_error& e) {
1515 - if (filter && first_attempt) {  
1516 - stream.warn("error while getting stream data: "s + e.what());  
1517 - stream.warn("qpdf will attempt to write the damaged stream unchanged");  
1518 - filter = false;  
1519 - stream.setFilterOnWrite(false);  
1520 - continue; 1575 + if (!(filter && first_attempt)) {
  1576 + throw std::runtime_error(
  1577 + "error while getting stream data for " + stream.unparse() + ": " + e.what());
1521 } 1578 }
1522 - throw std::runtime_error(  
1523 - "error while getting stream data for " + stream.unparse() + ": " + e.what()); 1579 + stream.warn("error while getting stream data: "s + e.what());
  1580 + stream.warn("qpdf will attempt to write the damaged stream unchanged");
1524 } 1581 }
  1582 + // Try again
  1583 + filter = false;
  1584 + stream.setFilterOnWrite(false);
1525 if (stream_data) { 1585 if (stream_data) {
1526 stream_data->clear(); 1586 stream_data->clear();
1527 } 1587 }
1528 } 1588 }
1529 - if (!filtered) {  
1530 - compress_stream = false;  
1531 - }  
1532 - return filtered; 1589 + return {false, false, is_root_metadata};
1533 } 1590 }
1534 1591
1535 void 1592 void
@@ -1724,16 +1781,15 @@ QPDFWriter::Members::unparseObject( @@ -1724,16 +1781,15 @@ QPDFWriter::Members::unparseObject(
1724 } 1781 }
1725 1782
1726 flags |= f_stream; 1783 flags |= f_stream;
1727 - bool compress_stream = false;  
1728 - bool is_metadata = false;  
1729 std::string stream_data; 1784 std::string stream_data;
1730 - if (willFilterStream(object, compress_stream, is_metadata, &stream_data)) { 1785 + auto [filter, compress_stream, is_root_metadata] = will_filter_stream(object, &stream_data);
  1786 + if (filter) {
1731 flags |= f_filtered; 1787 flags |= f_filtered;
1732 } 1788 }
1733 QPDFObjectHandle stream_dict = object.getDict(); 1789 QPDFObjectHandle stream_dict = object.getDict();
1734 1790
1735 cur_stream_length = stream_data.size(); 1791 cur_stream_length = stream_data.size();
1736 - if (is_metadata && encryption && !encryption->getEncryptMetadata()) { 1792 + if (is_root_metadata && encryption && !encryption->getEncryptMetadata()) {
1737 // Don't encrypt stream data for the metadata stream 1793 // Don't encrypt stream data for the metadata stream
1738 cur_data_key.clear(); 1794 cur_data_key.clear();
1739 } 1795 }
@@ -2089,7 +2145,7 @@ QPDFWriter::Members::initializeSpecialStreams() @@ -2089,7 +2145,7 @@ QPDFWriter::Members::initializeSpecialStreams()
2089 void 2145 void
2090 QPDFWriter::Members::preserveObjectStreams() 2146 QPDFWriter::Members::preserveObjectStreams()
2091 { 2147 {
2092 - auto const& xref = QPDF::Writer::getXRefTable(pdf); 2148 + auto const& xref = getXRefTable();
2093 // Our object_to_object_stream map has to map ObjGen -> ObjGen since we may be generating object 2149 // Our object_to_object_stream map has to map ObjGen -> ObjGen since we may be generating object
2094 // streams out of old objects that have generation numbers greater than zero. However in an 2150 // streams out of old objects that have generation numbers greater than zero. However in an
2095 // existing PDF, all object stream objects and all objects in them must have generation 0 2151 // existing PDF, all object stream objects and all objects in them must have generation 0
@@ -2114,7 +2170,7 @@ QPDFWriter::Members::preserveObjectStreams() @@ -2114,7 +2170,7 @@ QPDFWriter::Members::preserveObjectStreams()
2114 if (iter->second.getType() == 2) { 2170 if (iter->second.getType() == 2) {
2115 // Pdf contains object streams. 2171 // Pdf contains object streams.
2116 obj.streams_empty = false; 2172 obj.streams_empty = false;
2117 - auto eligible = QPDF::Writer::getCompressibleObjSet(pdf); 2173 + auto eligible = getCompressibleObjSet();
2118 // The object pointed to by iter may be a previous generation, in which case it is 2174 // The object pointed to by iter may be a previous generation, in which case it is
2119 // removed by getCompressibleObjSet. We need to restart the loop (while the object 2175 // removed by getCompressibleObjSet. We need to restart the loop (while the object
2120 // table may contain multiple generations of an object). 2176 // table may contain multiple generations of an object).
@@ -2145,7 +2201,7 @@ QPDFWriter::Members::generateObjectStreams() @@ -2145,7 +2201,7 @@ QPDFWriter::Members::generateObjectStreams()
2145 2201
2146 // This code doesn't do anything with /Extends. 2202 // This code doesn't do anything with /Extends.
2147 2203
2148 - std::vector<QPDFObjGen> eligible = QPDF::Writer::getCompressibleObjGens(pdf); 2204 + std::vector<QPDFObjGen> eligible = getCompressibleObjGens();
2149 size_t n_object_streams = (eligible.size() + 99U) / 100U; 2205 size_t n_object_streams = (eligible.size() + 99U) / 100U;
2150 2206
2151 initializeTables(2U * n_object_streams); 2207 initializeTables(2U * n_object_streams);
@@ -2173,28 +2229,28 @@ QPDFWriter::Members::generateObjectStreams() @@ -2173,28 +2229,28 @@ QPDFWriter::Members::generateObjectStreams()
2173 } 2229 }
2174 } 2230 }
2175 2231
2176 -QPDFObjectHandle  
2177 -QPDFWriter::Members::getTrimmedTrailer() 2232 +Dictionary
  2233 +QPDFWriter::Members::trimmed_trailer()
2178 { 2234 {
2179 // Remove keys from the trailer that necessarily have to be replaced when writing the file. 2235 // Remove keys from the trailer that necessarily have to be replaced when writing the file.
2180 2236
2181 - QPDFObjectHandle trailer = pdf.getTrailer().unsafeShallowCopy(); 2237 + Dictionary trailer = pdf.getTrailer().unsafeShallowCopy();
2182 2238
2183 // Remove encryption keys 2239 // Remove encryption keys
2184 - trailer.removeKey("/ID");  
2185 - trailer.removeKey("/Encrypt"); 2240 + trailer.erase("/ID");
  2241 + trailer.erase("/Encrypt");
2186 2242
2187 // Remove modification information 2243 // Remove modification information
2188 - trailer.removeKey("/Prev"); 2244 + trailer.erase("/Prev");
2189 2245
2190 // Remove all trailer keys that potentially come from a cross-reference stream 2246 // Remove all trailer keys that potentially come from a cross-reference stream
2191 - trailer.removeKey("/Index");  
2192 - trailer.removeKey("/W");  
2193 - trailer.removeKey("/Length");  
2194 - trailer.removeKey("/Filter");  
2195 - trailer.removeKey("/DecodeParms");  
2196 - trailer.removeKey("/Type");  
2197 - trailer.removeKey("/XRefStm"); 2247 + trailer.erase("/Index");
  2248 + trailer.erase("/W");
  2249 + trailer.erase("/Length");
  2250 + trailer.erase("/Filter");
  2251 + trailer.erase("/DecodeParms");
  2252 + trailer.erase("/Type");
  2253 + trailer.erase("/XRefStm");
2198 2254
2199 return trailer; 2255 return trailer;
2200 } 2256 }
@@ -2226,7 +2282,7 @@ QPDFWriter::Members::prepareFileForWrite() @@ -2226,7 +2282,7 @@ QPDFWriter::Members::prepareFileForWrite()
2226 void 2282 void
2227 QPDFWriter::Members::initializeTables(size_t extra) 2283 QPDFWriter::Members::initializeTables(size_t extra)
2228 { 2284 {
2229 - auto size = QIntC::to_size(QPDF::Writer::tableSize(pdf) + 100) + extra; 2285 + auto size = QIntC::to_size(tableSize() + 100) + extra;
2230 obj.resize(size); 2286 obj.resize(size);
2231 new_obj.resize(size); 2287 new_obj.resize(size);
2232 } 2288 }
@@ -2505,8 +2561,8 @@ QPDFWriter::Members::writeHintStream(int hint_id) @@ -2505,8 +2561,8 @@ QPDFWriter::Members::writeHintStream(int hint_id)
2505 std::string hint_buffer; 2561 std::string hint_buffer;
2506 int S = 0; 2562 int S = 0;
2507 int O = 0; 2563 int O = 0;
2508 - bool compressed = compress_streams && !qdf_mode;  
2509 - QPDF::Writer::generateHintStream(pdf, new_obj, obj, hint_buffer, S, O, compressed); 2564 + bool compressed = compress_streams;
  2565 + generateHintStream(new_obj, obj, hint_buffer, S, O, compressed);
2510 2566
2511 openObject(hint_id); 2567 openObject(hint_id);
2512 setDataKey(hint_id); 2568 setDataKey(hint_id);
@@ -2702,27 +2758,21 @@ QPDFWriter::Members::writeLinearized() @@ -2702,27 +2758,21 @@ QPDFWriter::Members::writeLinearized()
2702 std::map<int, int> stream_cache; 2758 std::map<int, int> stream_cache;
2703 2759
2704 auto skip_stream_parameters = [this, &stream_cache](QPDFObjectHandle& stream) { 2760 auto skip_stream_parameters = [this, &stream_cache](QPDFObjectHandle& stream) {
2705 - auto& result = stream_cache[stream.getObjectID()];  
2706 - if (result == 0) {  
2707 - bool compress_stream;  
2708 - bool is_metadata;  
2709 - if (willFilterStream(stream, compress_stream, is_metadata, nullptr)) {  
2710 - result = 2;  
2711 - } else {  
2712 - result = 1;  
2713 - } 2761 + if (auto& result = stream_cache[stream.getObjectID()]) {
  2762 + return result;
  2763 + } else {
  2764 + return result = will_filter_stream(stream) ? 2 : 1;
2714 } 2765 }
2715 - return result;  
2716 }; 2766 };
2717 2767
2718 - QPDF::Writer::optimize(pdf, obj, skip_stream_parameters); 2768 + optimize(obj, skip_stream_parameters);
2719 2769
2720 std::vector<QPDFObjectHandle> part4; 2770 std::vector<QPDFObjectHandle> part4;
2721 std::vector<QPDFObjectHandle> part6; 2771 std::vector<QPDFObjectHandle> part6;
2722 std::vector<QPDFObjectHandle> part7; 2772 std::vector<QPDFObjectHandle> part7;
2723 std::vector<QPDFObjectHandle> part8; 2773 std::vector<QPDFObjectHandle> part8;
2724 std::vector<QPDFObjectHandle> part9; 2774 std::vector<QPDFObjectHandle> part9;
2725 - QPDF::Writer::getLinearizedParts(pdf, obj, part4, part6, part7, part8, part9); 2775 + getLinearizedParts(obj, part4, part6, part7, part8, part9);
2726 2776
2727 // Object number sequence: 2777 // Object number sequence:
2728 // 2778 //
@@ -3060,12 +3110,12 @@ QPDFWriter::Members::enqueueObjectsStandard() @@ -3060,12 +3110,12 @@ QPDFWriter::Members::enqueueObjectsStandard()
3060 } 3110 }
3061 3111
3062 // Put root first on queue. 3112 // Put root first on queue.
3063 - QPDFObjectHandle trailer = getTrimmedTrailer();  
3064 - enqueueObject(trailer.getKey("/Root")); 3113 + auto trailer = trimmed_trailer();
  3114 + enqueueObject(trailer["/Root"]);
3065 3115
3066 // Next place any other objects referenced from the trailer dictionary into the queue, handling 3116 // Next place any other objects referenced from the trailer dictionary into the queue, handling
3067 // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op. 3117 // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op.
3068 - for (auto& item: trailer.as_dictionary()) { 3118 + for (auto& item: trailer) {
3069 if (!item.second.null()) { 3119 if (!item.second.null()) {
3070 enqueueObject(item.second); 3120 enqueueObject(item.second);
3071 } 3121 }
@@ -3098,9 +3148,7 @@ QPDFWriter::Members::enqueueObjectsPCLm() @@ -3098,9 +3148,7 @@ QPDFWriter::Members::enqueueObjectsPCLm()
3098 } 3148 }
3099 } 3149 }
3100 3150
3101 - // Put root in queue.  
3102 - QPDFObjectHandle trailer = getTrimmedTrailer();  
3103 - enqueueObject(trailer.getKey("/Root")); 3151 + enqueueObject(trimmed_trailer()["/Root"]);
3104 } 3152 }
3105 3153
3106 void 3154 void
libqpdf/qpdf/QPDF_private.hh
@@ -13,72 +13,6 @@ @@ -13,72 +13,6 @@
13 13
14 using namespace qpdf; 14 using namespace qpdf;
15 15
16 -// Writer class is restricted to QPDFWriter so that only it can call certain methods.  
17 -class QPDF::Writer  
18 -{  
19 - friend class QPDFWriter;  
20 -  
21 - private:  
22 - static void  
23 - optimize(  
24 - QPDF& qpdf,  
25 - QPDFWriter::ObjTable const& obj,  
26 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters)  
27 - {  
28 - qpdf.optimize(obj, skip_stream_parameters);  
29 - }  
30 -  
31 - static void  
32 - getLinearizedParts(  
33 - QPDF& qpdf,  
34 - QPDFWriter::ObjTable const& obj,  
35 - std::vector<QPDFObjectHandle>& part4,  
36 - std::vector<QPDFObjectHandle>& part6,  
37 - std::vector<QPDFObjectHandle>& part7,  
38 - std::vector<QPDFObjectHandle>& part8,  
39 - std::vector<QPDFObjectHandle>& part9)  
40 - {  
41 - qpdf.getLinearizedParts(obj, part4, part6, part7, part8, part9);  
42 - }  
43 -  
44 - static void  
45 - generateHintStream(  
46 - QPDF& qpdf,  
47 - QPDFWriter::NewObjTable const& new_obj,  
48 - QPDFWriter::ObjTable const& obj,  
49 - std::string& hint_stream,  
50 - int& S,  
51 - int& O,  
52 - bool compressed)  
53 - {  
54 - qpdf.generateHintStream(new_obj, obj, hint_stream, S, O, compressed);  
55 - }  
56 -  
57 - static std::vector<QPDFObjGen>  
58 - getCompressibleObjGens(QPDF& qpdf)  
59 - {  
60 - return qpdf.getCompressibleObjVector();  
61 - }  
62 -  
63 - static std::vector<bool>  
64 - getCompressibleObjSet(QPDF& qpdf)  
65 - {  
66 - return qpdf.getCompressibleObjSet();  
67 - }  
68 -  
69 - static std::map<QPDFObjGen, QPDFXRefEntry> const&  
70 - getXRefTable(QPDF& qpdf)  
71 - {  
72 - return qpdf.getXRefTableInternal();  
73 - }  
74 -  
75 - static size_t  
76 - tableSize(QPDF& qpdf)  
77 - {  
78 - return qpdf.tableSize();  
79 - }  
80 -};  
81 -  
82 // The Resolver class is restricted to QPDFObject so that only it can resolve indirect 16 // The Resolver class is restricted to QPDFObject so that only it can resolve indirect
83 // references. 17 // references.
84 class QPDF::Resolver 18 class QPDF::Resolver