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,6 +275,8 @@ namespace qpdf::impl
275 275
276 protected: 276 protected:
277 Doc::Linearization& lin; 277 Doc::Linearization& lin;
  278 +
  279 + qpdf::Writer::Config cfg;
278 }; 280 };
279 } // namespace qpdf::impl 281 } // namespace qpdf::impl
280 282
@@ -437,27 +439,9 @@ class QPDFWriter::Members: impl::Writer @@ -437,27 +439,9 @@ class QPDFWriter::Members: impl::Writer
437 bool close_file{false}; 439 bool close_file{false};
438 std::unique_ptr<Pl_Buffer> buffer_pipeline{nullptr}; 440 std::unique_ptr<Pl_Buffer> buffer_pipeline{nullptr};
439 Buffer* output_buffer{nullptr}; 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 std::unique_ptr<QPDF::Doc::Encryption> encryption; 443 std::unique_ptr<QPDF::Doc::Encryption> encryption;
459 std::string encryption_key; 444 std::string encryption_key;
460 - bool encrypt_use_aes{false};  
461 445
462 std::string id1; // for /ID key of 446 std::string id1; // for /ID key of
463 std::string id2; // trailer dictionary 447 std::string id2; // trailer dictionary
@@ -465,9 +449,6 @@ class QPDFWriter::Members: impl::Writer @@ -465,9 +449,6 @@ class QPDFWriter::Members: impl::Writer
465 int final_extension_level{0}; 449 int final_extension_level{0};
466 std::string min_pdf_version; 450 std::string min_pdf_version;
467 int min_extension_level{0}; 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 int encryption_dict_objid{0}; 452 int encryption_dict_objid{0};
472 std::string cur_data_key; 453 std::string cur_data_key;
473 std::unique_ptr<Pipeline> file_pl; 454 std::unique_ptr<Pipeline> file_pl;
@@ -486,13 +467,9 @@ class QPDFWriter::Members: impl::Writer @@ -486,13 +467,9 @@ class QPDFWriter::Members: impl::Writer
486 std::map<QPDFObjGen, int> contents_to_page_seq; 467 std::map<QPDFObjGen, int> contents_to_page_seq;
487 std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects; 468 std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects;
488 Pl_stack pipeline_stack; 469 Pl_stack pipeline_stack;
489 - bool deterministic_id{false};  
490 std::string deterministic_id_data; 470 std::string deterministic_id_data;
491 bool did_write_setup{false}; 471 bool did_write_setup{false};
492 472
493 - // For linearization only  
494 - std::string lin_pass1_filename;  
495 -  
496 // For progress reporting 473 // For progress reporting
497 std::shared_ptr<QPDFWriter::ProgressReporter> progress_reporter; 474 std::shared_ptr<QPDFWriter::ProgressReporter> progress_reporter;
498 int events_expected{0}; 475 int events_expected{0};
@@ -576,7 +553,7 @@ QPDFWriter::setOutputPipeline(Pipeline* p) @@ -576,7 +553,7 @@ QPDFWriter::setOutputPipeline(Pipeline* p)
576 void 553 void
577 QPDFWriter::setObjectStreamMode(qpdf_object_stream_e mode) 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 void 559 void
@@ -584,67 +561,67 @@ QPDFWriter::setStreamDataMode(qpdf_stream_data_e mode) @@ -584,67 +561,67 @@ QPDFWriter::setStreamDataMode(qpdf_stream_data_e mode)
584 { 561 {
585 switch (mode) { 562 switch (mode) {
586 case qpdf_s_uncompress: 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 break; 566 break;
590 567
591 case qpdf_s_preserve: 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 break; 571 break;
595 572
596 case qpdf_s_compress: 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 break; 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 void 582 void
606 QPDFWriter::setCompressStreams(bool val) 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 void 589 void
613 QPDFWriter::setDecodeLevel(qpdf_stream_decode_level_e val) 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 void 596 void
620 QPDFWriter::setRecompressFlate(bool val) 597 QPDFWriter::setRecompressFlate(bool val)
621 { 598 {
622 - m->recompress_flate = val; 599 + m->cfg.recompress_flate_ = val;
623 } 600 }
624 601
625 void 602 void
626 QPDFWriter::setContentNormalization(bool val) 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 void 609 void
633 QPDFWriter::setQDFMode(bool val) 610 QPDFWriter::setQDFMode(bool val)
634 { 611 {
635 - m->qdf_mode = val; 612 + m->cfg.qdf_mode_ = val;
636 } 613 }
637 614
638 void 615 void
639 QPDFWriter::setPreserveUnreferencedObjects(bool val) 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 void 621 void
645 QPDFWriter::setNewlineBeforeEndstream(bool val) 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 void 627 void
@@ -700,16 +677,16 @@ QPDFWriter::setMinimumPDFVersion(PDFVersion const&amp; v) @@ -700,16 +677,16 @@ QPDFWriter::setMinimumPDFVersion(PDFVersion const&amp; v)
700 void 677 void
701 QPDFWriter::forcePDFVersion(std::string const& version, int extension_level) 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 void 684 void
708 QPDFWriter::setExtraHeaderText(std::string const& text) 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 } else { 690 } else {
714 QTC::TC("qpdf", "QPDFWriter extra header text no newline"); 691 QTC::TC("qpdf", "QPDFWriter extra header text no newline");
715 } 692 }
@@ -718,13 +695,13 @@ QPDFWriter::setExtraHeaderText(std::string const&amp; text) @@ -718,13 +695,13 @@ QPDFWriter::setExtraHeaderText(std::string const&amp; text)
718 void 695 void
719 QPDFWriter::setStaticID(bool val) 696 QPDFWriter::setStaticID(bool val)
720 { 697 {
721 - m->static_id = val; 698 + m->cfg.static_id_ = val;
722 } 699 }
723 700
724 void 701 void
725 QPDFWriter::setDeterministicID(bool val) 702 QPDFWriter::setDeterministicID(bool val)
726 { 703 {
727 - m->deterministic_id = val; 704 + m->cfg.deterministic_id_ = val;
728 } 705 }
729 706
730 void 707 void
@@ -738,36 +715,36 @@ QPDFWriter::setStaticAesIV(bool val) @@ -738,36 +715,36 @@ QPDFWriter::setStaticAesIV(bool val)
738 void 715 void
739 QPDFWriter::setSuppressOriginalObjectIDs(bool val) 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 void 721 void
745 QPDFWriter::setPreserveEncryption(bool val) 722 QPDFWriter::setPreserveEncryption(bool val)
746 { 723 {
747 - m->preserve_encryption = val; 724 + m->cfg.preserve_encryption_ = val;
748 } 725 }
749 726
750 void 727 void
751 QPDFWriter::setLinearization(bool val) 728 QPDFWriter::setLinearization(bool val)
752 { 729 {
753 - m->linearized = val; 730 + m->cfg.linearized_ = val;
754 if (val) { 731 if (val) {
755 - m->pclm = false; 732 + m->cfg.pclm_ = false;
756 } 733 }
757 } 734 }
758 735
759 void 736 void
760 QPDFWriter::setLinearizationPass1Filename(std::string const& filename) 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 void 742 void
766 QPDFWriter::setPCLm(bool val) 743 QPDFWriter::setPCLm(bool val)
767 { 744 {
768 - m->pclm = val; 745 + m->cfg.pclm_ = val;
769 if (val) { 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,7 +813,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
836 bool use_aes) 813 bool use_aes)
837 { 814 {
838 m->encryption = std::make_unique<Encryption>(4, 4, 16, encrypt_metadata); 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 m->interpretR3EncryptionParameters( 817 m->interpretR3EncryptionParameters(
841 allow_accessibility, 818 allow_accessibility,
842 allow_extract, 819 allow_extract,
@@ -863,7 +840,7 @@ QPDFWriter::setR5EncryptionParameters( @@ -863,7 +840,7 @@ QPDFWriter::setR5EncryptionParameters(
863 bool encrypt_metadata) 840 bool encrypt_metadata)
864 { 841 {
865 m->encryption = std::make_unique<Encryption>(5, 5, 32, encrypt_metadata); 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 m->interpretR3EncryptionParameters( 844 m->interpretR3EncryptionParameters(
868 allow_accessibility, 845 allow_accessibility,
869 allow_extract, 846 allow_extract,
@@ -899,7 +876,7 @@ QPDFWriter::setR6EncryptionParameters( @@ -899,7 +876,7 @@ QPDFWriter::setR6EncryptionParameters(
899 allow_modify_other, 876 allow_modify_other,
900 print, 877 print,
901 qpdf_r3m_all); 878 qpdf_r3m_all);
902 - m->encrypt_use_aes = true; 879 + m->cfg.encrypt_use_aes_ = true;
903 m->setEncryptionParameters(user_password, owner_password); 880 m->setEncryptionParameters(user_password, owner_password);
904 } 881 }
905 882
@@ -1020,7 +997,7 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf) @@ -1020,7 +997,7 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
1020 void 997 void
1021 QPDFWriter::Members::copyEncryptionParameters(QPDF& qpdf) 998 QPDFWriter::Members::copyEncryptionParameters(QPDF& qpdf)
1022 { 999 {
1023 - preserve_encryption = false; 1000 + cfg.preserve_encryption_ = false;
1024 QPDFObjectHandle trailer = qpdf.getTrailer(); 1001 QPDFObjectHandle trailer = qpdf.getTrailer();
1025 if (trailer.hasKey("/Encrypt")) { 1002 if (trailer.hasKey("/Encrypt")) {
1026 generateID(true); 1003 generateID(true);
@@ -1040,10 +1017,10 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf) @@ -1040,10 +1017,10 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf)
1040 // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of 1017 // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of
1041 // figuring out whether AES is used or not is complicated with /StmF, /StrF, and /EFF 1018 // figuring out whether AES is used or not is complicated with /StmF, /StrF, and /EFF
1042 // all potentially having different values. 1019 // all potentially having different values.
1043 - encrypt_use_aes = true; 1020 + cfg.encrypt_use_aes_ = true;
1044 } 1021 }
1045 QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1); 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 encryption = std::make_unique<Encryption>( 1025 encryption = std::make_unique<Encryption>(
1049 V, 1026 V,
@@ -1084,7 +1061,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext @@ -1084,7 +1061,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext
1084 encryption = nullptr; 1061 encryption = nullptr;
1085 } 1062 }
1086 } else if (compareVersions(major, minor, 1, 6) < 0) { 1063 } else if (compareVersions(major, minor, 1, 6) < 0) {
1087 - if (encrypt_use_aes) { 1064 + if (cfg.encrypt_use_aes_) {
1088 encryption = nullptr; 1065 encryption = nullptr;
1089 } 1066 }
1090 } else if ( 1067 } else if (
@@ -1141,7 +1118,7 @@ QPDFWriter::Members::setEncryptionMinimumVersion() @@ -1141,7 +1118,7 @@ QPDFWriter::Members::setEncryptionMinimumVersion()
1141 } else if (R == 5) { 1118 } else if (R == 5) {
1142 w.setMinimumPDFVersion("1.7", 3); 1119 w.setMinimumPDFVersion("1.7", 3);
1143 } else if (R == 4) { 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 } else if (R == 3) { 1122 } else if (R == 3) {
1146 w.setMinimumPDFVersion("1.4"); 1123 w.setMinimumPDFVersion("1.4");
1147 } else { 1124 } else {
@@ -1154,7 +1131,7 @@ QPDFWriter::Members::setDataKey(int objid) @@ -1154,7 +1131,7 @@ QPDFWriter::Members::setDataKey(int objid)
1154 { 1131 {
1155 if (encryption) { 1132 if (encryption) {
1156 cur_data_key = QPDF::compute_data_key( 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,7 +1199,7 @@ template &lt;typename... Args&gt;
1222 QPDFWriter::Members& 1199 QPDFWriter::Members&
1223 QPDFWriter::Members::write_qdf(Args&&... args) 1200 QPDFWriter::Members::write_qdf(Args&&... args)
1224 { 1201 {
1225 - if (qdf_mode) { 1202 + if (cfg.qdf_mode_) {
1226 pipeline->write(std::forward<Args>(args)...); 1203 pipeline->write(std::forward<Args>(args)...);
1227 } 1204 }
1228 return *this; 1205 return *this;
@@ -1232,7 +1209,7 @@ template &lt;typename... Args&gt; @@ -1232,7 +1209,7 @@ template &lt;typename... Args&gt;
1232 QPDFWriter::Members& 1209 QPDFWriter::Members&
1233 QPDFWriter::Members::write_no_qdf(Args&&... args) 1210 QPDFWriter::Members::write_no_qdf(Args&&... args)
1234 { 1211 {
1235 - if (!qdf_mode) { 1212 + if (!cfg.qdf_mode_) {
1236 pipeline->write(std::forward<Args>(args)...); 1213 pipeline->write(std::forward<Args>(args)...);
1237 } 1214 }
1238 return *this; 1215 return *this;
@@ -1241,7 +1218,7 @@ QPDFWriter::Members::write_no_qdf(Args&amp;&amp;... args) @@ -1241,7 +1218,7 @@ QPDFWriter::Members::write_no_qdf(Args&amp;&amp;... args)
1241 void 1218 void
1242 QPDFWriter::Members::adjustAESStreamLength(size_t& length) 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 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will 1222 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will
1246 // also be prepended by 16 bits of random data. 1223 // also be prepended by 16 bits of random data.
1247 length += 32 - (length & 0xf); 1224 length += 32 - (length & 0xf);
@@ -1253,7 +1230,7 @@ QPDFWriter::Members::write_encrypted(std::string_view str) @@ -1253,7 +1230,7 @@ QPDFWriter::Members::write_encrypted(std::string_view str)
1253 { 1230 {
1254 if (!(encryption && !cur_data_key.empty())) { 1231 if (!(encryption && !cur_data_key.empty())) {
1255 write(str); 1232 write(str);
1256 - } else if (encrypt_use_aes) { 1233 + } else if (cfg.encrypt_use_aes_) {
1257 write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key)); 1234 write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key));
1258 } else { 1235 } else {
1259 write(pl::pipe<Pl_RC4>(str, cur_data_key)); 1236 write(pl::pipe<Pl_RC4>(str, cur_data_key));
@@ -1324,7 +1301,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object) @@ -1324,7 +1301,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1324 "Use QPDF::copyForeignObject to add objects from another file." // 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 // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so 1305 // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so
1329 // will confuse fix-qdf, which expects to see only one XRef stream at the end of the 1306 // will confuse fix-qdf, which expects to see only one XRef stream at the end of the
1330 // file. This case can occur when creating a QDF from a file with object streams when 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,10 +1327,10 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1350 if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) { 1327 if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) {
1351 // For linearized files, uncompressed objects go at end, and we take care of 1328 // For linearized files, uncompressed objects go at end, and we take care of
1352 // assigning numbers to them elsewhere. 1329 // assigning numbers to them elsewhere.
1353 - if (!linearized) { 1330 + if (!cfg.linearized_) {
1354 assignCompressedObjectNumbers(og); 1331 assignCompressedObjectNumbers(og);
1355 } 1332 }
1356 - } else if (!direct_stream_lengths && object.isStream()) { 1333 + } else if (!cfg.direct_stream_lengths_ && object.isStream()) {
1357 // reserve next object ID for length 1334 // reserve next object ID for length
1358 ++next_objid; 1335 ++next_objid;
1359 } 1336 }
@@ -1362,7 +1339,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object) @@ -1362,7 +1339,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1362 return; 1339 return;
1363 } 1340 }
1364 1341
1365 - if (linearized) { 1342 + if (cfg.linearized_) {
1366 return; 1343 return;
1367 } 1344 }
1368 1345
@@ -1383,7 +1360,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object) @@ -1383,7 +1360,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1383 void 1360 void
1384 QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags) 1361 QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
1385 { 1362 {
1386 - if (!linearized) { 1363 + if (!cfg.linearized_) {
1387 enqueue(child); 1364 enqueue(child);
1388 } 1365 }
1389 if (child.indirect()) { 1366 if (child.indirect()) {
@@ -1442,7 +1419,7 @@ QPDFWriter::Members::writeTrailer( @@ -1442,7 +1419,7 @@ QPDFWriter::Members::writeTrailer(
1442 } 1419 }
1443 write("<00000000000000000000000000000000>"); 1420 write("<00000000000000000000000000000000>");
1444 } else { 1421 } else {
1445 - if (linearization_pass == 0 && deterministic_id) { 1422 + if (linearization_pass == 0 && cfg.deterministic_id_) {
1446 computeDeterministicIDData(); 1423 computeDeterministicIDData();
1447 } 1424 }
1448 generateID(encryption.get()); 1425 generateID(encryption.get());
@@ -1473,19 +1450,19 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st @@ -1473,19 +1450,19 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st
1473 { 1450 {
1474 const bool is_root_metadata = stream.isRootMetadata(); 1451 const bool is_root_metadata = stream.isRootMetadata();
1475 bool filter = false; 1452 bool filter = false;
1476 - auto decode_level = stream_decode_level; 1453 + auto decode_level = cfg.stream_decode_level_;
1477 int encode_flags = 0; 1454 int encode_flags = 0;
1478 Dictionary stream_dict = stream.getDict(); 1455 Dictionary stream_dict = stream.getDict();
1479 1456
1480 if (stream.getFilterOnWrite()) { 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 // Don't filter if the stream is already compressed with FlateDecode. This way we don't 1460 // Don't filter if the stream is already compressed with FlateDecode. This way we don't
1484 // make it worse if the original file used a better Flate algorithm, and we don't spend 1461 // make it worse if the original file used a better Flate algorithm, and we don't spend
1485 // time and CPU cycles uncompressing and recompressing stuff. This can be overridden 1462 // time and CPU cycles uncompressing and recompressing stuff. This can be overridden
1486 // with setRecompressFlate(true). 1463 // with setRecompressFlate(true).
1487 Name Filter = stream_dict["/Filter"]; 1464 Name Filter = stream_dict["/Filter"];
1488 - if (Filter && !recompress_flate && !stream.isDataModified() && 1465 + if (Filter && !cfg.recompress_flate_ && !stream.isDataModified() &&
1489 (Filter == "/FlateDecode" || Filter == "/Fl")) { 1466 (Filter == "/FlateDecode" || Filter == "/Fl")) {
1490 filter = false; 1467 filter = false;
1491 } 1468 }
@@ -1493,10 +1470,10 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st @@ -1493,10 +1470,10 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st
1493 if (is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) { 1470 if (is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) {
1494 filter = true; 1471 filter = true;
1495 decode_level = qpdf_dl_all; 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 encode_flags = qpdf_ef_normalize; 1474 encode_flags = qpdf_ef_normalize;
1498 filter = true; 1475 filter = true;
1499 - } else if (filter && compress_streams) { 1476 + } else if (filter && cfg.compress_streams_) {
1500 encode_flags = qpdf_ef_compress; 1477 encode_flags = qpdf_ef_compress;
1501 } 1478 }
1502 } 1479 }
@@ -1550,11 +1527,11 @@ QPDFWriter::Members::unparseObject( @@ -1550,11 +1527,11 @@ QPDFWriter::Members::unparseObject(
1550 // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they 1527 // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they
1551 // include the preceding newline. 1528 // include the preceding newline.
1552 std::string indent_large = " "; 1529 std::string indent_large = " ";
1553 - if (qdf_mode) { 1530 + if (cfg.qdf_mode_) {
1554 indent_large.append(2 * (level + 1), ' '); 1531 indent_large.append(2 * (level + 1), ' ');
1555 indent_large[0] = '\n'; 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 if (auto const tc = object.getTypeCode(); tc == ::ot_array) { 1536 if (auto const tc = object.getTypeCode(); tc == ::ot_array) {
1560 // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the 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,7 +1587,7 @@ QPDFWriter::Members::unparseObject(
1610 if (need_extensions_adbe) { 1587 if (need_extensions_adbe) {
1611 if (!(have_extensions_other || have_extensions_adbe)) { 1588 if (!(have_extensions_other || have_extensions_adbe)) {
1612 // We need Extensions and don't have it. Create it here. 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 extensions = object.replaceKeyAndGetNew( 1591 extensions = object.replaceKeyAndGetNew(
1615 "/Extensions", QPDFObjectHandle::newDictionary()); 1592 "/Extensions", QPDFObjectHandle::newDictionary());
1616 } 1593 }
@@ -1715,7 +1692,7 @@ QPDFWriter::Members::unparseObject( @@ -1715,7 +1692,7 @@ QPDFWriter::Members::unparseObject(
1715 if (flags & f_stream) { 1692 if (flags & f_stream) {
1716 write(indent_large).write("/Length "); 1693 write(indent_large).write("/Length ");
1717 1694
1718 - if (direct_stream_lengths) { 1695 + if (cfg.direct_stream_lengths_) {
1719 write(stream_length); 1696 write(stream_length);
1720 } else { 1697 } else {
1721 write(cur_stream_length_id).write(" 0 R"); 1698 write(cur_stream_length_id).write(" 0 R");
@@ -1728,7 +1705,7 @@ QPDFWriter::Members::unparseObject( @@ -1728,7 +1705,7 @@ QPDFWriter::Members::unparseObject(
1728 write(indent).write(">>"); 1705 write(indent).write(">>");
1729 } else if (tc == ::ot_stream) { 1706 } else if (tc == ::ot_stream) {
1730 // Write stream data to a buffer. 1707 // Write stream data to a buffer.
1731 - if (!direct_stream_lengths) { 1708 + if (!cfg.direct_stream_lengths_) {
1732 cur_stream_length_id = obj[old_og].renumber + 1; 1709 cur_stream_length_id = obj[old_og].renumber + 1;
1733 } 1710 }
1734 1711
@@ -1749,14 +1726,14 @@ QPDFWriter::Members::unparseObject( @@ -1749,14 +1726,14 @@ QPDFWriter::Members::unparseObject(
1749 unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream); 1726 unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream);
1750 char last_char = stream_data.empty() ? '\0' : stream_data.back(); 1727 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1751 write("\nstream\n").write_encrypted(stream_data); 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 write(added_newline ? "\nendstream" : "endstream"); 1730 write(added_newline ? "\nendstream" : "endstream");
1754 } else if (tc == ::ot_string) { 1731 } else if (tc == ::ot_string) {
1755 std::string val; 1732 std::string val;
1756 if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) && 1733 if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
1757 !cur_data_key.empty()) { 1734 !cur_data_key.empty()) {
1758 val = object.getStringValue(); 1735 val = object.getStringValue();
1759 - if (encrypt_use_aes) { 1736 + if (cfg.encrypt_use_aes_) {
1760 Pl_Buffer bufpl("encrypted string"); 1737 Pl_Buffer bufpl("encrypted string");
1761 Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key); 1738 Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key);
1762 pl.writeString(val); 1739 pl.writeString(val);
@@ -1822,7 +1799,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) @@ -1822,7 +1799,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1822 std::string stream_buffer_pass1; 1799 std::string stream_buffer_pass1;
1823 std::string stream_buffer_pass2; 1800 std::string stream_buffer_pass2;
1824 int first_obj = -1; 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 // Pass 1 1804 // Pass 1
1828 auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1); 1805 auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1);
@@ -1834,9 +1811,9 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) @@ -1834,9 +1811,9 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1834 if (first_obj == -1) { 1811 if (first_obj == -1) {
1835 first_obj = new_o; 1812 first_obj = new_o;
1836 } 1813 }
1837 - if (qdf_mode) { 1814 + if (cfg.qdf_mode_) {
1838 write("%% Object stream: object ").write(new_o).write(", index ").write(count); 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 write("; original object ID: ").write(og.getObj()); 1817 write("; original object ID: ").write(og.getObj());
1841 // For compatibility, only write the generation if non-zero. While object 1818 // For compatibility, only write the generation if non-zero. While object
1842 // streams only allow objects with generation 0, if we are generating object 1819 // streams only allow objects with generation 0, if we are generating object
@@ -1915,7 +1892,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) @@ -1915,7 +1892,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1915 if (encryption) { 1892 if (encryption) {
1916 QTC::TC("qpdf", "QPDFWriter encrypt object stream"); 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 cur_data_key.clear(); 1896 cur_data_key.clear();
1920 closeObject(new_stream_id); 1897 closeObject(new_stream_id);
1921 } 1898 }
@@ -1933,7 +1910,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde @@ -1933,7 +1910,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
1933 1910
1934 indicateProgress(false, false); 1911 indicateProgress(false, false);
1935 auto new_id = obj[old_og].renumber; 1912 auto new_id = obj[old_og].renumber;
1936 - if (qdf_mode) { 1913 + if (cfg.qdf_mode_) {
1937 if (page_object_to_seq.contains(old_og)) { 1914 if (page_object_to_seq.contains(old_og)) {
1938 write("%% Page ").write(page_object_to_seq[old_og]).write("\n"); 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,7 +1919,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
1942 } 1919 }
1943 } 1920 }
1944 if (object_stream_index == -1) { 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 write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n"); 1923 write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n");
1947 } 1924 }
1948 openObject(new_id); 1925 openObject(new_id);
@@ -1955,8 +1932,8 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde @@ -1955,8 +1932,8 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
1955 write("\n"); 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 if (added_newline) { 1937 if (added_newline) {
1961 write("%QDF: ignore_newline\n"); 1938 write("%QDF: ignore_newline\n");
1962 } 1939 }
@@ -1992,7 +1969,7 @@ QPDFWriter::Members::generateID(bool encrypted) @@ -1992,7 +1969,7 @@ QPDFWriter::Members::generateID(bool encrypted)
1992 1969
1993 std::string result; 1970 std::string result;
1994 1971
1995 - if (static_id) { 1972 + if (cfg.static_id_) {
1996 // For test suite use only... 1973 // For test suite use only...
1997 static unsigned char tmp[] = { 1974 static unsigned char tmp[] = {
1998 0x31, 1975 0x31,
@@ -2024,7 +2001,7 @@ QPDFWriter::Members::generateID(bool encrypted) @@ -2024,7 +2001,7 @@ QPDFWriter::Members::generateID(bool encrypted)
2024 // that case, would have the same ID regardless of the output file's name. 2001 // that case, would have the same ID regardless of the output file's name.
2025 2002
2026 std::string seed; 2003 std::string seed;
2027 - if (deterministic_id) { 2004 + if (cfg.deterministic_id_) {
2028 if (encrypted) { 2005 if (encrypted) {
2029 throw std::runtime_error( 2006 throw std::runtime_error(
2030 "QPDFWriter: unable to generated a deterministic ID because the file to be " 2007 "QPDFWriter: unable to generated a deterministic ID because the file to be "
@@ -2106,7 +2083,7 @@ QPDFWriter::Members::preserveObjectStreams() @@ -2106,7 +2083,7 @@ QPDFWriter::Members::preserveObjectStreams()
2106 // objects from being included. 2083 // objects from being included.
2107 auto end = xref.cend(); 2084 auto end = xref.cend();
2108 obj.streams_empty = true; 2085 obj.streams_empty = true;
2109 - if (preserve_unreferenced_objects) { 2086 + if (cfg.preserve_unreferenced_objects_) {
2110 for (auto iter = xref.cbegin(); iter != end; ++iter) { 2087 for (auto iter = xref.cbegin(); iter != end; ++iter) {
2111 if (iter->second.getType() == 2) { 2088 if (iter->second.getType() == 2) {
2112 // Pdf contains object streams. 2089 // Pdf contains object streams.
@@ -2248,63 +2225,63 @@ QPDFWriter::Members::doWriteSetup() @@ -2248,63 +2225,63 @@ QPDFWriter::Members::doWriteSetup()
2248 2225
2249 // Do preliminary setup 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 encryption = nullptr; 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 if (encryption) { 2250 if (encryption) {
2274 // Encryption has been explicitly set 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 // Encryption makes looking at contents pretty useless. If the user explicitly encrypted 2254 // Encryption makes looking at contents pretty useless. If the user explicitly encrypted
2278 // though, we still obey that. 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 copyEncryptionParameters(qpdf); 2260 copyEncryptionParameters(qpdf);
2284 } 2261 }
2285 2262
2286 - if (!forced_pdf_version.empty()) { 2263 + if (!cfg.forced_pdf_version_.empty()) {
2287 int major = 0; 2264 int major = 0;
2288 int minor = 0; 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 if (compareVersions(major, minor, 1, 5) < 0) { 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 initializeSpecialStreams(); 2274 initializeSpecialStreams();
2298 } 2275 }
2299 2276
2300 - if (qdf_mode) { 2277 + if (cfg.qdf_mode_) {
2301 // Generate indirect stream lengths for qdf mode since fix-qdf uses them for storing 2278 // Generate indirect stream lengths for qdf mode since fix-qdf uses them for storing
2302 // recomputed stream length data. Certain streams such as object streams, xref streams, and 2279 // recomputed stream length data. Certain streams such as object streams, xref streams, and
2303 // hint streams always get direct stream lengths. 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 case qpdf_o_disable: 2285 case qpdf_o_disable:
2309 initializeTables(); 2286 initializeTables();
2310 obj.streams_empty = true; 2287 obj.streams_empty = true;
@@ -2323,7 +2300,7 @@ QPDFWriter::Members::doWriteSetup() @@ -2323,7 +2300,7 @@ QPDFWriter::Members::doWriteSetup()
2323 } 2300 }
2324 2301
2325 if (!obj.streams_empty) { 2302 if (!obj.streams_empty) {
2326 - if (linearized) { 2303 + if (cfg.linearized_) {
2327 // Page dictionaries are not allowed to be compressed objects. 2304 // Page dictionaries are not allowed to be compressed objects.
2328 for (auto& page: pages) { 2305 for (auto& page: pages) {
2329 if (obj[page].object_stream > 0) { 2306 if (obj[page].object_stream > 0) {
@@ -2332,9 +2309,9 @@ QPDFWriter::Members::doWriteSetup() @@ -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 // handle encrypted files with compressed document catalogs, so we disable them in that 2315 // handle encrypted files with compressed document catalogs, so we disable them in that
2339 // case as well. 2316 // case as well.
2340 if (obj[root_og].object_stream > 0) { 2317 if (obj[root_og].object_stream > 0) {
@@ -2364,9 +2341,9 @@ QPDFWriter::Members::doWriteSetup() @@ -2364,9 +2341,9 @@ QPDFWriter::Members::doWriteSetup()
2364 setMinimumPDFVersion(qpdf.getPDFVersion(), qpdf.getExtensionLevel()); 2341 setMinimumPDFVersion(qpdf.getPDFVersion(), qpdf.getExtensionLevel());
2365 final_pdf_version = min_pdf_version; 2342 final_pdf_version = min_pdf_version;
2366 final_extension_level = min_extension_level; 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,11 +2360,11 @@ QPDFWriter::Members::write()
2383 2360
2384 // Set up progress reporting. For linearized files, we write two passes. events_expected is an 2361 // Set up progress reporting. For linearized files, we write two passes. events_expected is an
2385 // approximation, but it's good enough for progress reporting, which is mostly a guess anyway. 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 prepareFileForWrite(); 2365 prepareFileForWrite();
2389 2366
2390 - if (linearized) { 2367 + if (cfg.linearized_) {
2391 writeLinearized(); 2368 writeLinearized();
2392 } else { 2369 } else {
2393 writeStandard(); 2370 writeStandard();
@@ -2449,10 +2426,10 @@ QPDFWriter::Members::writeEncryptionDictionary() @@ -2449,10 +2426,10 @@ QPDFWriter::Members::writeEncryptionDictionary()
2449 write("<<"); 2426 write("<<");
2450 if (V >= 4) { 2427 if (V >= 4) {
2451 write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM "); 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 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of 2430 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
2454 // MacOS won't open encrypted files without it. 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 if (!encryption->getEncryptMetadata()) { 2433 if (!encryption->getEncryptMetadata()) {
2457 write(" /EncryptMetadata false"); 2434 write(" /EncryptMetadata false");
2458 } 2435 }
@@ -2490,7 +2467,7 @@ void @@ -2490,7 +2467,7 @@ void
2490 QPDFWriter::Members::writeHeader() 2467 QPDFWriter::Members::writeHeader()
2491 { 2468 {
2492 write("%PDF-").write(final_pdf_version); 2469 write("%PDF-").write(final_pdf_version);
2493 - if (pclm) { 2470 + if (cfg.pclm_) {
2494 // PCLm version 2471 // PCLm version
2495 write("\n%PCLm 1.0\n"); 2472 write("\n%PCLm 1.0\n");
2496 } else { 2473 } else {
@@ -2512,7 +2489,7 @@ QPDFWriter::Members::writeHintStream(int hint_id) @@ -2512,7 +2489,7 @@ QPDFWriter::Members::writeHintStream(int hint_id)
2512 std::string hint_buffer; 2489 std::string hint_buffer;
2513 int S = 0; 2490 int S = 0;
2514 int O = 0; 2491 int O = 0;
2515 - bool compressed = compress_streams; 2492 + bool compressed = cfg.compress_streams_;
2516 lin.generateHintStream(new_obj, obj, hint_buffer, S, O, compressed); 2493 lin.generateHintStream(new_obj, obj, hint_buffer, S, O, compressed);
2517 2494
2518 openObject(hint_id); 2495 openObject(hint_id);
@@ -2625,7 +2602,7 @@ QPDFWriter::Members::writeXRefStream( @@ -2625,7 +2602,7 @@ QPDFWriter::Members::writeXRefStream(
2625 new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount()); 2602 new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount());
2626 2603
2627 std::string xref_data; 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 auto pp_xref = pipeline_stack.activate(xref_data); 2607 auto pp_xref = pipeline_stack.activate(xref_data);
2631 2608
@@ -2815,7 +2792,7 @@ QPDFWriter::Members::writeLinearized() @@ -2815,7 +2792,7 @@ QPDFWriter::Members::writeLinearized()
2815 enqueuePart(part8); 2792 enqueuePart(part8);
2816 enqueuePart(part9); 2793 enqueuePart(part9);
2817 if (next_objid != after_second_half) { 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 qpdf_offset_t hint_length = 0; 2798 qpdf_offset_t hint_length = 0;
@@ -2828,15 +2805,15 @@ QPDFWriter::Members::writeLinearized() @@ -2828,15 +2805,15 @@ QPDFWriter::Members::writeLinearized()
2828 auto pp_md5 = pipeline_stack.popper(); 2805 auto pp_md5 = pipeline_stack.popper();
2829 for (int pass: {1, 2}) { 2806 for (int pass: {1, 2}) {
2830 if (pass == 1) { 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 pipeline_stack.activate( 2810 pipeline_stack.activate(
2834 pp_pass1, 2811 pp_pass1,
2835 std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file)); 2812 std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file));
2836 } else { 2813 } else {
2837 pipeline_stack.activate(pp_pass1, true); 2814 pipeline_stack.activate(pp_pass1, true);
2838 } 2815 }
2839 - if (deterministic_id) { 2816 + if (cfg.deterministic_id_) {
2840 pipeline_stack.activate_md5(pp_md5); 2817 pipeline_stack.activate_md5(pp_md5);
2841 } 2818 }
2842 } 2819 }
@@ -2871,7 +2848,7 @@ QPDFWriter::Members::writeLinearized() @@ -2871,7 +2848,7 @@ QPDFWriter::Members::writeLinearized()
2871 2848
2872 // If the user supplied any additional header text, write it here after the linearization 2849 // If the user supplied any additional header text, write it here after the linearization
2873 // parameter dictionary. 2850 // parameter dictionary.
2874 - write(extra_header_text); 2851 + write(cfg.extra_header_text_);
2875 2852
2876 // Part 3: first page cross reference table and trailer. 2853 // Part 3: first page cross reference table and trailer.
2877 2854
@@ -3006,7 +2983,7 @@ QPDFWriter::Members::writeLinearized() @@ -3006,7 +2983,7 @@ QPDFWriter::Members::writeLinearized()
3006 write("startxref\n").write(first_xref_offset).write("\n%%EOF\n"); 2983 write("startxref\n").write(first_xref_offset).write("\n%%EOF\n");
3007 2984
3008 if (pass == 1) { 2985 if (pass == 1) {
3009 - if (deterministic_id) { 2986 + if (cfg.deterministic_id_) {
3010 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1); 2987 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1);
3011 computeDeterministicIDData(); 2988 computeDeterministicIDData();
3012 pp_md5.pop(); 2989 pp_md5.pop();
@@ -3051,7 +3028,7 @@ QPDFWriter::Members::writeLinearized() @@ -3051,7 +3028,7 @@ QPDFWriter::Members::writeLinearized()
3051 void 3028 void
3052 QPDFWriter::Members::enqueueObjectsStandard() 3029 QPDFWriter::Members::enqueueObjectsStandard()
3053 { 3030 {
3054 - if (preserve_unreferenced_objects) { 3031 + if (cfg.preserve_unreferenced_objects_) {
3055 for (auto const& oh: qpdf.getAllObjects()) { 3032 for (auto const& oh: qpdf.getAllObjects()) {
3056 enqueue(oh); 3033 enqueue(oh);
3057 } 3034 }
@@ -3132,16 +3109,16 @@ void @@ -3132,16 +3109,16 @@ void
3132 QPDFWriter::Members::writeStandard() 3109 QPDFWriter::Members::writeStandard()
3133 { 3110 {
3134 auto pp_md5 = pipeline_stack.popper(); 3111 auto pp_md5 = pipeline_stack.popper();
3135 - if (deterministic_id) { 3112 + if (cfg.deterministic_id_) {
3136 pipeline_stack.activate_md5(pp_md5); 3113 pipeline_stack.activate_md5(pp_md5);
3137 } 3114 }
3138 3115
3139 // Start writing 3116 // Start writing
3140 3117
3141 writeHeader(); 3118 writeHeader();
3142 - write(extra_header_text); 3119 + write(cfg.extra_header_text_);
3143 3120
3144 - if (pclm) { 3121 + if (cfg.pclm_) {
3145 enqueueObjectsPCLm(); 3122 enqueueObjectsPCLm();
3146 } else { 3123 } else {
3147 enqueueObjectsStandard(); 3124 enqueueObjectsStandard();
@@ -3171,7 +3148,7 @@ QPDFWriter::Members::writeStandard() @@ -3171,7 +3148,7 @@ QPDFWriter::Members::writeStandard()
3171 } 3148 }
3172 write("startxref\n").write(xref_offset).write("\n%%EOF\n"); 3149 write("startxref\n").write(xref_offset).write("\n%%EOF\n");
3173 3150
3174 - if (deterministic_id) { 3151 + if (cfg.deterministic_id_) {
3175 QTC::TC( 3152 QTC::TC(
3176 "qpdf", 3153 "qpdf",
3177 "QPDFWriter standard deterministic ID", 3154 "QPDFWriter standard deterministic ID",
libqpdf/qpdf/QPDFWriter_private.hh
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 5
6 #include <qpdf/ObjTable.hh> 6 #include <qpdf/ObjTable.hh>
7 #include <qpdf/Pipeline_private.hh> 7 #include <qpdf/Pipeline_private.hh>
  8 +#include <qpdf/QPDF.hh>
8 9
9 // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization 10 // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization
10 // only. 11 // only.
@@ -43,4 +44,50 @@ class QPDFWriter::NewObjTable: public ::ObjTable&lt;QPDFWriter::NewObject&gt; @@ -43,4 +44,50 @@ class QPDFWriter::NewObjTable: public ::ObjTable&lt;QPDFWriter::NewObject&gt;
43 friend class QPDFWriter; 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 #endif // QPDFWRITER_PRIVATE_HH 93 #endif // QPDFWRITER_PRIVATE_HH