Commit de2e46cc333e5ec9dc37a13b713038eacdc6cbd3

Authored by m-holger
1 parent a9d8b976

Refactor `QPDFWriter` configuration: encapsulate configuration parameters in `Co…

…nfig` struct, update member access to use `cfg`, and streamline related logic across `QPDFWriter`.
libqpdf/QPDFWriter.cc
... ... @@ -275,6 +275,8 @@ namespace qpdf::impl
275 275  
276 276 protected:
277 277 Doc::Linearization& lin;
  278 +
  279 + qpdf::Writer::Config cfg;
278 280 };
279 281 } // namespace qpdf::impl
280 282  
... ... @@ -437,27 +439,9 @@ class QPDFWriter::Members: impl::Writer
437 439 bool close_file{false};
438 440 std::unique_ptr<Pl_Buffer> buffer_pipeline{nullptr};
439 441 Buffer* output_buffer{nullptr};
440   - bool normalize_content_set{false};
441   - bool normalize_content{false};
442   - bool compress_streams{true};
443   - bool compress_streams_set{false};
444   - qpdf_stream_decode_level_e stream_decode_level{qpdf_dl_generalized};
445   - bool stream_decode_level_set{false};
446   - bool recompress_flate{false};
447   - bool qdf_mode{false};
448   - bool preserve_unreferenced_objects{false};
449   - bool newline_before_endstream{false};
450   - bool static_id{false};
451   - bool suppress_original_object_ids{false};
452   - bool direct_stream_lengths{true};
453   - bool preserve_encryption{true};
454   - bool linearized{false};
455   - bool pclm{false};
456   - qpdf_object_stream_e object_stream_mode{qpdf_o_preserve};
457 442  
458 443 std::unique_ptr<QPDF::Doc::Encryption> encryption;
459 444 std::string encryption_key;
460   - bool encrypt_use_aes{false};
461 445  
462 446 std::string id1; // for /ID key of
463 447 std::string id2; // trailer dictionary
... ... @@ -465,9 +449,6 @@ class QPDFWriter::Members: impl::Writer
465 449 int final_extension_level{0};
466 450 std::string min_pdf_version;
467 451 int min_extension_level{0};
468   - std::string forced_pdf_version;
469   - int forced_extension_level{0};
470   - std::string extra_header_text;
471 452 int encryption_dict_objid{0};
472 453 std::string cur_data_key;
473 454 std::unique_ptr<Pipeline> file_pl;
... ... @@ -486,13 +467,9 @@ class QPDFWriter::Members: impl::Writer
486 467 std::map<QPDFObjGen, int> contents_to_page_seq;
487 468 std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects;
488 469 Pl_stack pipeline_stack;
489   - bool deterministic_id{false};
490 470 std::string deterministic_id_data;
491 471 bool did_write_setup{false};
492 472  
493   - // For linearization only
494   - std::string lin_pass1_filename;
495   -
496 473 // For progress reporting
497 474 std::shared_ptr<QPDFWriter::ProgressReporter> progress_reporter;
498 475 int events_expected{0};
... ... @@ -576,7 +553,7 @@ QPDFWriter::setOutputPipeline(Pipeline* p)
576 553 void
577 554 QPDFWriter::setObjectStreamMode(qpdf_object_stream_e mode)
578 555 {
579   - m->object_stream_mode = mode;
  556 + m->cfg.object_stream_mode_ = mode;
580 557 }
581 558  
582 559 void
... ... @@ -584,67 +561,67 @@ QPDFWriter::setStreamDataMode(qpdf_stream_data_e mode)
584 561 {
585 562 switch (mode) {
586 563 case qpdf_s_uncompress:
587   - m->stream_decode_level = std::max(qpdf_dl_generalized, m->stream_decode_level);
588   - m->compress_streams = false;
  564 + m->cfg.stream_decode_level_ = std::max(qpdf_dl_generalized, m->cfg.stream_decode_level_);
  565 + m->cfg.compress_streams_ = false;
589 566 break;
590 567  
591 568 case qpdf_s_preserve:
592   - m->stream_decode_level = qpdf_dl_none;
593   - m->compress_streams = false;
  569 + m->cfg.stream_decode_level_ = qpdf_dl_none;
  570 + m->cfg.compress_streams_ = false;
594 571 break;
595 572  
596 573 case qpdf_s_compress:
597   - m->stream_decode_level = std::max(qpdf_dl_generalized, m->stream_decode_level);
598   - m->compress_streams = true;
  574 + m->cfg.stream_decode_level_ = std::max(qpdf_dl_generalized, m->cfg.stream_decode_level_);
  575 + m->cfg.compress_streams_ = true;
599 576 break;
600 577 }
601   - m->stream_decode_level_set = true;
602   - m->compress_streams_set = true;
  578 + m->cfg.stream_decode_level_set_ = true;
  579 + m->cfg.compress_streams_set_ = true;
603 580 }
604 581  
605 582 void
606 583 QPDFWriter::setCompressStreams(bool val)
607 584 {
608   - m->compress_streams = val;
609   - m->compress_streams_set = true;
  585 + m->cfg.compress_streams_ = val;
  586 + m->cfg.compress_streams_set_ = true;
610 587 }
611 588  
612 589 void
613 590 QPDFWriter::setDecodeLevel(qpdf_stream_decode_level_e val)
614 591 {
615   - m->stream_decode_level = val;
616   - m->stream_decode_level_set = true;
  592 + m->cfg.stream_decode_level_ = val;
  593 + m->cfg.stream_decode_level_set_ = true;
617 594 }
618 595  
619 596 void
620 597 QPDFWriter::setRecompressFlate(bool val)
621 598 {
622   - m->recompress_flate = val;
  599 + m->cfg.recompress_flate_ = val;
623 600 }
624 601  
625 602 void
626 603 QPDFWriter::setContentNormalization(bool val)
627 604 {
628   - m->normalize_content_set = true;
629   - m->normalize_content = val;
  605 + m->cfg.normalize_content_set_ = true;
  606 + m->cfg.normalize_content_ = val;
630 607 }
631 608  
632 609 void
633 610 QPDFWriter::setQDFMode(bool val)
634 611 {
635   - m->qdf_mode = val;
  612 + m->cfg.qdf_mode_ = val;
636 613 }
637 614  
638 615 void
639 616 QPDFWriter::setPreserveUnreferencedObjects(bool val)
640 617 {
641   - m->preserve_unreferenced_objects = val;
  618 + m->cfg.preserve_unreferenced_objects_ = val;
642 619 }
643 620  
644 621 void
645 622 QPDFWriter::setNewlineBeforeEndstream(bool val)
646 623 {
647   - m->newline_before_endstream = val;
  624 + m->cfg.newline_before_endstream_ = val;
648 625 }
649 626  
650 627 void
... ... @@ -700,16 +677,16 @@ QPDFWriter::setMinimumPDFVersion(PDFVersion const&amp; v)
700 677 void
701 678 QPDFWriter::forcePDFVersion(std::string const& version, int extension_level)
702 679 {
703   - m->forced_pdf_version = version;
704   - m->forced_extension_level = extension_level;
  680 + m->cfg.forced_pdf_version_ = version;
  681 + m->cfg.forced_extension_level_ = extension_level;
705 682 }
706 683  
707 684 void
708 685 QPDFWriter::setExtraHeaderText(std::string const& text)
709 686 {
710   - m->extra_header_text = text;
711   - if (!m->extra_header_text.empty() && *m->extra_header_text.rbegin() != '\n') {
712   - m->extra_header_text += "\n";
  687 + m->cfg.extra_header_text_ = text;
  688 + if (!m->cfg.extra_header_text_.empty() && m->cfg.extra_header_text_.back() != '\n') {
  689 + m->cfg.extra_header_text_ += "\n";
713 690 } else {
714 691 QTC::TC("qpdf", "QPDFWriter extra header text no newline");
715 692 }
... ... @@ -718,13 +695,13 @@ QPDFWriter::setExtraHeaderText(std::string const&amp; text)
718 695 void
719 696 QPDFWriter::setStaticID(bool val)
720 697 {
721   - m->static_id = val;
  698 + m->cfg.static_id_ = val;
722 699 }
723 700  
724 701 void
725 702 QPDFWriter::setDeterministicID(bool val)
726 703 {
727   - m->deterministic_id = val;
  704 + m->cfg.deterministic_id_ = val;
728 705 }
729 706  
730 707 void
... ... @@ -738,36 +715,36 @@ QPDFWriter::setStaticAesIV(bool val)
738 715 void
739 716 QPDFWriter::setSuppressOriginalObjectIDs(bool val)
740 717 {
741   - m->suppress_original_object_ids = val;
  718 + m->cfg.suppress_original_object_ids_ = val;
742 719 }
743 720  
744 721 void
745 722 QPDFWriter::setPreserveEncryption(bool val)
746 723 {
747   - m->preserve_encryption = val;
  724 + m->cfg.preserve_encryption_ = val;
748 725 }
749 726  
750 727 void
751 728 QPDFWriter::setLinearization(bool val)
752 729 {
753   - m->linearized = val;
  730 + m->cfg.linearized_ = val;
754 731 if (val) {
755   - m->pclm = false;
  732 + m->cfg.pclm_ = false;
756 733 }
757 734 }
758 735  
759 736 void
760 737 QPDFWriter::setLinearizationPass1Filename(std::string const& filename)
761 738 {
762   - m->lin_pass1_filename = filename;
  739 + m->cfg.lin_pass1_filename_ = filename;
763 740 }
764 741  
765 742 void
766 743 QPDFWriter::setPCLm(bool val)
767 744 {
768   - m->pclm = val;
  745 + m->cfg.pclm_ = val;
769 746 if (val) {
770   - m->linearized = false;
  747 + m->cfg.linearized_ = false;
771 748 }
772 749 }
773 750  
... ... @@ -836,7 +813,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
836 813 bool use_aes)
837 814 {
838 815 m->encryption = std::make_unique<Encryption>(4, 4, 16, encrypt_metadata);
839   - m->encrypt_use_aes = use_aes;
  816 + m->cfg.encrypt_use_aes_ = use_aes;
840 817 m->interpretR3EncryptionParameters(
841 818 allow_accessibility,
842 819 allow_extract,
... ... @@ -863,7 +840,7 @@ QPDFWriter::setR5EncryptionParameters(
863 840 bool encrypt_metadata)
864 841 {
865 842 m->encryption = std::make_unique<Encryption>(5, 5, 32, encrypt_metadata);
866   - m->encrypt_use_aes = true;
  843 + m->cfg.encrypt_use_aes_ = true;
867 844 m->interpretR3EncryptionParameters(
868 845 allow_accessibility,
869 846 allow_extract,
... ... @@ -899,7 +876,7 @@ QPDFWriter::setR6EncryptionParameters(
899 876 allow_modify_other,
900 877 print,
901 878 qpdf_r3m_all);
902   - m->encrypt_use_aes = true;
  879 + m->cfg.encrypt_use_aes_ = true;
903 880 m->setEncryptionParameters(user_password, owner_password);
904 881 }
905 882  
... ... @@ -1020,7 +997,7 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
1020 997 void
1021 998 QPDFWriter::Members::copyEncryptionParameters(QPDF& qpdf)
1022 999 {
1023   - preserve_encryption = false;
  1000 + cfg.preserve_encryption_ = false;
1024 1001 QPDFObjectHandle trailer = qpdf.getTrailer();
1025 1002 if (trailer.hasKey("/Encrypt")) {
1026 1003 generateID(true);
... ... @@ -1040,10 +1017,10 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf)
1040 1017 // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of
1041 1018 // figuring out whether AES is used or not is complicated with /StmF, /StrF, and /EFF
1042 1019 // all potentially having different values.
1043   - encrypt_use_aes = true;
  1020 + cfg.encrypt_use_aes_ = true;
1044 1021 }
1045 1022 QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1);
1046   - QTC::TC("qpdf", "QPDFWriter copy use_aes", encrypt_use_aes ? 0 : 1);
  1023 + QTC::TC("qpdf", "QPDFWriter copy use_aes", cfg.encrypt_use_aes_ ? 0 : 1);
1047 1024  
1048 1025 encryption = std::make_unique<Encryption>(
1049 1026 V,
... ... @@ -1084,7 +1061,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext
1084 1061 encryption = nullptr;
1085 1062 }
1086 1063 } else if (compareVersions(major, minor, 1, 6) < 0) {
1087   - if (encrypt_use_aes) {
  1064 + if (cfg.encrypt_use_aes_) {
1088 1065 encryption = nullptr;
1089 1066 }
1090 1067 } else if (
... ... @@ -1141,7 +1118,7 @@ QPDFWriter::Members::setEncryptionMinimumVersion()
1141 1118 } else if (R == 5) {
1142 1119 w.setMinimumPDFVersion("1.7", 3);
1143 1120 } else if (R == 4) {
1144   - w.setMinimumPDFVersion(encrypt_use_aes ? "1.6" : "1.5");
  1121 + w.setMinimumPDFVersion(cfg.encrypt_use_aes_ ? "1.6" : "1.5");
1145 1122 } else if (R == 3) {
1146 1123 w.setMinimumPDFVersion("1.4");
1147 1124 } else {
... ... @@ -1154,7 +1131,7 @@ QPDFWriter::Members::setDataKey(int objid)
1154 1131 {
1155 1132 if (encryption) {
1156 1133 cur_data_key = QPDF::compute_data_key(
1157   - encryption_key, objid, 0, encrypt_use_aes, encryption->getV(), encryption->getR());
  1134 + encryption_key, objid, 0, cfg.encrypt_use_aes_, encryption->getV(), encryption->getR());
1158 1135 }
1159 1136 }
1160 1137  
... ... @@ -1222,7 +1199,7 @@ template &lt;typename... Args&gt;
1222 1199 QPDFWriter::Members&
1223 1200 QPDFWriter::Members::write_qdf(Args&&... args)
1224 1201 {
1225   - if (qdf_mode) {
  1202 + if (cfg.qdf_mode_) {
1226 1203 pipeline->write(std::forward<Args>(args)...);
1227 1204 }
1228 1205 return *this;
... ... @@ -1232,7 +1209,7 @@ template &lt;typename... Args&gt;
1232 1209 QPDFWriter::Members&
1233 1210 QPDFWriter::Members::write_no_qdf(Args&&... args)
1234 1211 {
1235   - if (!qdf_mode) {
  1212 + if (!cfg.qdf_mode_) {
1236 1213 pipeline->write(std::forward<Args>(args)...);
1237 1214 }
1238 1215 return *this;
... ... @@ -1241,7 +1218,7 @@ QPDFWriter::Members::write_no_qdf(Args&amp;&amp;... args)
1241 1218 void
1242 1219 QPDFWriter::Members::adjustAESStreamLength(size_t& length)
1243 1220 {
1244   - if (encryption && !cur_data_key.empty() && encrypt_use_aes) {
  1221 + if (encryption && !cur_data_key.empty() && cfg.encrypt_use_aes_) {
1245 1222 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will
1246 1223 // also be prepended by 16 bits of random data.
1247 1224 length += 32 - (length & 0xf);
... ... @@ -1253,7 +1230,7 @@ QPDFWriter::Members::write_encrypted(std::string_view str)
1253 1230 {
1254 1231 if (!(encryption && !cur_data_key.empty())) {
1255 1232 write(str);
1256   - } else if (encrypt_use_aes) {
  1233 + } else if (cfg.encrypt_use_aes_) {
1257 1234 write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key));
1258 1235 } else {
1259 1236 write(pl::pipe<Pl_RC4>(str, cur_data_key));
... ... @@ -1324,7 +1301,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1324 1301 "Use QPDF::copyForeignObject to add objects from another file." //
1325 1302 );
1326 1303  
1327   - if (qdf_mode && object.isStreamOfType("/XRef")) {
  1304 + if (cfg.qdf_mode_ && object.isStreamOfType("/XRef")) {
1328 1305 // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so
1329 1306 // will confuse fix-qdf, which expects to see only one XRef stream at the end of the
1330 1307 // file. This case can occur when creating a QDF from a file with object streams when
... ... @@ -1350,10 +1327,10 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1350 1327 if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) {
1351 1328 // For linearized files, uncompressed objects go at end, and we take care of
1352 1329 // assigning numbers to them elsewhere.
1353   - if (!linearized) {
  1330 + if (!cfg.linearized_) {
1354 1331 assignCompressedObjectNumbers(og);
1355 1332 }
1356   - } else if (!direct_stream_lengths && object.isStream()) {
  1333 + } else if (!cfg.direct_stream_lengths_ && object.isStream()) {
1357 1334 // reserve next object ID for length
1358 1335 ++next_objid;
1359 1336 }
... ... @@ -1362,7 +1339,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1362 1339 return;
1363 1340 }
1364 1341  
1365   - if (linearized) {
  1342 + if (cfg.linearized_) {
1366 1343 return;
1367 1344 }
1368 1345  
... ... @@ -1383,7 +1360,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1383 1360 void
1384 1361 QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
1385 1362 {
1386   - if (!linearized) {
  1363 + if (!cfg.linearized_) {
1387 1364 enqueue(child);
1388 1365 }
1389 1366 if (child.indirect()) {
... ... @@ -1442,7 +1419,7 @@ QPDFWriter::Members::writeTrailer(
1442 1419 }
1443 1420 write("<00000000000000000000000000000000>");
1444 1421 } else {
1445   - if (linearization_pass == 0 && deterministic_id) {
  1422 + if (linearization_pass == 0 && cfg.deterministic_id_) {
1446 1423 computeDeterministicIDData();
1447 1424 }
1448 1425 generateID(encryption.get());
... ... @@ -1473,19 +1450,19 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st
1473 1450 {
1474 1451 const bool is_root_metadata = stream.isRootMetadata();
1475 1452 bool filter = false;
1476   - auto decode_level = stream_decode_level;
  1453 + auto decode_level = cfg.stream_decode_level_;
1477 1454 int encode_flags = 0;
1478 1455 Dictionary stream_dict = stream.getDict();
1479 1456  
1480 1457 if (stream.getFilterOnWrite()) {
1481   - filter = stream.isDataModified() || compress_streams || decode_level != qpdf_dl_none;
1482   - if (compress_streams) {
  1458 + filter = stream.isDataModified() || cfg.compress_streams_ || decode_level != qpdf_dl_none;
  1459 + if (cfg.compress_streams_) {
1483 1460 // Don't filter if the stream is already compressed with FlateDecode. This way we don't
1484 1461 // make it worse if the original file used a better Flate algorithm, and we don't spend
1485 1462 // time and CPU cycles uncompressing and recompressing stuff. This can be overridden
1486 1463 // with setRecompressFlate(true).
1487 1464 Name Filter = stream_dict["/Filter"];
1488   - if (Filter && !recompress_flate && !stream.isDataModified() &&
  1465 + if (Filter && !cfg.recompress_flate_ && !stream.isDataModified() &&
1489 1466 (Filter == "/FlateDecode" || Filter == "/Fl")) {
1490 1467 filter = false;
1491 1468 }
... ... @@ -1493,10 +1470,10 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st
1493 1470 if (is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) {
1494 1471 filter = true;
1495 1472 decode_level = qpdf_dl_all;
1496   - } else if (normalize_content && normalized_streams.contains(stream)) {
  1473 + } else if (cfg.normalize_content_ && normalized_streams.contains(stream)) {
1497 1474 encode_flags = qpdf_ef_normalize;
1498 1475 filter = true;
1499   - } else if (filter && compress_streams) {
  1476 + } else if (filter && cfg.compress_streams_) {
1500 1477 encode_flags = qpdf_ef_compress;
1501 1478 }
1502 1479 }
... ... @@ -1550,11 +1527,11 @@ QPDFWriter::Members::unparseObject(
1550 1527 // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they
1551 1528 // include the preceding newline.
1552 1529 std::string indent_large = " ";
1553   - if (qdf_mode) {
  1530 + if (cfg.qdf_mode_) {
1554 1531 indent_large.append(2 * (level + 1), ' ');
1555 1532 indent_large[0] = '\n';
1556 1533 }
1557   - std::string_view indent{indent_large.data(), qdf_mode ? indent_large.size() - 2 : 1};
  1534 + std::string_view indent{indent_large.data(), cfg.qdf_mode_ ? indent_large.size() - 2 : 1};
1558 1535  
1559 1536 if (auto const tc = object.getTypeCode(); tc == ::ot_array) {
1560 1537 // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the
... ... @@ -1610,7 +1587,7 @@ QPDFWriter::Members::unparseObject(
1610 1587 if (need_extensions_adbe) {
1611 1588 if (!(have_extensions_other || have_extensions_adbe)) {
1612 1589 // We need Extensions and don't have it. Create it here.
1613   - QTC::TC("qpdf", "QPDFWriter create Extensions", qdf_mode ? 0 : 1);
  1590 + QTC::TC("qpdf", "QPDFWriter create Extensions", cfg.qdf_mode_ ? 0 : 1);
1614 1591 extensions = object.replaceKeyAndGetNew(
1615 1592 "/Extensions", QPDFObjectHandle::newDictionary());
1616 1593 }
... ... @@ -1715,7 +1692,7 @@ QPDFWriter::Members::unparseObject(
1715 1692 if (flags & f_stream) {
1716 1693 write(indent_large).write("/Length ");
1717 1694  
1718   - if (direct_stream_lengths) {
  1695 + if (cfg.direct_stream_lengths_) {
1719 1696 write(stream_length);
1720 1697 } else {
1721 1698 write(cur_stream_length_id).write(" 0 R");
... ... @@ -1728,7 +1705,7 @@ QPDFWriter::Members::unparseObject(
1728 1705 write(indent).write(">>");
1729 1706 } else if (tc == ::ot_stream) {
1730 1707 // Write stream data to a buffer.
1731   - if (!direct_stream_lengths) {
  1708 + if (!cfg.direct_stream_lengths_) {
1732 1709 cur_stream_length_id = obj[old_og].renumber + 1;
1733 1710 }
1734 1711  
... ... @@ -1749,14 +1726,14 @@ QPDFWriter::Members::unparseObject(
1749 1726 unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream);
1750 1727 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1751 1728 write("\nstream\n").write_encrypted(stream_data);
1752   - added_newline = newline_before_endstream || (qdf_mode && last_char != '\n');
  1729 + added_newline = cfg.newline_before_endstream_ || (cfg.qdf_mode_ && last_char != '\n');
1753 1730 write(added_newline ? "\nendstream" : "endstream");
1754 1731 } else if (tc == ::ot_string) {
1755 1732 std::string val;
1756 1733 if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
1757 1734 !cur_data_key.empty()) {
1758 1735 val = object.getStringValue();
1759   - if (encrypt_use_aes) {
  1736 + if (cfg.encrypt_use_aes_) {
1760 1737 Pl_Buffer bufpl("encrypted string");
1761 1738 Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key);
1762 1739 pl.writeString(val);
... ... @@ -1822,7 +1799,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1822 1799 std::string stream_buffer_pass1;
1823 1800 std::string stream_buffer_pass2;
1824 1801 int first_obj = -1;
1825   - const bool compressed = compress_streams && !qdf_mode;
  1802 + const bool compressed = cfg.compress_streams_ && !cfg.qdf_mode_;
1826 1803 {
1827 1804 // Pass 1
1828 1805 auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1);
... ... @@ -1834,9 +1811,9 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1834 1811 if (first_obj == -1) {
1835 1812 first_obj = new_o;
1836 1813 }
1837   - if (qdf_mode) {
  1814 + if (cfg.qdf_mode_) {
1838 1815 write("%% Object stream: object ").write(new_o).write(", index ").write(count);
1839   - if (!suppress_original_object_ids) {
  1816 + if (!cfg.suppress_original_object_ids_) {
1840 1817 write("; original object ID: ").write(og.getObj());
1841 1818 // For compatibility, only write the generation if non-zero. While object
1842 1819 // streams only allow objects with generation 0, if we are generating object
... ... @@ -1915,7 +1892,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1915 1892 if (encryption) {
1916 1893 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
1917 1894 }
1918   - write(newline_before_endstream ? "\nendstream" : "endstream");
  1895 + write(cfg.newline_before_endstream_ ? "\nendstream" : "endstream");
1919 1896 cur_data_key.clear();
1920 1897 closeObject(new_stream_id);
1921 1898 }
... ... @@ -1933,7 +1910,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
1933 1910  
1934 1911 indicateProgress(false, false);
1935 1912 auto new_id = obj[old_og].renumber;
1936   - if (qdf_mode) {
  1913 + if (cfg.qdf_mode_) {
1937 1914 if (page_object_to_seq.contains(old_og)) {
1938 1915 write("%% Page ").write(page_object_to_seq[old_og]).write("\n");
1939 1916 }
... ... @@ -1942,7 +1919,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
1942 1919 }
1943 1920 }
1944 1921 if (object_stream_index == -1) {
1945   - if (qdf_mode && !suppress_original_object_ids) {
  1922 + if (cfg.qdf_mode_ && !cfg.suppress_original_object_ids_) {
1946 1923 write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n");
1947 1924 }
1948 1925 openObject(new_id);
... ... @@ -1955,8 +1932,8 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
1955 1932 write("\n");
1956 1933 }
1957 1934  
1958   - if (!direct_stream_lengths && object.isStream()) {
1959   - if (qdf_mode) {
  1935 + if (!cfg.direct_stream_lengths_ && object.isStream()) {
  1936 + if (cfg.qdf_mode_) {
1960 1937 if (added_newline) {
1961 1938 write("%QDF: ignore_newline\n");
1962 1939 }
... ... @@ -1992,7 +1969,7 @@ QPDFWriter::Members::generateID(bool encrypted)
1992 1969  
1993 1970 std::string result;
1994 1971  
1995   - if (static_id) {
  1972 + if (cfg.static_id_) {
1996 1973 // For test suite use only...
1997 1974 static unsigned char tmp[] = {
1998 1975 0x31,
... ... @@ -2024,7 +2001,7 @@ QPDFWriter::Members::generateID(bool encrypted)
2024 2001 // that case, would have the same ID regardless of the output file's name.
2025 2002  
2026 2003 std::string seed;
2027   - if (deterministic_id) {
  2004 + if (cfg.deterministic_id_) {
2028 2005 if (encrypted) {
2029 2006 throw std::runtime_error(
2030 2007 "QPDFWriter: unable to generated a deterministic ID because the file to be "
... ... @@ -2106,7 +2083,7 @@ QPDFWriter::Members::preserveObjectStreams()
2106 2083 // objects from being included.
2107 2084 auto end = xref.cend();
2108 2085 obj.streams_empty = true;
2109   - if (preserve_unreferenced_objects) {
  2086 + if (cfg.preserve_unreferenced_objects_) {
2110 2087 for (auto iter = xref.cbegin(); iter != end; ++iter) {
2111 2088 if (iter->second.getType() == 2) {
2112 2089 // Pdf contains object streams.
... ... @@ -2248,63 +2225,63 @@ QPDFWriter::Members::doWriteSetup()
2248 2225  
2249 2226 // Do preliminary setup
2250 2227  
2251   - if (linearized) {
2252   - qdf_mode = false;
  2228 + if (cfg.linearized_) {
  2229 + cfg.qdf_mode_ = false;
2253 2230 }
2254 2231  
2255   - if (pclm) {
2256   - stream_decode_level = qpdf_dl_none;
2257   - compress_streams = false;
  2232 + if (cfg.pclm_) {
  2233 + cfg.stream_decode_level_ = qpdf_dl_none;
  2234 + cfg.compress_streams_ = false;
2258 2235 encryption = nullptr;
2259 2236 }
2260 2237  
2261   - if (qdf_mode) {
2262   - if (!normalize_content_set) {
2263   - normalize_content = true;
  2238 + if (cfg.qdf_mode_) {
  2239 + if (!cfg.normalize_content_set_) {
  2240 + cfg.normalize_content_ = true;
2264 2241 }
2265   - if (!compress_streams_set) {
2266   - compress_streams = false;
  2242 + if (!cfg.compress_streams_set_) {
  2243 + cfg.compress_streams_ = false;
2267 2244 }
2268   - if (!stream_decode_level_set) {
2269   - stream_decode_level = qpdf_dl_generalized;
  2245 + if (!cfg.stream_decode_level_set_) {
  2246 + cfg.stream_decode_level_ = qpdf_dl_generalized;
2270 2247 }
2271 2248 }
2272 2249  
2273 2250 if (encryption) {
2274 2251 // Encryption has been explicitly set
2275   - preserve_encryption = false;
2276   - } else if (normalize_content || pclm || qdf_mode) {
  2252 + cfg.preserve_encryption_ = false;
  2253 + } else if (cfg.normalize_content_ || cfg.pclm_ || cfg.qdf_mode_) {
2277 2254 // Encryption makes looking at contents pretty useless. If the user explicitly encrypted
2278 2255 // though, we still obey that.
2279   - preserve_encryption = false;
  2256 + cfg.preserve_encryption_ = false;
2280 2257 }
2281 2258  
2282   - if (preserve_encryption) {
  2259 + if (cfg.preserve_encryption_) {
2283 2260 copyEncryptionParameters(qpdf);
2284 2261 }
2285 2262  
2286   - if (!forced_pdf_version.empty()) {
  2263 + if (!cfg.forced_pdf_version_.empty()) {
2287 2264 int major = 0;
2288 2265 int minor = 0;
2289   - parseVersion(forced_pdf_version, major, minor);
2290   - disableIncompatibleEncryption(major, minor, forced_extension_level);
  2266 + parseVersion(cfg.forced_pdf_version_, major, minor);
  2267 + disableIncompatibleEncryption(major, minor, cfg.forced_extension_level_);
2291 2268 if (compareVersions(major, minor, 1, 5) < 0) {
2292   - object_stream_mode = qpdf_o_disable;
  2269 + cfg.object_stream_mode_ = qpdf_o_disable;
2293 2270 }
2294 2271 }
2295 2272  
2296   - if (qdf_mode || normalize_content) {
  2273 + if (cfg.qdf_mode_ || cfg.normalize_content_) {
2297 2274 initializeSpecialStreams();
2298 2275 }
2299 2276  
2300   - if (qdf_mode) {
  2277 + if (cfg.qdf_mode_) {
2301 2278 // Generate indirect stream lengths for qdf mode since fix-qdf uses them for storing
2302 2279 // recomputed stream length data. Certain streams such as object streams, xref streams, and
2303 2280 // hint streams always get direct stream lengths.
2304   - direct_stream_lengths = false;
  2281 + cfg.direct_stream_lengths_ = false;
2305 2282 }
2306 2283  
2307   - switch (object_stream_mode) {
  2284 + switch (cfg.object_stream_mode_) {
2308 2285 case qpdf_o_disable:
2309 2286 initializeTables();
2310 2287 obj.streams_empty = true;
... ... @@ -2323,7 +2300,7 @@ QPDFWriter::Members::doWriteSetup()
2323 2300 }
2324 2301  
2325 2302 if (!obj.streams_empty) {
2326   - if (linearized) {
  2303 + if (cfg.linearized_) {
2327 2304 // Page dictionaries are not allowed to be compressed objects.
2328 2305 for (auto& page: pages) {
2329 2306 if (obj[page].object_stream > 0) {
... ... @@ -2332,9 +2309,9 @@ QPDFWriter::Members::doWriteSetup()
2332 2309 }
2333 2310 }
2334 2311  
2335   - if (linearized || encryption) {
2336   - // The document catalog is not allowed to be compressed in linearized files either. It
2337   - // also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to
  2312 + if (cfg.linearized_ || encryption) {
  2313 + // The document catalog is not allowed to be compressed in cfg.linearized_ files either.
  2314 + // It also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to
2338 2315 // handle encrypted files with compressed document catalogs, so we disable them in that
2339 2316 // case as well.
2340 2317 if (obj[root_og].object_stream > 0) {
... ... @@ -2364,9 +2341,9 @@ QPDFWriter::Members::doWriteSetup()
2364 2341 setMinimumPDFVersion(qpdf.getPDFVersion(), qpdf.getExtensionLevel());
2365 2342 final_pdf_version = min_pdf_version;
2366 2343 final_extension_level = min_extension_level;
2367   - if (!forced_pdf_version.empty()) {
2368   - final_pdf_version = forced_pdf_version;
2369   - final_extension_level = forced_extension_level;
  2344 + if (!cfg.forced_pdf_version_.empty()) {
  2345 + final_pdf_version = cfg.forced_pdf_version_;
  2346 + final_extension_level = cfg.forced_extension_level_;
2370 2347 }
2371 2348 }
2372 2349  
... ... @@ -2383,11 +2360,11 @@ QPDFWriter::Members::write()
2383 2360  
2384 2361 // Set up progress reporting. For linearized files, we write two passes. events_expected is an
2385 2362 // approximation, but it's good enough for progress reporting, which is mostly a guess anyway.
2386   - events_expected = QIntC::to_int(qpdf.getObjectCount() * (linearized ? 2 : 1));
  2363 + events_expected = QIntC::to_int(qpdf.getObjectCount() * (cfg.linearized_ ? 2 : 1));
2387 2364  
2388 2365 prepareFileForWrite();
2389 2366  
2390   - if (linearized) {
  2367 + if (cfg.linearized_) {
2391 2368 writeLinearized();
2392 2369 } else {
2393 2370 writeStandard();
... ... @@ -2449,10 +2426,10 @@ QPDFWriter::Members::writeEncryptionDictionary()
2449 2426 write("<<");
2450 2427 if (V >= 4) {
2451 2428 write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM ");
2452   - write(encrypt_use_aes ? (V < 5 ? "/AESV2" : "/AESV3") : "/V2");
  2429 + write(cfg.encrypt_use_aes_ ? (V < 5 ? "/AESV2" : "/AESV3") : "/V2");
2453 2430 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
2454 2431 // MacOS won't open encrypted files without it.
2455   - write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>");
  2432 + write(V < 5 ? " /Length 16 >> >>" : " /Length 32 >> >>");
2456 2433 if (!encryption->getEncryptMetadata()) {
2457 2434 write(" /EncryptMetadata false");
2458 2435 }
... ... @@ -2490,7 +2467,7 @@ void
2490 2467 QPDFWriter::Members::writeHeader()
2491 2468 {
2492 2469 write("%PDF-").write(final_pdf_version);
2493   - if (pclm) {
  2470 + if (cfg.pclm_) {
2494 2471 // PCLm version
2495 2472 write("\n%PCLm 1.0\n");
2496 2473 } else {
... ... @@ -2512,7 +2489,7 @@ QPDFWriter::Members::writeHintStream(int hint_id)
2512 2489 std::string hint_buffer;
2513 2490 int S = 0;
2514 2491 int O = 0;
2515   - bool compressed = compress_streams;
  2492 + bool compressed = cfg.compress_streams_;
2516 2493 lin.generateHintStream(new_obj, obj, hint_buffer, S, O, compressed);
2517 2494  
2518 2495 openObject(hint_id);
... ... @@ -2625,7 +2602,7 @@ QPDFWriter::Members::writeXRefStream(
2625 2602 new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount());
2626 2603  
2627 2604 std::string xref_data;
2628   - const bool compressed = compress_streams && !qdf_mode;
  2605 + const bool compressed = cfg.compress_streams_ && !cfg.qdf_mode_;
2629 2606 {
2630 2607 auto pp_xref = pipeline_stack.activate(xref_data);
2631 2608  
... ... @@ -2815,7 +2792,7 @@ QPDFWriter::Members::writeLinearized()
2815 2792 enqueuePart(part8);
2816 2793 enqueuePart(part9);
2817 2794 if (next_objid != after_second_half) {
2818   - throw std::runtime_error("error encountered after writing part 9 of linearized data");
  2795 + throw std::runtime_error("error encountered after writing part 9 of cfg.linearized_ data");
2819 2796 }
2820 2797  
2821 2798 qpdf_offset_t hint_length = 0;
... ... @@ -2828,15 +2805,15 @@ QPDFWriter::Members::writeLinearized()
2828 2805 auto pp_md5 = pipeline_stack.popper();
2829 2806 for (int pass: {1, 2}) {
2830 2807 if (pass == 1) {
2831   - if (!lin_pass1_filename.empty()) {
2832   - lin_pass1_file = QUtil::safe_fopen(lin_pass1_filename.c_str(), "wb");
  2808 + if (!cfg.lin_pass1_filename_.empty()) {
  2809 + lin_pass1_file = QUtil::safe_fopen(cfg.lin_pass1_filename_.data(), "wb");
2833 2810 pipeline_stack.activate(
2834 2811 pp_pass1,
2835 2812 std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file));
2836 2813 } else {
2837 2814 pipeline_stack.activate(pp_pass1, true);
2838 2815 }
2839   - if (deterministic_id) {
  2816 + if (cfg.deterministic_id_) {
2840 2817 pipeline_stack.activate_md5(pp_md5);
2841 2818 }
2842 2819 }
... ... @@ -2871,7 +2848,7 @@ QPDFWriter::Members::writeLinearized()
2871 2848  
2872 2849 // If the user supplied any additional header text, write it here after the linearization
2873 2850 // parameter dictionary.
2874   - write(extra_header_text);
  2851 + write(cfg.extra_header_text_);
2875 2852  
2876 2853 // Part 3: first page cross reference table and trailer.
2877 2854  
... ... @@ -3006,7 +2983,7 @@ QPDFWriter::Members::writeLinearized()
3006 2983 write("startxref\n").write(first_xref_offset).write("\n%%EOF\n");
3007 2984  
3008 2985 if (pass == 1) {
3009   - if (deterministic_id) {
  2986 + if (cfg.deterministic_id_) {
3010 2987 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1);
3011 2988 computeDeterministicIDData();
3012 2989 pp_md5.pop();
... ... @@ -3051,7 +3028,7 @@ QPDFWriter::Members::writeLinearized()
3051 3028 void
3052 3029 QPDFWriter::Members::enqueueObjectsStandard()
3053 3030 {
3054   - if (preserve_unreferenced_objects) {
  3031 + if (cfg.preserve_unreferenced_objects_) {
3055 3032 for (auto const& oh: qpdf.getAllObjects()) {
3056 3033 enqueue(oh);
3057 3034 }
... ... @@ -3132,16 +3109,16 @@ void
3132 3109 QPDFWriter::Members::writeStandard()
3133 3110 {
3134 3111 auto pp_md5 = pipeline_stack.popper();
3135   - if (deterministic_id) {
  3112 + if (cfg.deterministic_id_) {
3136 3113 pipeline_stack.activate_md5(pp_md5);
3137 3114 }
3138 3115  
3139 3116 // Start writing
3140 3117  
3141 3118 writeHeader();
3142   - write(extra_header_text);
  3119 + write(cfg.extra_header_text_);
3143 3120  
3144   - if (pclm) {
  3121 + if (cfg.pclm_) {
3145 3122 enqueueObjectsPCLm();
3146 3123 } else {
3147 3124 enqueueObjectsStandard();
... ... @@ -3171,7 +3148,7 @@ QPDFWriter::Members::writeStandard()
3171 3148 }
3172 3149 write("startxref\n").write(xref_offset).write("\n%%EOF\n");
3173 3150  
3174   - if (deterministic_id) {
  3151 + if (cfg.deterministic_id_) {
3175 3152 QTC::TC(
3176 3153 "qpdf",
3177 3154 "QPDFWriter standard deterministic ID",
... ...
libqpdf/qpdf/QPDFWriter_private.hh
... ... @@ -5,6 +5,7 @@
5 5  
6 6 #include <qpdf/ObjTable.hh>
7 7 #include <qpdf/Pipeline_private.hh>
  8 +#include <qpdf/QPDF.hh>
8 9  
9 10 // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization
10 11 // only.
... ... @@ -43,4 +44,50 @@ class QPDFWriter::NewObjTable: public ::ObjTable&lt;QPDFWriter::NewObject&gt;
43 44 friend class QPDFWriter;
44 45 };
45 46  
  47 +namespace qpdf
  48 +{
  49 + namespace impl
  50 + {
  51 + class Writer;
  52 + }
  53 +
  54 + class Writer
  55 + {
  56 + public:
  57 + class Config
  58 + {
  59 + friend class impl::Writer;
  60 + friend class ::QPDFWriter;
  61 +
  62 + std::string forced_pdf_version_;
  63 + std::string extra_header_text_;
  64 + // For linearization only
  65 + std::string lin_pass1_filename_;
  66 +
  67 + qpdf_object_stream_e object_stream_mode_{qpdf_o_preserve};
  68 +
  69 + int forced_extension_level_{0};
  70 +
  71 + bool normalize_content_set_{false};
  72 + bool normalize_content_{false};
  73 + bool compress_streams_{true};
  74 + bool compress_streams_set_{false};
  75 + qpdf_stream_decode_level_e stream_decode_level_{qpdf_dl_generalized};
  76 + bool stream_decode_level_set_{false};
  77 + bool recompress_flate_{false};
  78 + bool qdf_mode_{false};
  79 + bool preserve_unreferenced_objects_{false};
  80 + bool newline_before_endstream_{false};
  81 + bool deterministic_id_{false};
  82 + bool static_id_{false};
  83 + bool suppress_original_object_ids_{false};
  84 + bool direct_stream_lengths_{true};
  85 + bool preserve_encryption_{true};
  86 + bool linearized_{false};
  87 + bool pclm_{false};
  88 + bool encrypt_use_aes_{false};
  89 + }; // class Writer::Config
  90 + }; // class Writer
  91 +} // namespace qpdf
  92 +
46 93 #endif // QPDFWRITER_PRIVATE_HH
... ...