Commit 2ef866db3c00e5b1617cff7b3774bf5addcd4ca1

Authored by m-holger
Committed by GitHub
2 parents 5ee0e171 efd5aced

Merge pull request #1565 from m-holger/writer

Refactor QPDFWriter configuration
include/qpdf/QPDFJob.hh
@@ -490,7 +490,7 @@ class QPDFJob @@ -490,7 +490,7 @@ class QPDFJob
490 490
491 // Output generation 491 // Output generation
492 void doSplitPages(QPDF& pdf); 492 void doSplitPages(QPDF& pdf);
493 - void setWriterOptions(QPDFWriter&); 493 + void setWriterOptions(qpdf::Writer&);
494 void setEncryptionOptions(QPDFWriter&); 494 void setEncryptionOptions(QPDFWriter&);
495 void maybeFixWritePassword(int R, std::string& password); 495 void maybeFixWritePassword(int R, std::string& password);
496 void writeOutfile(QPDF& pdf); 496 void writeOutfile(QPDF& pdf);
include/qpdf/QPDFWriter.hh
@@ -43,6 +43,11 @@ @@ -43,6 +43,11 @@
43 #include <string_view> 43 #include <string_view>
44 #include <vector> 44 #include <vector>
45 45
  46 +namespace qpdf
  47 +{
  48 + class Writer;
  49 +}
  50 +
46 class QPDF; 51 class QPDF;
47 52
48 // This class implements a simple writer for saving QPDF objects to new PDF files. See comments 53 // This class implements a simple writer for saving QPDF objects to new PDF files. See comments
@@ -440,6 +445,8 @@ class QPDFWriter @@ -440,6 +445,8 @@ class QPDFWriter
440 class NewObjTable; 445 class NewObjTable;
441 446
442 private: 447 private:
  448 + friend class qpdf::Writer;
  449 +
443 class Members; 450 class Members;
444 451
445 std::shared_ptr<Members> m; 452 std::shared_ptr<Members> m;
libqpdf/QPDF.cc
@@ -27,9 +27,8 @@ @@ -27,9 +27,8 @@
27 using namespace qpdf; 27 using namespace qpdf;
28 using namespace std::literals; 28 using namespace std::literals;
29 29
30 -using QDoc = QPDF::Doc;  
31 -using Common = QDoc::Common;  
32 -using Objects = QDoc::Objects; 30 +using Common = impl::Doc::Common;
  31 +using Objects = impl::Doc::Objects;
33 using Foreign = Objects::Foreign; 32 using Foreign = Objects::Foreign;
34 using Streams = Objects::Streams; 33 using Streams = Objects::Streams;
35 34
@@ -724,11 +723,11 @@ QPDF::getRoot() @@ -724,11 +723,11 @@ QPDF::getRoot()
724 std::map<QPDFObjGen, QPDFXRefEntry> 723 std::map<QPDFObjGen, QPDFXRefEntry>
725 QPDF::getXRefTable() 724 QPDF::getXRefTable()
726 { 725 {
727 - return m->objects.getXRefTableInternal(); 726 + return m->objects.xref_table();
728 } 727 }
729 728
730 std::map<QPDFObjGen, QPDFXRefEntry> const& 729 std::map<QPDFObjGen, QPDFXRefEntry> const&
731 -Objects::getXRefTableInternal() 730 +Objects::xref_table()
732 { 731 {
733 if (!m->parsed) { 732 if (!m->parsed) {
734 throw std::logic_error("QPDF::getXRefTable called before parsing."); 733 throw std::logic_error("QPDF::getXRefTable called before parsing.");
libqpdf/QPDFJob.cc
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 #include <qpdf/QPDFPageObjectHelper.hh> 20 #include <qpdf/QPDFPageObjectHelper.hh>
21 #include <qpdf/QPDFSystemError.hh> 21 #include <qpdf/QPDFSystemError.hh>
22 #include <qpdf/QPDFUsage.hh> 22 #include <qpdf/QPDFUsage.hh>
23 -#include <qpdf/QPDFWriter.hh> 23 +#include <qpdf/QPDFWriter_private.hh>
24 #include <qpdf/QPDF_private.hh> 24 #include <qpdf/QPDF_private.hh>
25 #include <qpdf/QTC.hh> 25 #include <qpdf/QTC.hh>
26 #include <qpdf/QUtil.hh> 26 #include <qpdf/QUtil.hh>
@@ -30,8 +30,7 @@ @@ -30,8 +30,7 @@
30 30
31 using namespace qpdf; 31 using namespace qpdf;
32 32
33 -using QDoc = QPDF::Doc;  
34 -using Pages = QDoc::Pages; 33 +using Pages = impl::Doc::Pages;
35 34
36 namespace 35 namespace
37 { 36 {
@@ -477,7 +476,7 @@ QPDFJob::writeQPDF(QPDF&amp; pdf) @@ -477,7 +476,7 @@ QPDFJob::writeQPDF(QPDF&amp; pdf)
477 if (!pdf.getWarnings().empty()) { 476 if (!pdf.getWarnings().empty()) {
478 m->warnings = true; 477 m->warnings = true;
479 } 478 }
480 - if (m->warnings && !m->qcf.suppress_warnings()) { 479 + if (m->warnings && !m->d_cfg.suppress_warnings()) {
481 if (createsOutput()) { 480 if (createsOutput()) {
482 *m->log->getWarn() 481 *m->log->getWarn()
483 << m->message_prefix 482 << m->message_prefix
@@ -745,7 +744,8 @@ QPDFJob::doCheck(QPDF&amp; pdf) @@ -745,7 +744,8 @@ QPDFJob::doCheck(QPDF&amp; pdf)
745 744
746 // Write the file to nowhere, uncompressing streams. This causes full file traversal and 745 // Write the file to nowhere, uncompressing streams. This causes full file traversal and
747 // decoding of all streams we can decode. 746 // decoding of all streams we can decode.
748 - QPDFWriter w(pdf); 747 + Writer::Config cfg;
  748 + Writer w(pdf, cfg);
749 Pl_Discard discard; 749 Pl_Discard discard;
750 w.setOutputPipeline(&discard); 750 w.setOutputPipeline(&discard);
751 w.setDecodeLevel(qpdf_dl_all); 751 w.setDecodeLevel(qpdf_dl_all);
@@ -804,7 +804,7 @@ QPDFJob::doShowObj(QPDF&amp; pdf) @@ -804,7 +804,7 @@ QPDFJob::doShowObj(QPDF&amp; pdf)
804 m->log->saveToStandardOutput(true); 804 m->log->saveToStandardOutput(true);
805 obj.pipeStreamData( 805 obj.pipeStreamData(
806 m->log->getSave().get(), 806 m->log->getSave().get(),
807 - (filter && m->normalize) ? qpdf_ef_normalize : 0, 807 + filter && m->w_cfg.normalize_content() ? qpdf_ef_normalize : 0,
808 filter ? qpdf_dl_all : qpdf_dl_none); 808 filter ? qpdf_dl_all : qpdf_dl_none);
809 } 809 }
810 } else { 810 } else {
@@ -974,7 +974,7 @@ QPDFJob::doJSONObjects(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -974,7 +974,7 @@ QPDFJob::doJSONObjects(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
974 p, 974 p,
975 false, 975 false,
976 first, 976 first,
977 - m->decode_level, 977 + m->w_cfg.decode_level(),
978 m->json_stream_data, 978 m->json_stream_data,
979 m->json_stream_prefix, 979 m->json_stream_prefix,
980 json_objects); 980 json_objects);
@@ -1052,7 +1052,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1052,7 +1052,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1052 j_image.addDictionaryMember("decodeparms", dp_array.getJSON(m->json_version)); 1052 j_image.addDictionaryMember("decodeparms", dp_array.getJSON(m->json_version));
1053 j_image.addDictionaryMember( 1053 j_image.addDictionaryMember(
1054 "filterable", 1054 "filterable",
1055 - JSON::makeBool(image.pipeStreamData(nullptr, 0, m->decode_level, true))); 1055 + JSON::makeBool(image.pipeStreamData(nullptr, 0, m->w_cfg.decode_level(), true)));
1056 } 1056 }
1057 j_page.addDictionaryMember("images", j_images); 1057 j_page.addDictionaryMember("images", j_images);
1058 JSON j_contents = j_page.addDictionaryMember("contents", JSON::makeArray()); 1058 JSON j_contents = j_page.addDictionaryMember("contents", JSON::makeArray());
@@ -1569,7 +1569,7 @@ QPDFJob::doJSON(QPDF&amp; pdf, Pipeline* p) @@ -1569,7 +1569,7 @@ QPDFJob::doJSON(QPDF&amp; pdf, Pipeline* p)
1569 JSON::writeDictionaryItem(p, first, "version", JSON::makeInt(m->json_version), 1); 1569 JSON::writeDictionaryItem(p, first, "version", JSON::makeInt(m->json_version), 1);
1570 JSON j_params = JSON::makeDictionary(); 1570 JSON j_params = JSON::makeDictionary();
1571 std::string decode_level_str; 1571 std::string decode_level_str;
1572 - switch (m->decode_level) { 1572 + switch (m->w_cfg.decode_level()) {
1573 case qpdf_dl_none: 1573 case qpdf_dl_none:
1574 decode_level_str = "none"; 1574 decode_level_str = "none";
1575 break; 1575 break;
@@ -1709,7 +1709,7 @@ QPDFJob::doProcessOnce( @@ -1709,7 +1709,7 @@ QPDFJob::doProcessOnce(
1709 bool main_input) 1709 bool main_input)
1710 { 1710 {
1711 pdf = std::make_unique<QPDF>(); 1711 pdf = std::make_unique<QPDF>();
1712 - pdf->doc().config(m->qcf.log(m->log)); 1712 + pdf->doc().config(m->d_cfg.log(m->log));
1713 if (empty) { 1713 if (empty) {
1714 pdf->emptyPDF(); 1714 pdf->emptyPDF();
1715 } else if (main_input && m->json_input) { 1715 } else if (main_input && m->json_input) {
@@ -1739,7 +1739,7 @@ QPDFJob::doProcess( @@ -1739,7 +1739,7 @@ QPDFJob::doProcess(
1739 // was incorrectly encoded, there's a good chance we'd succeed here. 1739 // was incorrectly encoded, there's a good chance we'd succeed here.
1740 1740
1741 std::string ptemp; 1741 std::string ptemp;
1742 - if (password && !m->qcf.password_is_hex_key()) { 1742 + if (password && !m->d_cfg.password_is_hex_key()) {
1743 if (m->password_mode == QPDFJob::pm_hex_bytes) { 1743 if (m->password_mode == QPDFJob::pm_hex_bytes) {
1744 // Special case: handle --password-mode=hex-bytes for input password as well as output 1744 // Special case: handle --password-mode=hex-bytes for input password as well as output
1745 // password 1745 // password
@@ -1747,7 +1747,7 @@ QPDFJob::doProcess( @@ -1747,7 +1747,7 @@ QPDFJob::doProcess(
1747 password = ptemp.c_str(); 1747 password = ptemp.c_str();
1748 } 1748 }
1749 } 1749 }
1750 - if (!password || empty || m->qcf.password_is_hex_key() || m->suppress_password_recovery) { 1750 + if (!password || empty || m->d_cfg.password_is_hex_key() || m->suppress_password_recovery) {
1751 // There is no password, or we're not doing recovery, so just do the normal processing with 1751 // There is no password, or we're not doing recovery, so just do the normal processing with
1752 // the supplied password. 1752 // the supplied password.
1753 doProcessOnce(pdf, fn, password, empty, used_for_input, main_input); 1753 doProcessOnce(pdf, fn, password, empty, used_for_input, main_input);
@@ -2879,50 +2879,17 @@ parse_version(std::string const&amp; full_version_string, std::string&amp; version, int&amp; @@ -2879,50 +2879,17 @@ parse_version(std::string const&amp; full_version_string, std::string&amp; version, int&amp;
2879 } 2879 }
2880 2880
2881 void 2881 void
2882 -QPDFJob::setWriterOptions(QPDFWriter& w) 2882 +QPDFJob::setWriterOptions(Writer& w)
2883 { 2883 {
2884 if (m->compression_level >= 0) { 2884 if (m->compression_level >= 0) {
2885 Pl_Flate::setCompressionLevel(m->compression_level); 2885 Pl_Flate::setCompressionLevel(m->compression_level);
2886 } 2886 }
2887 - if (m->qdf_mode) {  
2888 - w.setQDFMode(true);  
2889 - }  
2890 - if (m->preserve_unreferenced_objects) {  
2891 - w.setPreserveUnreferencedObjects(true);  
2892 - }  
2893 - if (m->newline_before_endstream) {  
2894 - w.setNewlineBeforeEndstream(true);  
2895 - }  
2896 - if (m->normalize_set) {  
2897 - w.setContentNormalization(m->normalize);  
2898 - }  
2899 - if (m->stream_data_set) {  
2900 - w.setStreamDataMode(m->stream_data_mode);  
2901 - }  
2902 - if (m->compress_streams_set) {  
2903 - w.setCompressStreams(m->compress_streams);  
2904 - }  
2905 - if (m->recompress_flate_set) {  
2906 - w.setRecompressFlate(m->recompress_flate);  
2907 - }  
2908 - if (m->decode_level_set) {  
2909 - w.setDecodeLevel(m->decode_level);  
2910 - }  
2911 if (m->decrypt) { 2887 if (m->decrypt) {
2912 w.setPreserveEncryption(false); 2888 w.setPreserveEncryption(false);
2913 } 2889 }
2914 - if (m->deterministic_id) {  
2915 - w.setDeterministicID(true);  
2916 - }  
2917 - if (m->static_id) {  
2918 - w.setStaticID(true);  
2919 - }  
2920 if (m->static_aes_iv) { 2890 if (m->static_aes_iv) {
2921 w.setStaticAesIV(true); 2891 w.setStaticAesIV(true);
2922 } 2892 }
2923 - if (m->suppress_original_object_id) {  
2924 - w.setSuppressOriginalObjectIDs(true);  
2925 - }  
2926 if (m->copy_encryption) { 2893 if (m->copy_encryption) {
2927 std::unique_ptr<QPDF> encryption_pdf; 2894 std::unique_ptr<QPDF> encryption_pdf;
2928 processFile( 2895 processFile(
@@ -2936,15 +2903,6 @@ QPDFJob::setWriterOptions(QPDFWriter&amp; w) @@ -2936,15 +2903,6 @@ QPDFJob::setWriterOptions(QPDFWriter&amp; w)
2936 if (m->encrypt) { 2903 if (m->encrypt) {
2937 setEncryptionOptions(w); 2904 setEncryptionOptions(w);
2938 } 2905 }
2939 - if (m->linearize) {  
2940 - w.setLinearization(true);  
2941 - }  
2942 - if (!m->linearize_pass1.empty()) {  
2943 - w.setLinearizationPass1Filename(m->linearize_pass1);  
2944 - }  
2945 - if (m->object_stream_set) {  
2946 - w.setObjectStreamMode(m->object_stream_mode);  
2947 - }  
2948 w.setMinimumPDFVersion(m->max_input_version); 2906 w.setMinimumPDFVersion(m->max_input_version);
2949 if (!m->min_version.empty()) { 2907 if (!m->min_version.empty()) {
2950 std::string version; 2908 std::string version;
@@ -2961,15 +2919,13 @@ QPDFJob::setWriterOptions(QPDFWriter&amp; w) @@ -2961,15 +2919,13 @@ QPDFJob::setWriterOptions(QPDFWriter&amp; w)
2961 if (m->progress) { 2919 if (m->progress) {
2962 if (m->progress_handler) { 2920 if (m->progress_handler) {
2963 w.registerProgressReporter( 2921 w.registerProgressReporter(
2964 - std::shared_ptr<QPDFWriter::ProgressReporter>(  
2965 - new QPDFWriter::FunctionProgressReporter(m->progress_handler))); 2922 + std::make_shared<QPDFWriter::FunctionProgressReporter>(m->progress_handler));
2966 } else { 2923 } else {
2967 char const* outfilename = 2924 char const* outfilename =
2968 !m->outfilename.empty() ? m->outfilename.data() : "standard output"; 2925 !m->outfilename.empty() ? m->outfilename.data() : "standard output";
2969 w.registerProgressReporter( 2926 w.registerProgressReporter(
2970 - std::shared_ptr<QPDFWriter::ProgressReporter>(  
2971 - // line-break  
2972 - new ProgressReporter(*m->log->getInfo(), m->message_prefix, outfilename))); 2927 + std::make_shared<ProgressReporter>(
  2928 + *m->log->getInfo(), m->message_prefix, outfilename));
2973 } 2929 }
2974 } 2930 }
2975 } 2931 }
@@ -3014,7 +2970,7 @@ QPDFJob::doSplitPages(QPDF&amp; pdf) @@ -3014,7 +2970,7 @@ QPDFJob::doSplitPages(QPDF&amp; pdf)
3014 last = num_pages; 2970 last = num_pages;
3015 } 2971 }
3016 QPDF outpdf; 2972 QPDF outpdf;
3017 - outpdf.doc().config(m->qcf); 2973 + outpdf.doc().config(m->d_cfg);
3018 outpdf.emptyPDF(); 2974 outpdf.emptyPDF();
3019 QPDFAcroFormDocumentHelper* out_afdh = 2975 QPDFAcroFormDocumentHelper* out_afdh =
3020 afdh.hasAcroForm() ? &outpdf.doc().acroform() : nullptr; 2976 afdh.hasAcroForm() ? &outpdf.doc().acroform() : nullptr;
@@ -3052,7 +3008,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf) @@ -3052,7 +3008,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf)
3052 if (QUtil::same_file(m->infile_nm(), outfile.data())) { 3008 if (QUtil::same_file(m->infile_nm(), outfile.data())) {
3053 throw std::runtime_error("split pages would overwrite input file with " + outfile); 3009 throw std::runtime_error("split pages would overwrite input file with " + outfile);
3054 } 3010 }
3055 - QPDFWriter w(outpdf, outfile.c_str()); 3011 + Writer w(outpdf, m->w_cfg);
  3012 + w.setOutputFilename(outfile.data());
3056 setWriterOptions(w); 3013 setWriterOptions(w);
3057 w.write(); 3014 w.write();
3058 doIfVerbose([&](Pipeline& v, std::string const& prefix) { 3015 doIfVerbose([&](Pipeline& v, std::string const& prefix) {
@@ -3077,9 +3034,8 @@ QPDFJob::writeOutfile(QPDF&amp; pdf) @@ -3077,9 +3034,8 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3077 if (m->json_version) { 3034 if (m->json_version) {
3078 writeJSON(pdf); 3035 writeJSON(pdf);
3079 } else { 3036 } else {
3080 - // QPDFWriter must have block scope so the output file will be closed after write()  
3081 - // finishes.  
3082 - QPDFWriter w(pdf); 3037 + // Writer must have block scope so the output file will be closed after write() finishes.
  3038 + Writer w(pdf, m->w_cfg);
3083 if (!m->outfilename.empty()) { 3039 if (!m->outfilename.empty()) {
3084 w.setOutputFilename(m->outfilename.data()); 3040 w.setOutputFilename(m->outfilename.data());
3085 } else { 3041 } else {
libqpdf/QPDFJob_config.cc
@@ -68,7 +68,7 @@ QPDFJob::Config* @@ -68,7 +68,7 @@ QPDFJob::Config*
68 QPDFJob::Config::check() 68 QPDFJob::Config::check()
69 { 69 {
70 o.m->check = true; 70 o.m->check = true;
71 - o.m->qcf.check_mode(true); 71 + o.m->d_cfg.check_mode(true);
72 o.m->require_outfile = false; 72 o.m->require_outfile = false;
73 return this; 73 return this;
74 } 74 }
@@ -124,8 +124,7 @@ QPDFJob::Config::collate(std::string const&amp; parameter) @@ -124,8 +124,7 @@ QPDFJob::Config::collate(std::string const&amp; parameter)
124 QPDFJob::Config* 124 QPDFJob::Config*
125 QPDFJob::Config::compressStreams(std::string const& parameter) 125 QPDFJob::Config::compressStreams(std::string const& parameter)
126 { 126 {
127 - o.m->compress_streams_set = true;  
128 - o.m->compress_streams = (parameter == "y"); 127 + o.m->w_cfg.compress_streams(parameter == "y");
129 return this; 128 return this;
130 } 129 }
131 130
@@ -146,7 +145,7 @@ QPDFJob::Config::jpegQuality(std::string const&amp; parameter) @@ -146,7 +145,7 @@ QPDFJob::Config::jpegQuality(std::string const&amp; parameter)
146 QPDFJob::Config* 145 QPDFJob::Config*
147 QPDFJob::Config::copyEncryption(std::string const& parameter) 146 QPDFJob::Config::copyEncryption(std::string const& parameter)
148 { 147 {
149 - if (o.m->deterministic_id) { 148 + if (o.m->w_cfg.deterministic_id()) {
150 usage("the deterministic-id option is incompatible with encrypted output files"); 149 usage("the deterministic-id option is incompatible with encrypted output files");
151 } 150 }
152 o.m->inputs.encryption_file = parameter; 151 o.m->inputs.encryption_file = parameter;
@@ -171,7 +170,7 @@ QPDFJob::Config::deterministicId() @@ -171,7 +170,7 @@ QPDFJob::Config::deterministicId()
171 if (o.m->encrypt || o.m->copy_encryption) { 170 if (o.m->encrypt || o.m->copy_encryption) {
172 usage("the deterministic-id option is incompatible with encrypted output files"); 171 usage("the deterministic-id option is incompatible with encrypted output files");
173 } 172 }
174 - o.m->deterministic_id = true; 173 + o.m->w_cfg.deterministic_id(true);
175 return this; 174 return this;
176 } 175 }
177 176
@@ -234,7 +233,7 @@ QPDFJob::Config::generateAppearances() @@ -234,7 +233,7 @@ QPDFJob::Config::generateAppearances()
234 QPDFJob::Config* 233 QPDFJob::Config*
235 QPDFJob::Config::ignoreXrefStreams() 234 QPDFJob::Config::ignoreXrefStreams()
236 { 235 {
237 - o.m->qcf.ignore_xref_streams(true); 236 + o.m->d_cfg.ignore_xref_streams(true);
238 return this; 237 return this;
239 } 238 }
240 239
@@ -327,9 +326,7 @@ QPDFJob::Config::jsonOutput(std::string const&amp; parameter) @@ -327,9 +326,7 @@ QPDFJob::Config::jsonOutput(std::string const&amp; parameter)
327 // No need to set json_stream_data_set -- that indicates explicit use of --json-stream-data. 326 // No need to set json_stream_data_set -- that indicates explicit use of --json-stream-data.
328 o.m->json_stream_data = qpdf_sj_inline; 327 o.m->json_stream_data = qpdf_sj_inline;
329 } 328 }
330 - if (!o.m->decode_level_set) {  
331 - o.m->decode_level = qpdf_dl_none;  
332 - } 329 + o.m->w_cfg.default_decode_level(qpdf_dl_none);
333 o.m->json_keys.insert("qpdf"); 330 o.m->json_keys.insert("qpdf");
334 return this; 331 return this;
335 } 332 }
@@ -373,14 +370,14 @@ QPDFJob::Config::keepInlineImages() @@ -373,14 +370,14 @@ QPDFJob::Config::keepInlineImages()
373 QPDFJob::Config* 370 QPDFJob::Config*
374 QPDFJob::Config::linearize() 371 QPDFJob::Config::linearize()
375 { 372 {
376 - o.m->linearize = true; 373 + o.m->w_cfg.linearize(true);
377 return this; 374 return this;
378 } 375 }
379 376
380 QPDFJob::Config* 377 QPDFJob::Config*
381 QPDFJob::Config::linearizePass1(std::string const& parameter) 378 QPDFJob::Config::linearizePass1(std::string const& parameter)
382 { 379 {
383 - o.m->linearize_pass1 = parameter; 380 + o.m->w_cfg.linearize_pass1(parameter);
384 return this; 381 return this;
385 } 382 }
386 383
@@ -402,29 +399,28 @@ QPDFJob::Config::minVersion(std::string const&amp; parameter) @@ -402,29 +399,28 @@ QPDFJob::Config::minVersion(std::string const&amp; parameter)
402 QPDFJob::Config* 399 QPDFJob::Config*
403 QPDFJob::Config::newlineBeforeEndstream() 400 QPDFJob::Config::newlineBeforeEndstream()
404 { 401 {
405 - o.m->newline_before_endstream = true; 402 + o.m->w_cfg.newline_before_endstream(true);
406 return this; 403 return this;
407 } 404 }
408 405
409 QPDFJob::Config* 406 QPDFJob::Config*
410 QPDFJob::Config::noOriginalObjectIds() 407 QPDFJob::Config::noOriginalObjectIds()
411 { 408 {
412 - o.m->suppress_original_object_id = true; 409 + o.m->w_cfg.no_original_object_ids(true);
413 return this; 410 return this;
414 } 411 }
415 412
416 QPDFJob::Config* 413 QPDFJob::Config*
417 QPDFJob::Config::noWarn() 414 QPDFJob::Config::noWarn()
418 { 415 {
419 - o.m->qcf.suppress_warnings(true); 416 + o.m->d_cfg.suppress_warnings(true);
420 return this; 417 return this;
421 } 418 }
422 419
423 QPDFJob::Config* 420 QPDFJob::Config*
424 QPDFJob::Config::normalizeContent(std::string const& parameter) 421 QPDFJob::Config::normalizeContent(std::string const& parameter)
425 { 422 {
426 - o.m->normalize_set = true;  
427 - o.m->normalize = (parameter == "y"); 423 + o.m->w_cfg.normalize_content(parameter == "y");
428 return this; 424 return this;
429 } 425 }
430 426
@@ -466,14 +462,14 @@ QPDFJob::Config::password(std::string const&amp; parameter) @@ -466,14 +462,14 @@ QPDFJob::Config::password(std::string const&amp; parameter)
466 QPDFJob::Config* 462 QPDFJob::Config*
467 QPDFJob::Config::passwordIsHexKey() 463 QPDFJob::Config::passwordIsHexKey()
468 { 464 {
469 - o.m->qcf.password_is_hex_key(true); 465 + o.m->d_cfg.password_is_hex_key(true);
470 return this; 466 return this;
471 } 467 }
472 468
473 QPDFJob::Config* 469 QPDFJob::Config*
474 QPDFJob::Config::preserveUnreferenced() 470 QPDFJob::Config::preserveUnreferenced()
475 { 471 {
476 - o.m->preserve_unreferenced_objects = true; 472 + o.m->w_cfg.preserve_unreferenced(true);
477 return this; 473 return this;
478 } 474 }
479 475
@@ -494,7 +490,7 @@ QPDFJob::Config::progress() @@ -494,7 +490,7 @@ QPDFJob::Config::progress()
494 QPDFJob::Config* 490 QPDFJob::Config*
495 QPDFJob::Config::qdf() 491 QPDFJob::Config::qdf()
496 { 492 {
497 - o.m->qdf_mode = true; 493 + o.m->w_cfg.qdf(true);
498 return this; 494 return this;
499 } 495 }
500 496
@@ -508,8 +504,7 @@ QPDFJob::Config::rawStreamData() @@ -508,8 +504,7 @@ QPDFJob::Config::rawStreamData()
508 QPDFJob::Config* 504 QPDFJob::Config*
509 QPDFJob::Config::recompressFlate() 505 QPDFJob::Config::recompressFlate()
510 { 506 {
511 - o.m->recompress_flate_set = true;  
512 - o.m->recompress_flate = true; 507 + o.m->w_cfg.recompress_flate(true);
513 return this; 508 return this;
514 } 509 }
515 510
@@ -649,7 +644,7 @@ QPDFJob::Config::staticAesIv() @@ -649,7 +644,7 @@ QPDFJob::Config::staticAesIv()
649 QPDFJob::Config* 644 QPDFJob::Config*
650 QPDFJob::Config::staticId() 645 QPDFJob::Config::staticId()
651 { 646 {
652 - o.m->static_id = true; 647 + o.m->w_cfg.static_id(true);
653 return this; 648 return this;
654 } 649 }
655 650
@@ -663,7 +658,7 @@ QPDFJob::Config::suppressPasswordRecovery() @@ -663,7 +658,7 @@ QPDFJob::Config::suppressPasswordRecovery()
663 QPDFJob::Config* 658 QPDFJob::Config*
664 QPDFJob::Config::suppressRecovery() 659 QPDFJob::Config::suppressRecovery()
665 { 660 {
666 - o.m->qcf.surpress_recovery(true); 661 + o.m->d_cfg.surpress_recovery(true);
667 return this; 662 return this;
668 } 663 }
669 664
@@ -731,13 +726,12 @@ QPDFJob::Config::passwordMode(std::string const&amp; parameter) @@ -731,13 +726,12 @@ QPDFJob::Config::passwordMode(std::string const&amp; parameter)
731 QPDFJob::Config* 726 QPDFJob::Config*
732 QPDFJob::Config::streamData(std::string const& parameter) 727 QPDFJob::Config::streamData(std::string const& parameter)
733 { 728 {
734 - o.m->stream_data_set = true;  
735 if (parameter == "compress") { 729 if (parameter == "compress") {
736 - o.m->stream_data_mode = qpdf_s_compress; 730 + o.m->w_cfg.stream_data(qpdf_s_compress);
737 } else if (parameter == "preserve") { 731 } else if (parameter == "preserve") {
738 - o.m->stream_data_mode = qpdf_s_preserve; 732 + o.m->w_cfg.stream_data(qpdf_s_preserve);
739 } else if (parameter == "uncompress") { 733 } else if (parameter == "uncompress") {
740 - o.m->stream_data_mode = qpdf_s_uncompress; 734 + o.m->w_cfg.stream_data(qpdf_s_uncompress);
741 } else { 735 } else {
742 usage("invalid stream-data option"); 736 usage("invalid stream-data option");
743 } 737 }
@@ -747,15 +741,14 @@ QPDFJob::Config::streamData(std::string const&amp; parameter) @@ -747,15 +741,14 @@ QPDFJob::Config::streamData(std::string const&amp; parameter)
747 QPDFJob::Config* 741 QPDFJob::Config*
748 QPDFJob::Config::decodeLevel(std::string const& parameter) 742 QPDFJob::Config::decodeLevel(std::string const& parameter)
749 { 743 {
750 - o.m->decode_level_set = true;  
751 if (parameter == "none") { 744 if (parameter == "none") {
752 - o.m->decode_level = qpdf_dl_none; 745 + o.m->w_cfg.decode_level(qpdf_dl_none);
753 } else if (parameter == "generalized") { 746 } else if (parameter == "generalized") {
754 - o.m->decode_level = qpdf_dl_generalized; 747 + o.m->w_cfg.decode_level(qpdf_dl_generalized);
755 } else if (parameter == "specialized") { 748 } else if (parameter == "specialized") {
756 - o.m->decode_level = qpdf_dl_specialized; 749 + o.m->w_cfg.decode_level(qpdf_dl_specialized);
757 } else if (parameter == "all") { 750 } else if (parameter == "all") {
758 - o.m->decode_level = qpdf_dl_all; 751 + o.m->w_cfg.decode_level(qpdf_dl_all);
759 } else { 752 } else {
760 usage("invalid option"); 753 usage("invalid option");
761 } 754 }
@@ -765,13 +758,12 @@ QPDFJob::Config::decodeLevel(std::string const&amp; parameter) @@ -765,13 +758,12 @@ QPDFJob::Config::decodeLevel(std::string const&amp; parameter)
765 QPDFJob::Config* 758 QPDFJob::Config*
766 QPDFJob::Config::objectStreams(std::string const& parameter) 759 QPDFJob::Config::objectStreams(std::string const& parameter)
767 { 760 {
768 - o.m->object_stream_set = true;  
769 if (parameter == "disable") { 761 if (parameter == "disable") {
770 - o.m->object_stream_mode = qpdf_o_disable; 762 + o.m->w_cfg.object_streams(qpdf_o_disable);
771 } else if (parameter == "preserve") { 763 } else if (parameter == "preserve") {
772 - o.m->object_stream_mode = qpdf_o_preserve; 764 + o.m->w_cfg.object_streams(qpdf_o_preserve);
773 } else if (parameter == "generate") { 765 } else if (parameter == "generate") {
774 - o.m->object_stream_mode = qpdf_o_generate; 766 + o.m->w_cfg.object_streams(qpdf_o_generate);
775 } else { 767 } else {
776 usage("invalid object stream mode"); 768 usage("invalid object stream mode");
777 } 769 }
@@ -1111,7 +1103,7 @@ std::shared_ptr&lt;QPDFJob::EncConfig&gt; @@ -1111,7 +1103,7 @@ std::shared_ptr&lt;QPDFJob::EncConfig&gt;
1111 QPDFJob::Config::encrypt( 1103 QPDFJob::Config::encrypt(
1112 int keylen, std::string const& user_password, std::string const& owner_password) 1104 int keylen, std::string const& user_password, std::string const& owner_password)
1113 { 1105 {
1114 - if (o.m->deterministic_id) { 1106 + if (o.m->w_cfg.deterministic_id()) {
1115 usage("the deterministic-id option is incompatible with encrypted output files"); 1107 usage("the deterministic-id option is incompatible with encrypted output files");
1116 } 1108 }
1117 o.m->keylen = keylen; 1109 o.m->keylen = keylen;
libqpdf/QPDFWriter.cc
@@ -27,8 +27,8 @@ @@ -27,8 +27,8 @@
27 using namespace std::literals; 27 using namespace std::literals;
28 using namespace qpdf; 28 using namespace qpdf;
29 29
30 -using QDoc = QPDF::Doc;  
31 -using Encryption = QDoc::Encryption; 30 +using Encryption = impl::Doc::Encryption;
  31 +using Config = Writer::Config;
32 32
33 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default) 33 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default)
34 { 34 {
@@ -262,300 +262,231 @@ Pl_stack::Popper::pop() @@ -262,300 +262,231 @@ Pl_stack::Popper::pop()
262 stack = nullptr; 262 stack = nullptr;
263 } 263 }
264 264
265 -// Writer class is restricted to QPDFWriter so that only it can call certain methods.  
266 -class QPDF::Doc::Writer: QPDF::Doc::Common 265 +namespace qpdf::impl
267 { 266 {
268 - friend class QPDFWriter;  
269 - Writer(QPDF& qpdf) :  
270 - Common(qpdf, qpdf.doc().m),  
271 - lin(m->lin),  
272 - objects(m->objects)  
273 - {  
274 - }  
275 -  
276 - protected:  
277 - void  
278 - optimize(  
279 - QPDFWriter::ObjTable const& obj,  
280 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters)  
281 - {  
282 - lin.optimize(obj, skip_stream_parameters);  
283 - }  
284 -  
285 - void  
286 - getLinearizedParts(  
287 - QPDFWriter::ObjTable const& obj,  
288 - std::vector<QPDFObjectHandle>& part4,  
289 - std::vector<QPDFObjectHandle>& part6,  
290 - std::vector<QPDFObjectHandle>& part7,  
291 - std::vector<QPDFObjectHandle>& part8,  
292 - std::vector<QPDFObjectHandle>& part9)  
293 - {  
294 - lin.getLinearizedParts(obj, part4, part6, part7, part8, part9);  
295 - }  
296 -  
297 - void  
298 - generateHintStream(  
299 - QPDFWriter::NewObjTable const& new_obj,  
300 - QPDFWriter::ObjTable const& obj,  
301 - std::string& hint_stream,  
302 - int& S,  
303 - int& O,  
304 - bool compressed)  
305 - {  
306 - lin.generateHintStream(new_obj, obj, hint_stream, S, O, compressed);  
307 - }  
308 -  
309 - std::vector<QPDFObjGen>  
310 - getCompressibleObjGens()  
311 - {  
312 - return objects.getCompressibleObjVector();  
313 - }  
314 -  
315 - std::vector<bool>  
316 - getCompressibleObjSet()  
317 - {  
318 - return objects.getCompressibleObjSet();  
319 - }  
320 -  
321 - std::map<QPDFObjGen, QPDFXRefEntry> const&  
322 - getXRefTable()  
323 - {  
324 - return objects.getXRefTableInternal();  
325 - }  
326 -  
327 - size_t  
328 - tableSize() 267 + // Writer class is restricted to QPDFWriter so that only it can call certain methods.
  268 + class Writer: protected Doc::Common
329 { 269 {
330 - return qpdf.m->objects.tableSize();  
331 - }  
332 -  
333 - QPDF::Doc::Linearization& lin;  
334 - QPDF::Doc::Objects& objects;  
335 -}; 270 + public:
  271 + // flags used by unparseObject
  272 + static int const f_stream = 1 << 0;
  273 + static int const f_filtered = 1 << 1;
  274 + static int const f_in_ostream = 1 << 2;
  275 + static int const f_hex_string = 1 << 3;
  276 + static int const f_no_encryption = 1 << 4;
  277 +
  278 + enum trailer_e { t_normal, t_lin_first, t_lin_second };
  279 +
  280 + Writer() = delete;
  281 + Writer(Writer const&) = delete;
  282 + Writer(Writer&&) = delete;
  283 + Writer& operator=(Writer const&) = delete;
  284 + Writer& operator=(Writer&&) = delete;
  285 + ~Writer()
  286 + {
  287 + if (file && close_file) {
  288 + fclose(file);
  289 + }
  290 + delete output_buffer;
  291 + }
  292 + Writer(QPDF& qpdf, QPDFWriter& w) :
  293 + Common(qpdf.doc()),
  294 + lin(qpdf.doc().linearization()),
  295 + cfg(true),
  296 + root_og(qpdf.getRoot().indirect() ? qpdf.getRoot().id_gen() : QPDFObjGen(-1, 0)),
  297 + pipeline_stack(pipeline)
  298 + {
  299 + }
336 300
337 -class QPDFWriter::Members: QPDF::Doc::Writer 301 + void write();
  302 + std::map<QPDFObjGen, QPDFXRefEntry> getWrittenXRefTable();
  303 + void setMinimumPDFVersion(std::string const& version, int extension_level = 0);
  304 + void copyEncryptionParameters(QPDF&);
  305 + void doWriteSetup();
  306 + void prepareFileForWrite();
  307 +
  308 + void disableIncompatibleEncryption(int major, int minor, int extension_level);
  309 + void interpretR3EncryptionParameters(
  310 + bool allow_accessibility,
  311 + bool allow_extract,
  312 + bool allow_assemble,
  313 + bool allow_annotate_and_form,
  314 + bool allow_form_filling,
  315 + bool allow_modify_other,
  316 + qpdf_r3_print_e print,
  317 + qpdf_r3_modify_e modify);
  318 + void setEncryptionParameters(char const* user_password, char const* owner_password);
  319 + void setEncryptionMinimumVersion();
  320 + void parseVersion(std::string const& version, int& major, int& minor) const;
  321 + int compareVersions(int major1, int minor1, int major2, int minor2) const;
  322 + void generateID(bool encrypted);
  323 + std::string getOriginalID1();
  324 + void initializeTables(size_t extra = 0);
  325 + void preserveObjectStreams();
  326 + void generateObjectStreams();
  327 + void initializeSpecialStreams();
  328 + void enqueue(QPDFObjectHandle const& object);
  329 + void enqueueObjectsStandard();
  330 + void enqueueObjectsPCLm();
  331 + void enqueuePart(std::vector<QPDFObjectHandle>& part);
  332 + void assignCompressedObjectNumbers(QPDFObjGen og);
  333 + Dictionary trimmed_trailer();
  334 +
  335 + // Returns tuple<filter, compress_stream, is_root_metadata>
  336 + std::tuple<const bool, const bool, const bool>
  337 + will_filter_stream(QPDFObjectHandle stream, std::string* stream_data);
  338 +
  339 + // Test whether stream would be filtered if it were written.
  340 + bool will_filter_stream(QPDFObjectHandle stream);
  341 + unsigned int bytesNeeded(long long n);
  342 + void writeBinary(unsigned long long val, unsigned int bytes);
  343 + Writer& write(std::string_view str);
  344 + Writer& write(size_t count, char c);
  345 + Writer& write(std::integral auto val);
  346 + Writer& write_name(std::string const& str);
  347 + Writer& write_string(std::string const& str, bool force_binary = false);
  348 + Writer& write_encrypted(std::string_view str);
  349 +
  350 + template <typename... Args>
  351 + Writer& write_qdf(Args&&... args);
  352 + template <typename... Args>
  353 + Writer& write_no_qdf(Args&&... args);
  354 + void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj);
  355 + void writeObjectStream(QPDFObjectHandle object);
  356 + void writeObject(QPDFObjectHandle object, int object_stream_index = -1);
  357 + void writeTrailer(
  358 + trailer_e which,
  359 + int size,
  360 + bool xref_stream,
  361 + qpdf_offset_t prev,
  362 + int linearization_pass);
  363 + void unparseObject(
  364 + QPDFObjectHandle object,
  365 + size_t level,
  366 + int flags,
  367 + // for stream dictionaries
  368 + size_t stream_length = 0,
  369 + bool compress = false);
  370 + void unparseChild(QPDFObjectHandle const& child, size_t level, int flags);
  371 + int openObject(int objid = 0);
  372 + void closeObject(int objid);
  373 + void writeStandard();
  374 + void writeLinearized();
  375 + void writeEncryptionDictionary();
  376 + void writeHeader();
  377 + void writeHintStream(int hint_id);
  378 + qpdf_offset_t writeXRefTable(trailer_e which, int first, int last, int size);
  379 + qpdf_offset_t writeXRefTable(
  380 + trailer_e which,
  381 + int first,
  382 + int last,
  383 + int size,
  384 + // for linearization
  385 + qpdf_offset_t prev,
  386 + bool suppress_offsets,
  387 + int hint_id,
  388 + qpdf_offset_t hint_offset,
  389 + qpdf_offset_t hint_length,
  390 + int linearization_pass);
  391 + qpdf_offset_t writeXRefStream(
  392 + int objid,
  393 + int max_id,
  394 + qpdf_offset_t max_offset,
  395 + trailer_e which,
  396 + int first,
  397 + int last,
  398 + int size);
  399 + qpdf_offset_t writeXRefStream(
  400 + int objid,
  401 + int max_id,
  402 + qpdf_offset_t max_offset,
  403 + trailer_e which,
  404 + int first,
  405 + int last,
  406 + int size,
  407 + // for linearization
  408 + qpdf_offset_t prev,
  409 + int hint_id,
  410 + qpdf_offset_t hint_offset,
  411 + qpdf_offset_t hint_length,
  412 + bool skip_compression,
  413 + int linearization_pass);
  414 +
  415 + void setDataKey(int objid);
  416 + void indicateProgress(bool decrement, bool finished);
  417 + size_t calculateXrefStreamPadding(qpdf_offset_t xref_bytes);
  418 +
  419 + void adjustAESStreamLength(size_t& length);
  420 + void computeDeterministicIDData();
  421 +
  422 + protected:
  423 + Doc::Linearization& lin;
  424 +
  425 + qpdf::Writer::Config cfg;
  426 +
  427 + QPDFObjGen root_og{-1, 0};
  428 + char const* filename{"unspecified"};
  429 + FILE* file{nullptr};
  430 + bool close_file{false};
  431 + std::unique_ptr<Pl_Buffer> buffer_pipeline{nullptr};
  432 + Buffer* output_buffer{nullptr};
  433 +
  434 + std::unique_ptr<QPDF::Doc::Encryption> encryption;
  435 + std::string encryption_key;
  436 +
  437 + std::string id1; // for /ID key of
  438 + std::string id2; // trailer dictionary
  439 + std::string final_pdf_version;
  440 + int final_extension_level{0};
  441 + std::string min_pdf_version;
  442 + int min_extension_level{0};
  443 + int encryption_dict_objid{0};
  444 + std::string cur_data_key;
  445 + std::unique_ptr<Pipeline> file_pl;
  446 + qpdf::pl::Count* pipeline{nullptr};
  447 + std::vector<QPDFObjectHandle> object_queue;
  448 + size_t object_queue_front{0};
  449 + QPDFWriter::ObjTable obj;
  450 + QPDFWriter::NewObjTable new_obj;
  451 + int next_objid{1};
  452 + int cur_stream_length_id{0};
  453 + size_t cur_stream_length{0};
  454 + bool added_newline{false};
  455 + size_t max_ostream_index{0};
  456 + std::set<QPDFObjGen> normalized_streams;
  457 + std::map<QPDFObjGen, int> page_object_to_seq;
  458 + std::map<QPDFObjGen, int> contents_to_page_seq;
  459 + std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects;
  460 + Pl_stack pipeline_stack;
  461 + std::string deterministic_id_data;
  462 + bool did_write_setup{false};
  463 +
  464 + // For progress reporting
  465 + std::shared_ptr<QPDFWriter::ProgressReporter> progress_reporter;
  466 + int events_expected{0};
  467 + int events_seen{0};
  468 + int next_progress_report{0};
  469 + }; // class qpdf::impl::Writer
  470 +
  471 +} // namespace qpdf::impl
  472 +
  473 +class QPDFWriter::Members: impl::Writer
338 { 474 {
339 friend class QPDFWriter; 475 friend class QPDFWriter;
  476 + friend class qpdf::Writer;
340 477
341 public: 478 public:
342 - // flags used by unparseObject  
343 - static int const f_stream = 1 << 0;  
344 - static int const f_filtered = 1 << 1;  
345 - static int const f_in_ostream = 1 << 2;  
346 - static int const f_hex_string = 1 << 3;  
347 - static int const f_no_encryption = 1 << 4;  
348 -  
349 - enum trailer_e { t_normal, t_lin_first, t_lin_second };  
350 -  
351 - Members(QPDFWriter& w, QPDF& pdf) :  
352 - QPDF::Doc::Writer(pdf),  
353 - w(w),  
354 - root_og(  
355 - qpdf.getRoot().getObjGen().isIndirect() ? qpdf.getRoot().getObjGen()  
356 - : QPDFObjGen(-1, 0)),  
357 - pipeline_stack(pipeline) 479 + Members(QPDFWriter& w, QPDF& qpdf) :
  480 + impl::Writer(qpdf, w)
358 { 481 {
359 } 482 }
360 -  
361 - Members(Members const&) = delete;  
362 -  
363 - ~Members()  
364 - {  
365 - if (file && close_file) {  
366 - fclose(file);  
367 - }  
368 - delete output_buffer;  
369 - }  
370 -  
371 - void write();  
372 - std::map<QPDFObjGen, QPDFXRefEntry> getWrittenXRefTable();  
373 - void setMinimumPDFVersion(std::string const& version, int extension_level);  
374 - void copyEncryptionParameters(QPDF&);  
375 - void doWriteSetup();  
376 - void prepareFileForWrite();  
377 -  
378 - void disableIncompatibleEncryption(int major, int minor, int extension_level);  
379 - void interpretR3EncryptionParameters(  
380 - bool allow_accessibility,  
381 - bool allow_extract,  
382 - bool allow_assemble,  
383 - bool allow_annotate_and_form,  
384 - bool allow_form_filling,  
385 - bool allow_modify_other,  
386 - qpdf_r3_print_e print,  
387 - qpdf_r3_modify_e modify);  
388 - void setEncryptionParameters(char const* user_password, char const* owner_password);  
389 - void setEncryptionMinimumVersion();  
390 - void parseVersion(std::string const& version, int& major, int& minor) const;  
391 - int compareVersions(int major1, int minor1, int major2, int minor2) const;  
392 - void generateID(bool encrypted);  
393 - std::string getOriginalID1();  
394 - void initializeTables(size_t extra = 0);  
395 - void preserveObjectStreams();  
396 - void generateObjectStreams();  
397 - void initializeSpecialStreams();  
398 - void enqueue(QPDFObjectHandle const& object);  
399 - void enqueueObjectsStandard();  
400 - void enqueueObjectsPCLm();  
401 - void enqueuePart(std::vector<QPDFObjectHandle>& part);  
402 - void assignCompressedObjectNumbers(QPDFObjGen og);  
403 - Dictionary trimmed_trailer();  
404 -  
405 - // Returns tuple<filter, compress_stream, is_root_metadata>  
406 - std::tuple<const bool, const bool, const bool>  
407 - will_filter_stream(QPDFObjectHandle stream, std::string* stream_data);  
408 -  
409 - // Test whether stream would be filtered if it were written.  
410 - bool will_filter_stream(QPDFObjectHandle stream);  
411 - unsigned int bytesNeeded(long long n);  
412 - void writeBinary(unsigned long long val, unsigned int bytes);  
413 - Members& write(std::string_view str);  
414 - Members& write(size_t count, char c);  
415 - Members& write(std::integral auto val);  
416 - Members& write_name(std::string const& str);  
417 - Members& write_string(std::string const& str, bool force_binary = false);  
418 - Members& write_encrypted(std::string_view str);  
419 -  
420 - template <typename... Args>  
421 - Members& write_qdf(Args&&... args);  
422 - template <typename... Args>  
423 - Members& write_no_qdf(Args&&... args);  
424 - void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj);  
425 - void writeObjectStream(QPDFObjectHandle object);  
426 - void writeObject(QPDFObjectHandle object, int object_stream_index = -1);  
427 - void writeTrailer(  
428 - trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass);  
429 - void unparseObject(  
430 - QPDFObjectHandle object,  
431 - size_t level,  
432 - int flags,  
433 - // for stream dictionaries  
434 - size_t stream_length = 0,  
435 - bool compress = false);  
436 - void unparseChild(QPDFObjectHandle const& child, size_t level, int flags);  
437 - int openObject(int objid = 0);  
438 - void closeObject(int objid);  
439 - void writeStandard();  
440 - void writeLinearized();  
441 - void writeEncryptionDictionary();  
442 - void writeHeader();  
443 - void writeHintStream(int hint_id);  
444 - qpdf_offset_t writeXRefTable(trailer_e which, int first, int last, int size);  
445 - qpdf_offset_t writeXRefTable(  
446 - trailer_e which,  
447 - int first,  
448 - int last,  
449 - int size,  
450 - // for linearization  
451 - qpdf_offset_t prev,  
452 - bool suppress_offsets,  
453 - int hint_id,  
454 - qpdf_offset_t hint_offset,  
455 - qpdf_offset_t hint_length,  
456 - int linearization_pass);  
457 - qpdf_offset_t writeXRefStream(  
458 - int objid,  
459 - int max_id,  
460 - qpdf_offset_t max_offset,  
461 - trailer_e which,  
462 - int first,  
463 - int last,  
464 - int size);  
465 - qpdf_offset_t writeXRefStream(  
466 - int objid,  
467 - int max_id,  
468 - qpdf_offset_t max_offset,  
469 - trailer_e which,  
470 - int first,  
471 - int last,  
472 - int size,  
473 - // for linearization  
474 - qpdf_offset_t prev,  
475 - int hint_id,  
476 - qpdf_offset_t hint_offset,  
477 - qpdf_offset_t hint_length,  
478 - bool skip_compression,  
479 - int linearization_pass);  
480 -  
481 - void setDataKey(int objid);  
482 - void indicateProgress(bool decrement, bool finished);  
483 - size_t calculateXrefStreamPadding(qpdf_offset_t xref_bytes);  
484 -  
485 - void adjustAESStreamLength(size_t& length);  
486 - void computeDeterministicIDData();  
487 -  
488 - private:  
489 - QPDFWriter& w;  
490 - QPDFObjGen root_og{-1, 0};  
491 - char const* filename{"unspecified"};  
492 - FILE* file{nullptr};  
493 - bool close_file{false};  
494 - std::unique_ptr<Pl_Buffer> buffer_pipeline{nullptr};  
495 - Buffer* output_buffer{nullptr};  
496 - bool normalize_content_set{false};  
497 - bool normalize_content{false};  
498 - bool compress_streams{true};  
499 - bool compress_streams_set{false};  
500 - qpdf_stream_decode_level_e stream_decode_level{qpdf_dl_generalized};  
501 - bool stream_decode_level_set{false};  
502 - bool recompress_flate{false};  
503 - bool qdf_mode{false};  
504 - bool preserve_unreferenced_objects{false};  
505 - bool newline_before_endstream{false};  
506 - bool static_id{false};  
507 - bool suppress_original_object_ids{false};  
508 - bool direct_stream_lengths{true};  
509 - bool preserve_encryption{true};  
510 - bool linearized{false};  
511 - bool pclm{false};  
512 - qpdf_object_stream_e object_stream_mode{qpdf_o_preserve};  
513 -  
514 - std::unique_ptr<QPDF::Doc::Encryption> encryption;  
515 - std::string encryption_key;  
516 - bool encrypt_use_aes{false};  
517 -  
518 - std::string id1; // for /ID key of  
519 - std::string id2; // trailer dictionary  
520 - std::string final_pdf_version;  
521 - int final_extension_level{0};  
522 - std::string min_pdf_version;  
523 - int min_extension_level{0};  
524 - std::string forced_pdf_version;  
525 - int forced_extension_level{0};  
526 - std::string extra_header_text;  
527 - int encryption_dict_objid{0};  
528 - std::string cur_data_key;  
529 - std::unique_ptr<Pipeline> file_pl;  
530 - qpdf::pl::Count* pipeline{nullptr};  
531 - std::vector<QPDFObjectHandle> object_queue;  
532 - size_t object_queue_front{0};  
533 - QPDFWriter::ObjTable obj;  
534 - QPDFWriter::NewObjTable new_obj;  
535 - int next_objid{1};  
536 - int cur_stream_length_id{0};  
537 - size_t cur_stream_length{0};  
538 - bool added_newline{false};  
539 - size_t max_ostream_index{0};  
540 - std::set<QPDFObjGen> normalized_streams;  
541 - std::map<QPDFObjGen, int> page_object_to_seq;  
542 - std::map<QPDFObjGen, int> contents_to_page_seq;  
543 - std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects;  
544 - Pl_stack pipeline_stack;  
545 - bool deterministic_id{false};  
546 - std::string deterministic_id_data;  
547 - bool did_write_setup{false};  
548 -  
549 - // For linearization only  
550 - std::string lin_pass1_filename;  
551 -  
552 - // For progress reporting  
553 - std::shared_ptr<QPDFWriter::ProgressReporter> progress_reporter;  
554 - int events_expected{0};  
555 - int events_seen{0};  
556 - int next_progress_report{0};  
557 }; 483 };
558 484
  485 +qpdf::Writer::Writer(QPDF& qpdf, Config cfg) :
  486 + QPDFWriter(qpdf)
  487 +{
  488 + m->cfg = cfg;
  489 +}
559 QPDFWriter::QPDFWriter(QPDF& pdf) : 490 QPDFWriter::QPDFWriter(QPDF& pdf) :
560 m(std::make_shared<Members>(*this, pdf)) 491 m(std::make_shared<Members>(*this, pdf))
561 { 492 {
@@ -632,75 +563,129 @@ QPDFWriter::setOutputPipeline(Pipeline* p) @@ -632,75 +563,129 @@ QPDFWriter::setOutputPipeline(Pipeline* p)
632 void 563 void
633 QPDFWriter::setObjectStreamMode(qpdf_object_stream_e mode) 564 QPDFWriter::setObjectStreamMode(qpdf_object_stream_e mode)
634 { 565 {
635 - m->object_stream_mode = mode; 566 + m->cfg.object_streams(mode);
636 } 567 }
637 568
638 void 569 void
639 QPDFWriter::setStreamDataMode(qpdf_stream_data_e mode) 570 QPDFWriter::setStreamDataMode(qpdf_stream_data_e mode)
640 { 571 {
  572 + m->cfg.stream_data(mode);
  573 +}
  574 +
  575 +Config&
  576 +Config::stream_data(qpdf_stream_data_e mode)
  577 +{
641 switch (mode) { 578 switch (mode) {
642 case qpdf_s_uncompress: 579 case qpdf_s_uncompress:
643 - m->stream_decode_level = std::max(qpdf_dl_generalized, m->stream_decode_level);  
644 - m->compress_streams = false;  
645 - break; 580 + decode_level(std::max(qpdf_dl_generalized, decode_level_));
  581 + compress_streams(false);
  582 + return *this;
646 583
647 case qpdf_s_preserve: 584 case qpdf_s_preserve:
648 - m->stream_decode_level = qpdf_dl_none;  
649 - m->compress_streams = false;  
650 - break; 585 + decode_level(qpdf_dl_none);
  586 + compress_streams(false);
  587 + return *this;
651 588
652 case qpdf_s_compress: 589 case qpdf_s_compress:
653 - m->stream_decode_level = std::max(qpdf_dl_generalized, m->stream_decode_level);  
654 - m->compress_streams = true;  
655 - break; 590 + decode_level(std::max(qpdf_dl_generalized, decode_level_));
  591 + compress_streams(true);
656 } 592 }
657 - m->stream_decode_level_set = true;  
658 - m->compress_streams_set = true; 593 + return *this;
659 } 594 }
660 595
661 void 596 void
662 QPDFWriter::setCompressStreams(bool val) 597 QPDFWriter::setCompressStreams(bool val)
663 { 598 {
664 - m->compress_streams = val;  
665 - m->compress_streams_set = true; 599 + m->cfg.compress_streams(val);
  600 +}
  601 +
  602 +Config&
  603 +Config::compress_streams(bool val)
  604 +{
  605 + if (pclm_) {
  606 + usage("compress_streams cannot be set when pclm is set");
  607 + return *this;
  608 + }
  609 + compress_streams_set_ = true;
  610 + compress_streams_ = val;
  611 + return *this;
666 } 612 }
667 613
668 void 614 void
669 QPDFWriter::setDecodeLevel(qpdf_stream_decode_level_e val) 615 QPDFWriter::setDecodeLevel(qpdf_stream_decode_level_e val)
670 { 616 {
671 - m->stream_decode_level = val;  
672 - m->stream_decode_level_set = true; 617 + m->cfg.decode_level(val);
  618 +}
  619 +
  620 +Config&
  621 +Config::decode_level(qpdf_stream_decode_level_e val)
  622 +{
  623 + if (pclm_) {
  624 + usage("stream_decode_level cannot be set when pclm is set");
  625 + return *this;
  626 + }
  627 + decode_level_set_ = true;
  628 + decode_level_ = val;
  629 + return *this;
673 } 630 }
674 631
675 void 632 void
676 QPDFWriter::setRecompressFlate(bool val) 633 QPDFWriter::setRecompressFlate(bool val)
677 { 634 {
678 - m->recompress_flate = val; 635 + m->cfg.recompress_flate(val);
679 } 636 }
680 637
681 void 638 void
682 QPDFWriter::setContentNormalization(bool val) 639 QPDFWriter::setContentNormalization(bool val)
683 { 640 {
684 - m->normalize_content_set = true;  
685 - m->normalize_content = val; 641 + m->cfg.normalize_content(val);
686 } 642 }
687 643
688 void 644 void
689 QPDFWriter::setQDFMode(bool val) 645 QPDFWriter::setQDFMode(bool val)
690 { 646 {
691 - m->qdf_mode = val; 647 + m->cfg.qdf(val);
  648 +}
  649 +
  650 +Config&
  651 +Config::qdf(bool val)
  652 +{
  653 + if (pclm_ || linearize_) {
  654 + usage("qdf cannot be set when linearize or pclm are set");
  655 + }
  656 + if (preserve_encryption_) {
  657 + usage("preserve_encryption cannot be set when qdf is set");
  658 + }
  659 + qdf_ = val;
  660 + if (val) {
  661 + if (!normalize_content_set_) {
  662 + normalize_content(true);
  663 + }
  664 + if (!compress_streams_set_) {
  665 + compress_streams(false);
  666 + }
  667 + if (!decode_level_set_) {
  668 + decode_level(qpdf_dl_generalized);
  669 + }
  670 + preserve_encryption_ = false;
  671 + // Generate indirect stream lengths for qdf mode since fix-qdf uses them for storing
  672 + // recomputed stream length data. Certain streams such as object streams, xref streams, and
  673 + // hint streams always get direct stream lengths.
  674 + direct_stream_lengths_ = false;
  675 + }
  676 + return *this;
692 } 677 }
693 678
694 void 679 void
695 QPDFWriter::setPreserveUnreferencedObjects(bool val) 680 QPDFWriter::setPreserveUnreferencedObjects(bool val)
696 { 681 {
697 - m->preserve_unreferenced_objects = val; 682 + m->cfg.preserve_unreferenced(val);
698 } 683 }
699 684
700 void 685 void
701 QPDFWriter::setNewlineBeforeEndstream(bool val) 686 QPDFWriter::setNewlineBeforeEndstream(bool val)
702 { 687 {
703 - m->newline_before_endstream = val; 688 + m->cfg.newline_before_endstream(val);
704 } 689 }
705 690
706 void 691 void
@@ -710,7 +695,7 @@ QPDFWriter::setMinimumPDFVersion(std::string const&amp; version, int extension_level @@ -710,7 +695,7 @@ QPDFWriter::setMinimumPDFVersion(std::string const&amp; version, int extension_level
710 } 695 }
711 696
712 void 697 void
713 -QPDFWriter::Members::setMinimumPDFVersion(std::string const& version, int extension_level) 698 +impl::Writer::setMinimumPDFVersion(std::string const& version, int extension_level)
714 { 699 {
715 bool set_version = false; 700 bool set_version = false;
716 bool set_extension_level = false; 701 bool set_extension_level = false;
@@ -756,31 +741,37 @@ QPDFWriter::setMinimumPDFVersion(PDFVersion const&amp; v) @@ -756,31 +741,37 @@ QPDFWriter::setMinimumPDFVersion(PDFVersion const&amp; v)
756 void 741 void
757 QPDFWriter::forcePDFVersion(std::string const& version, int extension_level) 742 QPDFWriter::forcePDFVersion(std::string const& version, int extension_level)
758 { 743 {
759 - m->forced_pdf_version = version;  
760 - m->forced_extension_level = extension_level; 744 + m->cfg.forced_pdf_version(version, extension_level);
761 } 745 }
762 746
763 void 747 void
764 QPDFWriter::setExtraHeaderText(std::string const& text) 748 QPDFWriter::setExtraHeaderText(std::string const& text)
765 { 749 {
766 - m->extra_header_text = text;  
767 - if (!m->extra_header_text.empty() && *m->extra_header_text.rbegin() != '\n') {  
768 - m->extra_header_text += "\n"; 750 + m->cfg.extra_header_text(text);
  751 +}
  752 +
  753 +Config&
  754 +Config::extra_header_text(std::string const& val)
  755 +{
  756 + extra_header_text_ = val;
  757 + if (!extra_header_text_.empty() && extra_header_text_.back() != '\n') {
  758 + extra_header_text_ += "\n";
769 } else { 759 } else {
770 QTC::TC("qpdf", "QPDFWriter extra header text no newline"); 760 QTC::TC("qpdf", "QPDFWriter extra header text no newline");
771 } 761 }
  762 + return *this;
772 } 763 }
773 764
774 void 765 void
775 QPDFWriter::setStaticID(bool val) 766 QPDFWriter::setStaticID(bool val)
776 { 767 {
777 - m->static_id = val; 768 + m->cfg.static_id(val);
778 } 769 }
779 770
780 void 771 void
781 QPDFWriter::setDeterministicID(bool val) 772 QPDFWriter::setDeterministicID(bool val)
782 { 773 {
783 - m->deterministic_id = val; 774 + m->cfg.deterministic_id(val);
784 } 775 }
785 776
786 void 777 void
@@ -794,37 +785,61 @@ QPDFWriter::setStaticAesIV(bool val) @@ -794,37 +785,61 @@ QPDFWriter::setStaticAesIV(bool val)
794 void 785 void
795 QPDFWriter::setSuppressOriginalObjectIDs(bool val) 786 QPDFWriter::setSuppressOriginalObjectIDs(bool val)
796 { 787 {
797 - m->suppress_original_object_ids = val; 788 + m->cfg.no_original_object_ids(val);
798 } 789 }
799 790
800 void 791 void
801 QPDFWriter::setPreserveEncryption(bool val) 792 QPDFWriter::setPreserveEncryption(bool val)
802 { 793 {
803 - m->preserve_encryption = val; 794 + m->cfg.preserve_encryption(val);
804 } 795 }
805 796
806 void 797 void
807 QPDFWriter::setLinearization(bool val) 798 QPDFWriter::setLinearization(bool val)
808 { 799 {
809 - m->linearized = val;  
810 - if (val) {  
811 - m->pclm = false; 800 + m->cfg.linearize(val);
  801 +}
  802 +
  803 +Config&
  804 +Config::linearize(bool val)
  805 +{
  806 + if (pclm_ || qdf_) {
  807 + usage("linearize cannot be set when qdf or pclm are set");
  808 + return *this;
812 } 809 }
  810 + linearize_ = val;
  811 + return *this;
813 } 812 }
814 813
815 void 814 void
816 QPDFWriter::setLinearizationPass1Filename(std::string const& filename) 815 QPDFWriter::setLinearizationPass1Filename(std::string const& filename)
817 { 816 {
818 - m->lin_pass1_filename = filename; 817 + m->cfg.linearize_pass1(filename);
819 } 818 }
820 819
821 void 820 void
822 QPDFWriter::setPCLm(bool val) 821 QPDFWriter::setPCLm(bool val)
823 { 822 {
824 - m->pclm = val; 823 + m->cfg.pclm(val);
  824 +}
  825 +
  826 +Config&
  827 +Config::pclm(bool val)
  828 +{
  829 + if (decode_level_set_ || compress_streams_set_ || linearize_) {
  830 + usage(
  831 + "pclm cannot be set when stream_decode_level, compress_streams, linearize or qdf are "
  832 + "set");
  833 + return *this;
  834 + }
  835 + pclm_ = val;
825 if (val) { 836 if (val) {
826 - m->linearized = false; 837 + decode_level_ = qpdf_dl_none;
  838 + compress_streams_ = false;
  839 + linearize_ = false;
827 } 840 }
  841 +
  842 + return *this;
828 } 843 }
829 844
830 void 845 void
@@ -892,7 +907,7 @@ QPDFWriter::setR4EncryptionParametersInsecure( @@ -892,7 +907,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
892 bool use_aes) 907 bool use_aes)
893 { 908 {
894 m->encryption = std::make_unique<Encryption>(4, 4, 16, encrypt_metadata); 909 m->encryption = std::make_unique<Encryption>(4, 4, 16, encrypt_metadata);
895 - m->encrypt_use_aes = use_aes; 910 + m->cfg.encrypt_use_aes(use_aes);
896 m->interpretR3EncryptionParameters( 911 m->interpretR3EncryptionParameters(
897 allow_accessibility, 912 allow_accessibility,
898 allow_extract, 913 allow_extract,
@@ -919,7 +934,7 @@ QPDFWriter::setR5EncryptionParameters( @@ -919,7 +934,7 @@ QPDFWriter::setR5EncryptionParameters(
919 bool encrypt_metadata) 934 bool encrypt_metadata)
920 { 935 {
921 m->encryption = std::make_unique<Encryption>(5, 5, 32, encrypt_metadata); 936 m->encryption = std::make_unique<Encryption>(5, 5, 32, encrypt_metadata);
922 - m->encrypt_use_aes = true; 937 + m->cfg.encrypt_use_aes(true);
923 m->interpretR3EncryptionParameters( 938 m->interpretR3EncryptionParameters(
924 allow_accessibility, 939 allow_accessibility,
925 allow_extract, 940 allow_extract,
@@ -955,12 +970,12 @@ QPDFWriter::setR6EncryptionParameters( @@ -955,12 +970,12 @@ QPDFWriter::setR6EncryptionParameters(
955 allow_modify_other, 970 allow_modify_other,
956 print, 971 print,
957 qpdf_r3m_all); 972 qpdf_r3m_all);
958 - m->encrypt_use_aes = true; 973 + m->cfg.encrypt_use_aes(true);
959 m->setEncryptionParameters(user_password, owner_password); 974 m->setEncryptionParameters(user_password, owner_password);
960 } 975 }
961 976
962 void 977 void
963 -QPDFWriter::Members::interpretR3EncryptionParameters( 978 +impl::Writer::interpretR3EncryptionParameters(
964 bool allow_accessibility, 979 bool allow_accessibility,
965 bool allow_extract, 980 bool allow_extract,
966 bool allow_assemble, 981 bool allow_assemble,
@@ -1059,7 +1074,7 @@ QPDFWriter::Members::interpretR3EncryptionParameters( @@ -1059,7 +1074,7 @@ QPDFWriter::Members::interpretR3EncryptionParameters(
1059 } 1074 }
1060 1075
1061 void 1076 void
1062 -QPDFWriter::Members::setEncryptionParameters(char const* user_password, char const* owner_password) 1077 +impl::Writer::setEncryptionParameters(char const* user_password, char const* owner_password)
1063 { 1078 {
1064 generateID(true); 1079 generateID(true);
1065 encryption->setId1(id1); 1080 encryption->setId1(id1);
@@ -1074,9 +1089,9 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf) @@ -1074,9 +1089,9 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
1074 } 1089 }
1075 1090
1076 void 1091 void
1077 -QPDFWriter::Members::copyEncryptionParameters(QPDF& qpdf) 1092 +impl::Writer::copyEncryptionParameters(QPDF& qpdf)
1078 { 1093 {
1079 - preserve_encryption = false; 1094 + cfg.preserve_encryption(false);
1080 QPDFObjectHandle trailer = qpdf.getTrailer(); 1095 QPDFObjectHandle trailer = qpdf.getTrailer();
1081 if (trailer.hasKey("/Encrypt")) { 1096 if (trailer.hasKey("/Encrypt")) {
1082 generateID(true); 1097 generateID(true);
@@ -1096,10 +1111,10 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf) @@ -1096,10 +1111,10 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf)
1096 // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of 1111 // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of
1097 // figuring out whether AES is used or not is complicated with /StmF, /StrF, and /EFF 1112 // figuring out whether AES is used or not is complicated with /StmF, /StrF, and /EFF
1098 // all potentially having different values. 1113 // all potentially having different values.
1099 - encrypt_use_aes = true; 1114 + cfg.encrypt_use_aes(true);
1100 } 1115 }
1101 QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1); 1116 QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1);
1102 - QTC::TC("qpdf", "QPDFWriter copy use_aes", encrypt_use_aes ? 0 : 1); 1117 + QTC::TC("qpdf", "QPDFWriter copy use_aes", cfg.encrypt_use_aes() ? 0 : 1);
1103 1118
1104 encryption = std::make_unique<Encryption>( 1119 encryption = std::make_unique<Encryption>(
1105 V, 1120 V,
@@ -1120,7 +1135,7 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf) @@ -1120,7 +1135,7 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf)
1120 } 1135 }
1121 1136
1122 void 1137 void
1123 -QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int extension_level) 1138 +impl::Writer::disableIncompatibleEncryption(int major, int minor, int extension_level)
1124 { 1139 {
1125 if (!encryption) { 1140 if (!encryption) {
1126 return; 1141 return;
@@ -1140,7 +1155,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext @@ -1140,7 +1155,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext
1140 encryption = nullptr; 1155 encryption = nullptr;
1141 } 1156 }
1142 } else if (compareVersions(major, minor, 1, 6) < 0) { 1157 } else if (compareVersions(major, minor, 1, 6) < 0) {
1143 - if (encrypt_use_aes) { 1158 + if (cfg.encrypt_use_aes()) {
1144 encryption = nullptr; 1159 encryption = nullptr;
1145 } 1160 }
1146 } else if ( 1161 } else if (
@@ -1157,7 +1172,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext @@ -1157,7 +1172,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext
1157 } 1172 }
1158 1173
1159 void 1174 void
1160 -QPDFWriter::Members::parseVersion(std::string const& version, int& major, int& minor) const 1175 +impl::Writer::parseVersion(std::string const& version, int& major, int& minor) const
1161 { 1176 {
1162 major = QUtil::string_to_int(version.c_str()); 1177 major = QUtil::string_to_int(version.c_str());
1163 minor = 0; 1178 minor = 0;
@@ -1174,7 +1189,7 @@ QPDFWriter::Members::parseVersion(std::string const&amp; version, int&amp; major, int&amp; m @@ -1174,7 +1189,7 @@ QPDFWriter::Members::parseVersion(std::string const&amp; version, int&amp; major, int&amp; m
1174 } 1189 }
1175 1190
1176 int 1191 int
1177 -QPDFWriter::Members::compareVersions(int major1, int minor1, int major2, int minor2) const 1192 +impl::Writer::compareVersions(int major1, int minor1, int major2, int minor2) const
1178 { 1193 {
1179 if (major1 < major2) { 1194 if (major1 < major2) {
1180 return -1; 1195 return -1;
@@ -1189,33 +1204,38 @@ QPDFWriter::Members::compareVersions(int major1, int minor1, int major2, int min @@ -1189,33 +1204,38 @@ QPDFWriter::Members::compareVersions(int major1, int minor1, int major2, int min
1189 } 1204 }
1190 1205
1191 void 1206 void
1192 -QPDFWriter::Members::setEncryptionMinimumVersion() 1207 +impl::Writer::setEncryptionMinimumVersion()
1193 { 1208 {
1194 auto const R = encryption->getR(); 1209 auto const R = encryption->getR();
1195 if (R >= 6) { 1210 if (R >= 6) {
1196 - w.setMinimumPDFVersion("1.7", 8); 1211 + setMinimumPDFVersion("1.7", 8);
1197 } else if (R == 5) { 1212 } else if (R == 5) {
1198 - w.setMinimumPDFVersion("1.7", 3); 1213 + setMinimumPDFVersion("1.7", 3);
1199 } else if (R == 4) { 1214 } else if (R == 4) {
1200 - w.setMinimumPDFVersion(encrypt_use_aes ? "1.6" : "1.5"); 1215 + setMinimumPDFVersion(cfg.encrypt_use_aes() ? "1.6" : "1.5");
1201 } else if (R == 3) { 1216 } else if (R == 3) {
1202 - w.setMinimumPDFVersion("1.4"); 1217 + setMinimumPDFVersion("1.4");
1203 } else { 1218 } else {
1204 - w.setMinimumPDFVersion("1.3"); 1219 + setMinimumPDFVersion("1.3");
1205 } 1220 }
1206 } 1221 }
1207 1222
1208 void 1223 void
1209 -QPDFWriter::Members::setDataKey(int objid) 1224 +impl::Writer::setDataKey(int objid)
1210 { 1225 {
1211 if (encryption) { 1226 if (encryption) {
1212 cur_data_key = QPDF::compute_data_key( 1227 cur_data_key = QPDF::compute_data_key(
1213 - encryption_key, objid, 0, encrypt_use_aes, encryption->getV(), encryption->getR()); 1228 + encryption_key,
  1229 + objid,
  1230 + 0,
  1231 + cfg.encrypt_use_aes(),
  1232 + encryption->getV(),
  1233 + encryption->getR());
1214 } 1234 }
1215 } 1235 }
1216 1236
1217 unsigned int 1237 unsigned int
1218 -QPDFWriter::Members::bytesNeeded(long long n) 1238 +impl::Writer::bytesNeeded(long long n)
1219 { 1239 {
1220 unsigned int bytes = 0; 1240 unsigned int bytes = 0;
1221 while (n) { 1241 while (n) {
@@ -1226,7 +1246,7 @@ QPDFWriter::Members::bytesNeeded(long long n) @@ -1226,7 +1246,7 @@ QPDFWriter::Members::bytesNeeded(long long n)
1226 } 1246 }
1227 1247
1228 void 1248 void
1229 -QPDFWriter::Members::writeBinary(unsigned long long val, unsigned int bytes) 1249 +impl::Writer::writeBinary(unsigned long long val, unsigned int bytes)
1230 { 1250 {
1231 if (bytes > sizeof(unsigned long long)) { 1251 if (bytes > sizeof(unsigned long long)) {
1232 throw std::logic_error("QPDFWriter::writeBinary called with too many bytes"); 1252 throw std::logic_error("QPDFWriter::writeBinary called with too many bytes");
@@ -1239,77 +1259,77 @@ QPDFWriter::Members::writeBinary(unsigned long long val, unsigned int bytes) @@ -1239,77 +1259,77 @@ QPDFWriter::Members::writeBinary(unsigned long long val, unsigned int bytes)
1239 pipeline->write(data, bytes); 1259 pipeline->write(data, bytes);
1240 } 1260 }
1241 1261
1242 -QPDFWriter::Members&  
1243 -QPDFWriter::Members::write(std::string_view str) 1262 +impl::Writer&
  1263 +impl::Writer::write(std::string_view str)
1244 { 1264 {
1245 pipeline->write(str); 1265 pipeline->write(str);
1246 return *this; 1266 return *this;
1247 } 1267 }
1248 1268
1249 -QPDFWriter::Members&  
1250 -QPDFWriter::Members::write(std::integral auto val) 1269 +impl::Writer&
  1270 +impl::Writer::write(std::integral auto val)
1251 { 1271 {
1252 pipeline->write(std::to_string(val)); 1272 pipeline->write(std::to_string(val));
1253 return *this; 1273 return *this;
1254 } 1274 }
1255 1275
1256 -QPDFWriter::Members&  
1257 -QPDFWriter::Members::write(size_t count, char c) 1276 +impl::Writer&
  1277 +impl::Writer::write(size_t count, char c)
1258 { 1278 {
1259 pipeline->write(count, c); 1279 pipeline->write(count, c);
1260 return *this; 1280 return *this;
1261 } 1281 }
1262 1282
1263 -QPDFWriter::Members&  
1264 -QPDFWriter::Members::write_name(std::string const& str) 1283 +impl::Writer&
  1284 +impl::Writer::write_name(std::string const& str)
1265 { 1285 {
1266 pipeline->write(Name::normalize(str)); 1286 pipeline->write(Name::normalize(str));
1267 return *this; 1287 return *this;
1268 } 1288 }
1269 1289
1270 -QPDFWriter::Members&  
1271 -QPDFWriter::Members::write_string(std::string const& str, bool force_binary) 1290 +impl::Writer&
  1291 +impl::Writer::write_string(std::string const& str, bool force_binary)
1272 { 1292 {
1273 pipeline->write(QPDF_String(str).unparse(force_binary)); 1293 pipeline->write(QPDF_String(str).unparse(force_binary));
1274 return *this; 1294 return *this;
1275 } 1295 }
1276 1296
1277 template <typename... Args> 1297 template <typename... Args>
1278 -QPDFWriter::Members&  
1279 -QPDFWriter::Members::write_qdf(Args&&... args) 1298 +impl::Writer&
  1299 +impl::Writer::write_qdf(Args&&... args)
1280 { 1300 {
1281 - if (qdf_mode) { 1301 + if (cfg.qdf()) {
1282 pipeline->write(std::forward<Args>(args)...); 1302 pipeline->write(std::forward<Args>(args)...);
1283 } 1303 }
1284 return *this; 1304 return *this;
1285 } 1305 }
1286 1306
1287 template <typename... Args> 1307 template <typename... Args>
1288 -QPDFWriter::Members&  
1289 -QPDFWriter::Members::write_no_qdf(Args&&... args) 1308 +impl::Writer&
  1309 +impl::Writer::write_no_qdf(Args&&... args)
1290 { 1310 {
1291 - if (!qdf_mode) { 1311 + if (!cfg.qdf()) {
1292 pipeline->write(std::forward<Args>(args)...); 1312 pipeline->write(std::forward<Args>(args)...);
1293 } 1313 }
1294 return *this; 1314 return *this;
1295 } 1315 }
1296 1316
1297 void 1317 void
1298 -QPDFWriter::Members::adjustAESStreamLength(size_t& length) 1318 +impl::Writer::adjustAESStreamLength(size_t& length)
1299 { 1319 {
1300 - if (encryption && !cur_data_key.empty() && encrypt_use_aes) { 1320 + if (encryption && !cur_data_key.empty() && cfg.encrypt_use_aes()) {
1301 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will 1321 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will
1302 // also be prepended by 16 bits of random data. 1322 // also be prepended by 16 bits of random data.
1303 length += 32 - (length & 0xf); 1323 length += 32 - (length & 0xf);
1304 } 1324 }
1305 } 1325 }
1306 1326
1307 -QPDFWriter::Members&  
1308 -QPDFWriter::Members::write_encrypted(std::string_view str) 1327 +impl::Writer&
  1328 +impl::Writer::write_encrypted(std::string_view str)
1309 { 1329 {
1310 if (!(encryption && !cur_data_key.empty())) { 1330 if (!(encryption && !cur_data_key.empty())) {
1311 write(str); 1331 write(str);
1312 - } else if (encrypt_use_aes) { 1332 + } else if (cfg.encrypt_use_aes()) {
1313 write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key)); 1333 write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key));
1314 } else { 1334 } else {
1315 write(pl::pipe<Pl_RC4>(str, cur_data_key)); 1335 write(pl::pipe<Pl_RC4>(str, cur_data_key));
@@ -1319,7 +1339,7 @@ QPDFWriter::Members::write_encrypted(std::string_view str) @@ -1319,7 +1339,7 @@ QPDFWriter::Members::write_encrypted(std::string_view str)
1319 } 1339 }
1320 1340
1321 void 1341 void
1322 -QPDFWriter::Members::computeDeterministicIDData() 1342 +impl::Writer::computeDeterministicIDData()
1323 { 1343 {
1324 if (!id2.empty()) { 1344 if (!id2.empty()) {
1325 // Can't happen in the code 1345 // Can't happen in the code
@@ -1331,7 +1351,7 @@ QPDFWriter::Members::computeDeterministicIDData() @@ -1331,7 +1351,7 @@ QPDFWriter::Members::computeDeterministicIDData()
1331 } 1351 }
1332 1352
1333 int 1353 int
1334 -QPDFWriter::Members::openObject(int objid) 1354 +impl::Writer::openObject(int objid)
1335 { 1355 {
1336 if (objid == 0) { 1356 if (objid == 0) {
1337 objid = next_objid++; 1357 objid = next_objid++;
@@ -1342,7 +1362,7 @@ QPDFWriter::Members::openObject(int objid) @@ -1342,7 +1362,7 @@ QPDFWriter::Members::openObject(int objid)
1342 } 1362 }
1343 1363
1344 void 1364 void
1345 -QPDFWriter::Members::closeObject(int objid) 1365 +impl::Writer::closeObject(int objid)
1346 { 1366 {
1347 // Write a newline before endobj as it makes the file easier to repair. 1367 // Write a newline before endobj as it makes the file easier to repair.
1348 write("\nendobj\n").write_qdf("\n"); 1368 write("\nendobj\n").write_qdf("\n");
@@ -1351,7 +1371,7 @@ QPDFWriter::Members::closeObject(int objid) @@ -1351,7 +1371,7 @@ QPDFWriter::Members::closeObject(int objid)
1351 } 1371 }
1352 1372
1353 void 1373 void
1354 -QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og) 1374 +impl::Writer::assignCompressedObjectNumbers(QPDFObjGen og)
1355 { 1375 {
1356 int objid = og.getObj(); 1376 int objid = og.getObj();
1357 if (og.getGen() != 0 || !object_stream_to_objects.contains(objid)) { 1377 if (og.getGen() != 0 || !object_stream_to_objects.contains(objid)) {
@@ -1366,7 +1386,7 @@ QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og) @@ -1366,7 +1386,7 @@ QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og)
1366 } 1386 }
1367 1387
1368 void 1388 void
1369 -QPDFWriter::Members::enqueue(QPDFObjectHandle const& object) 1389 +impl::Writer::enqueue(QPDFObjectHandle const& object)
1370 { 1390 {
1371 if (object.indirect()) { 1391 if (object.indirect()) {
1372 util::assertion( 1392 util::assertion(
@@ -1380,7 +1400,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object) @@ -1380,7 +1400,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1380 "Use QPDF::copyForeignObject to add objects from another file." // 1400 "Use QPDF::copyForeignObject to add objects from another file." //
1381 ); 1401 );
1382 1402
1383 - if (qdf_mode && object.isStreamOfType("/XRef")) { 1403 + if (cfg.qdf() && object.isStreamOfType("/XRef")) {
1384 // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so 1404 // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so
1385 // will confuse fix-qdf, which expects to see only one XRef stream at the end of the 1405 // will confuse fix-qdf, which expects to see only one XRef stream at the end of the
1386 // file. This case can occur when creating a QDF from a file with object streams when 1406 // file. This case can occur when creating a QDF from a file with object streams when
@@ -1406,10 +1426,10 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object) @@ -1406,10 +1426,10 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1406 if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) { 1426 if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) {
1407 // For linearized files, uncompressed objects go at end, and we take care of 1427 // For linearized files, uncompressed objects go at end, and we take care of
1408 // assigning numbers to them elsewhere. 1428 // assigning numbers to them elsewhere.
1409 - if (!linearized) { 1429 + if (!cfg.linearize()) {
1410 assignCompressedObjectNumbers(og); 1430 assignCompressedObjectNumbers(og);
1411 } 1431 }
1412 - } else if (!direct_stream_lengths && object.isStream()) { 1432 + } else if (!cfg.direct_stream_lengths() && object.isStream()) {
1413 // reserve next object ID for length 1433 // reserve next object ID for length
1414 ++next_objid; 1434 ++next_objid;
1415 } 1435 }
@@ -1418,7 +1438,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object) @@ -1418,7 +1438,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1418 return; 1438 return;
1419 } 1439 }
1420 1440
1421 - if (linearized) { 1441 + if (cfg.linearize()) {
1422 return; 1442 return;
1423 } 1443 }
1424 1444
@@ -1437,9 +1457,9 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object) @@ -1437,9 +1457,9 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const&amp; object)
1437 } 1457 }
1438 1458
1439 void 1459 void
1440 -QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags) 1460 +impl::Writer::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
1441 { 1461 {
1442 - if (!linearized) { 1462 + if (!cfg.linearize()) {
1443 enqueue(child); 1463 enqueue(child);
1444 } 1464 }
1445 if (child.indirect()) { 1465 if (child.indirect()) {
@@ -1450,7 +1470,7 @@ QPDFWriter::Members::unparseChild(QPDFObjectHandle const&amp; child, size_t level, i @@ -1450,7 +1470,7 @@ QPDFWriter::Members::unparseChild(QPDFObjectHandle const&amp; child, size_t level, i
1450 } 1470 }
1451 1471
1452 void 1472 void
1453 -QPDFWriter::Members::writeTrailer( 1473 +impl::Writer::writeTrailer(
1454 trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass) 1474 trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass)
1455 { 1475 {
1456 auto trailer = trimmed_trailer(); 1476 auto trailer = trimmed_trailer();
@@ -1498,7 +1518,7 @@ QPDFWriter::Members::writeTrailer( @@ -1498,7 +1518,7 @@ QPDFWriter::Members::writeTrailer(
1498 } 1518 }
1499 write("<00000000000000000000000000000000>"); 1519 write("<00000000000000000000000000000000>");
1500 } else { 1520 } else {
1501 - if (linearization_pass == 0 && deterministic_id) { 1521 + if (linearization_pass == 0 && cfg.deterministic_id()) {
1502 computeDeterministicIDData(); 1522 computeDeterministicIDData();
1503 } 1523 }
1504 generateID(encryption.get()); 1524 generateID(encryption.get());
@@ -1517,7 +1537,7 @@ QPDFWriter::Members::writeTrailer( @@ -1517,7 +1537,7 @@ QPDFWriter::Members::writeTrailer(
1517 } 1537 }
1518 1538
1519 bool 1539 bool
1520 -QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream) 1540 +impl::Writer::will_filter_stream(QPDFObjectHandle stream)
1521 { 1541 {
1522 std::string s; 1542 std::string s;
1523 [[maybe_unused]] auto [filter, ignore1, ignore2] = will_filter_stream(stream, &s); 1543 [[maybe_unused]] auto [filter, ignore1, ignore2] = will_filter_stream(stream, &s);
@@ -1525,23 +1545,23 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream) @@ -1525,23 +1545,23 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream)
1525 } 1545 }
1526 1546
1527 std::tuple<const bool, const bool, const bool> 1547 std::tuple<const bool, const bool, const bool>
1528 -QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* stream_data) 1548 +impl::Writer::will_filter_stream(QPDFObjectHandle stream, std::string* stream_data)
1529 { 1549 {
1530 const bool is_root_metadata = stream.isRootMetadata(); 1550 const bool is_root_metadata = stream.isRootMetadata();
1531 bool filter = false; 1551 bool filter = false;
1532 - auto decode_level = stream_decode_level; 1552 + auto decode_level = cfg.decode_level();
1533 int encode_flags = 0; 1553 int encode_flags = 0;
1534 Dictionary stream_dict = stream.getDict(); 1554 Dictionary stream_dict = stream.getDict();
1535 1555
1536 if (stream.getFilterOnWrite()) { 1556 if (stream.getFilterOnWrite()) {
1537 - filter = stream.isDataModified() || compress_streams || decode_level != qpdf_dl_none;  
1538 - if (compress_streams) { 1557 + filter = stream.isDataModified() || cfg.compress_streams() || decode_level != qpdf_dl_none;
  1558 + if (cfg.compress_streams()) {
1539 // Don't filter if the stream is already compressed with FlateDecode. This way we don't 1559 // Don't filter if the stream is already compressed with FlateDecode. This way we don't
1540 // make it worse if the original file used a better Flate algorithm, and we don't spend 1560 // make it worse if the original file used a better Flate algorithm, and we don't spend
1541 // time and CPU cycles uncompressing and recompressing stuff. This can be overridden 1561 // time and CPU cycles uncompressing and recompressing stuff. This can be overridden
1542 // with setRecompressFlate(true). 1562 // with setRecompressFlate(true).
1543 Name Filter = stream_dict["/Filter"]; 1563 Name Filter = stream_dict["/Filter"];
1544 - if (Filter && !recompress_flate && !stream.isDataModified() && 1564 + if (Filter && !cfg.recompress_flate() && !stream.isDataModified() &&
1545 (Filter == "/FlateDecode" || Filter == "/Fl")) { 1565 (Filter == "/FlateDecode" || Filter == "/Fl")) {
1546 filter = false; 1566 filter = false;
1547 } 1567 }
@@ -1549,10 +1569,10 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st @@ -1549,10 +1569,10 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st
1549 if (is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) { 1569 if (is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) {
1550 filter = true; 1570 filter = true;
1551 decode_level = qpdf_dl_all; 1571 decode_level = qpdf_dl_all;
1552 - } else if (normalize_content && normalized_streams.contains(stream)) { 1572 + } else if (cfg.normalize_content() && normalized_streams.contains(stream)) {
1553 encode_flags = qpdf_ef_normalize; 1573 encode_flags = qpdf_ef_normalize;
1554 filter = true; 1574 filter = true;
1555 - } else if (filter && compress_streams) { 1575 + } else if (filter && cfg.compress_streams()) {
1556 encode_flags = qpdf_ef_compress; 1576 encode_flags = qpdf_ef_compress;
1557 } 1577 }
1558 } 1578 }
@@ -1598,7 +1618,7 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st @@ -1598,7 +1618,7 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st
1598 } 1618 }
1599 1619
1600 void 1620 void
1601 -QPDFWriter::Members::unparseObject( 1621 +impl::Writer::unparseObject(
1602 QPDFObjectHandle object, size_t level, int flags, size_t stream_length, bool compress) 1622 QPDFObjectHandle object, size_t level, int flags, size_t stream_length, bool compress)
1603 { 1623 {
1604 QPDFObjGen old_og = object.getObjGen(); 1624 QPDFObjGen old_og = object.getObjGen();
@@ -1606,11 +1626,11 @@ QPDFWriter::Members::unparseObject( @@ -1606,11 +1626,11 @@ QPDFWriter::Members::unparseObject(
1606 // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they 1626 // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they
1607 // include the preceding newline. 1627 // include the preceding newline.
1608 std::string indent_large = " "; 1628 std::string indent_large = " ";
1609 - if (qdf_mode) { 1629 + if (cfg.qdf()) {
1610 indent_large.append(2 * (level + 1), ' '); 1630 indent_large.append(2 * (level + 1), ' ');
1611 indent_large[0] = '\n'; 1631 indent_large[0] = '\n';
1612 } 1632 }
1613 - std::string_view indent{indent_large.data(), qdf_mode ? indent_large.size() - 2 : 1}; 1633 + std::string_view indent{indent_large.data(), cfg.qdf() ? indent_large.size() - 2 : 1};
1614 1634
1615 if (auto const tc = object.getTypeCode(); tc == ::ot_array) { 1635 if (auto const tc = object.getTypeCode(); tc == ::ot_array) {
1616 // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the 1636 // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the
@@ -1666,7 +1686,7 @@ QPDFWriter::Members::unparseObject( @@ -1666,7 +1686,7 @@ QPDFWriter::Members::unparseObject(
1666 if (need_extensions_adbe) { 1686 if (need_extensions_adbe) {
1667 if (!(have_extensions_other || have_extensions_adbe)) { 1687 if (!(have_extensions_other || have_extensions_adbe)) {
1668 // We need Extensions and don't have it. Create it here. 1688 // We need Extensions and don't have it. Create it here.
1669 - QTC::TC("qpdf", "QPDFWriter create Extensions", qdf_mode ? 0 : 1); 1689 + QTC::TC("qpdf", "QPDFWriter create Extensions", cfg.qdf() ? 0 : 1);
1670 extensions = object.replaceKeyAndGetNew( 1690 extensions = object.replaceKeyAndGetNew(
1671 "/Extensions", QPDFObjectHandle::newDictionary()); 1691 "/Extensions", QPDFObjectHandle::newDictionary());
1672 } 1692 }
@@ -1771,7 +1791,7 @@ QPDFWriter::Members::unparseObject( @@ -1771,7 +1791,7 @@ QPDFWriter::Members::unparseObject(
1771 if (flags & f_stream) { 1791 if (flags & f_stream) {
1772 write(indent_large).write("/Length "); 1792 write(indent_large).write("/Length ");
1773 1793
1774 - if (direct_stream_lengths) { 1794 + if (cfg.direct_stream_lengths()) {
1775 write(stream_length); 1795 write(stream_length);
1776 } else { 1796 } else {
1777 write(cur_stream_length_id).write(" 0 R"); 1797 write(cur_stream_length_id).write(" 0 R");
@@ -1784,7 +1804,7 @@ QPDFWriter::Members::unparseObject( @@ -1784,7 +1804,7 @@ QPDFWriter::Members::unparseObject(
1784 write(indent).write(">>"); 1804 write(indent).write(">>");
1785 } else if (tc == ::ot_stream) { 1805 } else if (tc == ::ot_stream) {
1786 // Write stream data to a buffer. 1806 // Write stream data to a buffer.
1787 - if (!direct_stream_lengths) { 1807 + if (!cfg.direct_stream_lengths()) {
1788 cur_stream_length_id = obj[old_og].renumber + 1; 1808 cur_stream_length_id = obj[old_og].renumber + 1;
1789 } 1809 }
1790 1810
@@ -1805,14 +1825,14 @@ QPDFWriter::Members::unparseObject( @@ -1805,14 +1825,14 @@ QPDFWriter::Members::unparseObject(
1805 unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream); 1825 unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream);
1806 char last_char = stream_data.empty() ? '\0' : stream_data.back(); 1826 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1807 write("\nstream\n").write_encrypted(stream_data); 1827 write("\nstream\n").write_encrypted(stream_data);
1808 - added_newline = newline_before_endstream || (qdf_mode && last_char != '\n'); 1828 + added_newline = cfg.newline_before_endstream() || (cfg.qdf() && last_char != '\n');
1809 write(added_newline ? "\nendstream" : "endstream"); 1829 write(added_newline ? "\nendstream" : "endstream");
1810 } else if (tc == ::ot_string) { 1830 } else if (tc == ::ot_string) {
1811 std::string val; 1831 std::string val;
1812 if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) && 1832 if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
1813 !cur_data_key.empty()) { 1833 !cur_data_key.empty()) {
1814 val = object.getStringValue(); 1834 val = object.getStringValue();
1815 - if (encrypt_use_aes) { 1835 + if (cfg.encrypt_use_aes()) {
1816 Pl_Buffer bufpl("encrypted string"); 1836 Pl_Buffer bufpl("encrypted string");
1817 Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key); 1837 Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key);
1818 pl.writeString(val); 1838 pl.writeString(val);
@@ -1841,7 +1861,7 @@ QPDFWriter::Members::unparseObject( @@ -1841,7 +1861,7 @@ QPDFWriter::Members::unparseObject(
1841 } 1861 }
1842 1862
1843 void 1863 void
1844 -QPDFWriter::Members::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj) 1864 +impl::Writer::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj)
1845 { 1865 {
1846 qpdf_assert_debug(first_obj > 0); 1866 qpdf_assert_debug(first_obj > 0);
1847 bool is_first = true; 1867 bool is_first = true;
@@ -1860,7 +1880,7 @@ QPDFWriter::Members::writeObjectStreamOffsets(std::vector&lt;qpdf_offset_t&gt;&amp; offset @@ -1860,7 +1880,7 @@ QPDFWriter::Members::writeObjectStreamOffsets(std::vector&lt;qpdf_offset_t&gt;&amp; offset
1860 } 1880 }
1861 1881
1862 void 1882 void
1863 -QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) 1883 +impl::Writer::writeObjectStream(QPDFObjectHandle object)
1864 { 1884 {
1865 // Note: object might be null if this is a place-holder for an object stream that we are 1885 // Note: object might be null if this is a place-holder for an object stream that we are
1866 // generating from scratch. 1886 // generating from scratch.
@@ -1878,7 +1898,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) @@ -1878,7 +1898,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1878 std::string stream_buffer_pass1; 1898 std::string stream_buffer_pass1;
1879 std::string stream_buffer_pass2; 1899 std::string stream_buffer_pass2;
1880 int first_obj = -1; 1900 int first_obj = -1;
1881 - const bool compressed = compress_streams && !qdf_mode; 1901 + const bool compressed = cfg.compress_streams() && !cfg.qdf();
1882 { 1902 {
1883 // Pass 1 1903 // Pass 1
1884 auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1); 1904 auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1);
@@ -1890,9 +1910,9 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) @@ -1890,9 +1910,9 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1890 if (first_obj == -1) { 1910 if (first_obj == -1) {
1891 first_obj = new_o; 1911 first_obj = new_o;
1892 } 1912 }
1893 - if (qdf_mode) { 1913 + if (cfg.qdf()) {
1894 write("%% Object stream: object ").write(new_o).write(", index ").write(count); 1914 write("%% Object stream: object ").write(new_o).write(", index ").write(count);
1895 - if (!suppress_original_object_ids) { 1915 + if (!cfg.no_original_object_ids()) {
1896 write("; original object ID: ").write(og.getObj()); 1916 write("; original object ID: ").write(og.getObj());
1897 // For compatibility, only write the generation if non-zero. While object 1917 // For compatibility, only write the generation if non-zero. While object
1898 // streams only allow objects with generation 0, if we are generating object 1918 // streams only allow objects with generation 0, if we are generating object
@@ -1968,16 +1988,15 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) @@ -1968,16 +1988,15 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1968 } 1988 }
1969 } 1989 }
1970 write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n").write_encrypted(stream_buffer_pass2); 1990 write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n").write_encrypted(stream_buffer_pass2);
  1991 + write(cfg.newline_before_endstream() ? "\nendstream" : "endstream");
1971 if (encryption) { 1992 if (encryption) {
1972 - QTC::TC("qpdf", "QPDFWriter encrypt object stream"); 1993 + cur_data_key.clear();
1973 } 1994 }
1974 - write(newline_before_endstream ? "\nendstream" : "endstream");  
1975 - cur_data_key.clear();  
1976 closeObject(new_stream_id); 1995 closeObject(new_stream_id);
1977 } 1996 }
1978 1997
1979 void 1998 void
1980 -QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_index) 1999 +impl::Writer::writeObject(QPDFObjectHandle object, int object_stream_index)
1981 { 2000 {
1982 QPDFObjGen old_og = object.getObjGen(); 2001 QPDFObjGen old_og = object.getObjGen();
1983 2002
@@ -1989,7 +2008,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde @@ -1989,7 +2008,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
1989 2008
1990 indicateProgress(false, false); 2009 indicateProgress(false, false);
1991 auto new_id = obj[old_og].renumber; 2010 auto new_id = obj[old_og].renumber;
1992 - if (qdf_mode) { 2011 + if (cfg.qdf()) {
1993 if (page_object_to_seq.contains(old_og)) { 2012 if (page_object_to_seq.contains(old_og)) {
1994 write("%% Page ").write(page_object_to_seq[old_og]).write("\n"); 2013 write("%% Page ").write(page_object_to_seq[old_og]).write("\n");
1995 } 2014 }
@@ -1998,7 +2017,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde @@ -1998,7 +2017,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
1998 } 2017 }
1999 } 2018 }
2000 if (object_stream_index == -1) { 2019 if (object_stream_index == -1) {
2001 - if (qdf_mode && !suppress_original_object_ids) { 2020 + if (cfg.qdf() && !cfg.no_original_object_ids()) {
2002 write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n"); 2021 write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n");
2003 } 2022 }
2004 openObject(new_id); 2023 openObject(new_id);
@@ -2011,8 +2030,8 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde @@ -2011,8 +2030,8 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
2011 write("\n"); 2030 write("\n");
2012 } 2031 }
2013 2032
2014 - if (!direct_stream_lengths && object.isStream()) {  
2015 - if (qdf_mode) { 2033 + if (!cfg.direct_stream_lengths() && object.isStream()) {
  2034 + if (cfg.qdf()) {
2016 if (added_newline) { 2035 if (added_newline) {
2017 write("%QDF: ignore_newline\n"); 2036 write("%QDF: ignore_newline\n");
2018 } 2037 }
@@ -2024,7 +2043,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde @@ -2024,7 +2043,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde
2024 } 2043 }
2025 2044
2026 std::string 2045 std::string
2027 -QPDFWriter::Members::getOriginalID1() 2046 +impl::Writer::getOriginalID1()
2028 { 2047 {
2029 QPDFObjectHandle trailer = qpdf.getTrailer(); 2048 QPDFObjectHandle trailer = qpdf.getTrailer();
2030 if (trailer.hasKey("/ID")) { 2049 if (trailer.hasKey("/ID")) {
@@ -2035,7 +2054,7 @@ QPDFWriter::Members::getOriginalID1() @@ -2035,7 +2054,7 @@ QPDFWriter::Members::getOriginalID1()
2035 } 2054 }
2036 2055
2037 void 2056 void
2038 -QPDFWriter::Members::generateID(bool encrypted) 2057 +impl::Writer::generateID(bool encrypted)
2039 { 2058 {
2040 // Generate the ID lazily so that we can handle the user's preference to use static or 2059 // Generate the ID lazily so that we can handle the user's preference to use static or
2041 // deterministic ID generation. 2060 // deterministic ID generation.
@@ -2048,7 +2067,7 @@ QPDFWriter::Members::generateID(bool encrypted) @@ -2048,7 +2067,7 @@ QPDFWriter::Members::generateID(bool encrypted)
2048 2067
2049 std::string result; 2068 std::string result;
2050 2069
2051 - if (static_id) { 2070 + if (cfg.static_id()) {
2052 // For test suite use only... 2071 // For test suite use only...
2053 static unsigned char tmp[] = { 2072 static unsigned char tmp[] = {
2054 0x31, 2073 0x31,
@@ -2080,7 +2099,7 @@ QPDFWriter::Members::generateID(bool encrypted) @@ -2080,7 +2099,7 @@ QPDFWriter::Members::generateID(bool encrypted)
2080 // that case, would have the same ID regardless of the output file's name. 2099 // that case, would have the same ID regardless of the output file's name.
2081 2100
2082 std::string seed; 2101 std::string seed;
2083 - if (deterministic_id) { 2102 + if (cfg.deterministic_id()) {
2084 if (encrypted) { 2103 if (encrypted) {
2085 throw std::runtime_error( 2104 throw std::runtime_error(
2086 "QPDFWriter: unable to generated a deterministic ID because the file to be " 2105 "QPDFWriter: unable to generated a deterministic ID because the file to be "
@@ -2125,7 +2144,7 @@ QPDFWriter::Members::generateID(bool encrypted) @@ -2125,7 +2144,7 @@ QPDFWriter::Members::generateID(bool encrypted)
2125 } 2144 }
2126 2145
2127 void 2146 void
2128 -QPDFWriter::Members::initializeSpecialStreams() 2147 +impl::Writer::initializeSpecialStreams()
2129 { 2148 {
2130 // Mark all page content streams in case we are filtering or normalizing. 2149 // Mark all page content streams in case we are filtering or normalizing.
2131 int num = 0; 2150 int num = 0;
@@ -2150,9 +2169,9 @@ QPDFWriter::Members::initializeSpecialStreams() @@ -2150,9 +2169,9 @@ QPDFWriter::Members::initializeSpecialStreams()
2150 } 2169 }
2151 2170
2152 void 2171 void
2153 -QPDFWriter::Members::preserveObjectStreams() 2172 +impl::Writer::preserveObjectStreams()
2154 { 2173 {
2155 - auto const& xref = getXRefTable(); 2174 + auto const& xref = objects.xref_table();
2156 // Our object_to_object_stream map has to map ObjGen -> ObjGen since we may be generating object 2175 // Our object_to_object_stream map has to map ObjGen -> ObjGen since we may be generating object
2157 // streams out of old objects that have generation numbers greater than zero. However in an 2176 // streams out of old objects that have generation numbers greater than zero. However in an
2158 // existing PDF, all object stream objects and all objects in them must have generation 0 2177 // existing PDF, all object stream objects and all objects in them must have generation 0
@@ -2162,7 +2181,7 @@ QPDFWriter::Members::preserveObjectStreams() @@ -2162,7 +2181,7 @@ QPDFWriter::Members::preserveObjectStreams()
2162 // objects from being included. 2181 // objects from being included.
2163 auto end = xref.cend(); 2182 auto end = xref.cend();
2164 obj.streams_empty = true; 2183 obj.streams_empty = true;
2165 - if (preserve_unreferenced_objects) { 2184 + if (cfg.preserve_unreferenced()) {
2166 for (auto iter = xref.cbegin(); iter != end; ++iter) { 2185 for (auto iter = xref.cbegin(); iter != end; ++iter) {
2167 if (iter->second.getType() == 2) { 2186 if (iter->second.getType() == 2) {
2168 // Pdf contains object streams. 2187 // Pdf contains object streams.
@@ -2177,9 +2196,9 @@ QPDFWriter::Members::preserveObjectStreams() @@ -2177,9 +2196,9 @@ QPDFWriter::Members::preserveObjectStreams()
2177 if (iter->second.getType() == 2) { 2196 if (iter->second.getType() == 2) {
2178 // Pdf contains object streams. 2197 // Pdf contains object streams.
2179 obj.streams_empty = false; 2198 obj.streams_empty = false;
2180 - auto eligible = getCompressibleObjSet(); 2199 + auto eligible = objects.compressible_set();
2181 // The object pointed to by iter may be a previous generation, in which case it is 2200 // The object pointed to by iter may be a previous generation, in which case it is
2182 - // removed by getCompressibleObjSet. We need to restart the loop (while the object 2201 + // removed by compressible_set. We need to restart the loop (while the object
2183 // table may contain multiple generations of an object). 2202 // table may contain multiple generations of an object).
2184 for (iter = xref.cbegin(); iter != end; ++iter) { 2203 for (iter = xref.cbegin(); iter != end; ++iter) {
2185 if (iter->second.getType() == 2) { 2204 if (iter->second.getType() == 2) {
@@ -2198,7 +2217,7 @@ QPDFWriter::Members::preserveObjectStreams() @@ -2198,7 +2217,7 @@ QPDFWriter::Members::preserveObjectStreams()
2198 } 2217 }
2199 2218
2200 void 2219 void
2201 -QPDFWriter::Members::generateObjectStreams() 2220 +impl::Writer::generateObjectStreams()
2202 { 2221 {
2203 // Basic strategy: make a list of objects that can go into an object stream. Then figure out 2222 // Basic strategy: make a list of objects that can go into an object stream. Then figure out
2204 // how many object streams are needed so that we can distribute objects approximately evenly 2223 // how many object streams are needed so that we can distribute objects approximately evenly
@@ -2208,7 +2227,7 @@ QPDFWriter::Members::generateObjectStreams() @@ -2208,7 +2227,7 @@ QPDFWriter::Members::generateObjectStreams()
2208 2227
2209 // This code doesn't do anything with /Extends. 2228 // This code doesn't do anything with /Extends.
2210 2229
2211 - std::vector<QPDFObjGen> eligible = getCompressibleObjGens(); 2230 + auto eligible = objects.compressible_vector();
2212 size_t n_object_streams = (eligible.size() + 99U) / 100U; 2231 size_t n_object_streams = (eligible.size() + 99U) / 100U;
2213 2232
2214 initializeTables(2U * n_object_streams); 2233 initializeTables(2U * n_object_streams);
@@ -2237,7 +2256,7 @@ QPDFWriter::Members::generateObjectStreams() @@ -2237,7 +2256,7 @@ QPDFWriter::Members::generateObjectStreams()
2237 } 2256 }
2238 2257
2239 Dictionary 2258 Dictionary
2240 -QPDFWriter::Members::trimmed_trailer() 2259 +impl::Writer::trimmed_trailer()
2241 { 2260 {
2242 // Remove keys from the trailer that necessarily have to be replaced when writing the file. 2261 // Remove keys from the trailer that necessarily have to be replaced when writing the file.
2243 2262
@@ -2264,7 +2283,7 @@ QPDFWriter::Members::trimmed_trailer() @@ -2264,7 +2283,7 @@ QPDFWriter::Members::trimmed_trailer()
2264 2283
2265 // Make document extension level information direct as required by the spec. 2284 // Make document extension level information direct as required by the spec.
2266 void 2285 void
2267 -QPDFWriter::Members::prepareFileForWrite() 2286 +impl::Writer::prepareFileForWrite()
2268 { 2287 {
2269 qpdf.fixDanglingReferences(); 2288 qpdf.fixDanglingReferences();
2270 auto root = qpdf.getRoot(); 2289 auto root = qpdf.getRoot();
@@ -2287,15 +2306,15 @@ QPDFWriter::Members::prepareFileForWrite() @@ -2287,15 +2306,15 @@ QPDFWriter::Members::prepareFileForWrite()
2287 } 2306 }
2288 2307
2289 void 2308 void
2290 -QPDFWriter::Members::initializeTables(size_t extra) 2309 +impl::Writer::initializeTables(size_t extra)
2291 { 2310 {
2292 - auto size = QIntC::to_size(tableSize() + 100) + extra; 2311 + auto size = objects.table_size() + 100u + extra;
2293 obj.resize(size); 2312 obj.resize(size);
2294 new_obj.resize(size); 2313 new_obj.resize(size);
2295 } 2314 }
2296 2315
2297 void 2316 void
2298 -QPDFWriter::Members::doWriteSetup() 2317 +impl::Writer::doWriteSetup()
2299 { 2318 {
2300 if (did_write_setup) { 2319 if (did_write_setup) {
2301 return; 2320 return;
@@ -2304,63 +2323,42 @@ QPDFWriter::Members::doWriteSetup() @@ -2304,63 +2323,42 @@ QPDFWriter::Members::doWriteSetup()
2304 2323
2305 // Do preliminary setup 2324 // Do preliminary setup
2306 2325
2307 - if (linearized) {  
2308 - qdf_mode = false; 2326 + if (cfg.linearize()) {
  2327 + cfg.qdf(false);
2309 } 2328 }
2310 2329
2311 - if (pclm) {  
2312 - stream_decode_level = qpdf_dl_none;  
2313 - compress_streams = false; 2330 + if (cfg.pclm()) {
2314 encryption = nullptr; 2331 encryption = nullptr;
2315 } 2332 }
2316 2333
2317 - if (qdf_mode) {  
2318 - if (!normalize_content_set) {  
2319 - normalize_content = true;  
2320 - }  
2321 - if (!compress_streams_set) {  
2322 - compress_streams = false;  
2323 - }  
2324 - if (!stream_decode_level_set) {  
2325 - stream_decode_level = qpdf_dl_generalized;  
2326 - }  
2327 - }  
2328 -  
2329 if (encryption) { 2334 if (encryption) {
2330 // Encryption has been explicitly set 2335 // Encryption has been explicitly set
2331 - preserve_encryption = false;  
2332 - } else if (normalize_content || pclm || qdf_mode) { 2336 + cfg.preserve_encryption(false);
  2337 + } else if (cfg.normalize_content() || cfg.pclm()) {
2333 // Encryption makes looking at contents pretty useless. If the user explicitly encrypted 2338 // Encryption makes looking at contents pretty useless. If the user explicitly encrypted
2334 // though, we still obey that. 2339 // though, we still obey that.
2335 - preserve_encryption = false; 2340 + cfg.preserve_encryption(false);
2336 } 2341 }
2337 2342
2338 - if (preserve_encryption) { 2343 + if (cfg.preserve_encryption()) {
2339 copyEncryptionParameters(qpdf); 2344 copyEncryptionParameters(qpdf);
2340 } 2345 }
2341 2346
2342 - if (!forced_pdf_version.empty()) { 2347 + if (!cfg.forced_pdf_version().empty()) {
2343 int major = 0; 2348 int major = 0;
2344 int minor = 0; 2349 int minor = 0;
2345 - parseVersion(forced_pdf_version, major, minor);  
2346 - disableIncompatibleEncryption(major, minor, forced_extension_level); 2350 + parseVersion(cfg.forced_pdf_version(), major, minor);
  2351 + disableIncompatibleEncryption(major, minor, cfg.forced_extension_level());
2347 if (compareVersions(major, minor, 1, 5) < 0) { 2352 if (compareVersions(major, minor, 1, 5) < 0) {
2348 - object_stream_mode = qpdf_o_disable; 2353 + cfg.object_streams(qpdf_o_disable);
2349 } 2354 }
2350 } 2355 }
2351 2356
2352 - if (qdf_mode || normalize_content) { 2357 + if (cfg.qdf() || cfg.normalize_content()) {
2353 initializeSpecialStreams(); 2358 initializeSpecialStreams();
2354 } 2359 }
2355 2360
2356 - if (qdf_mode) {  
2357 - // Generate indirect stream lengths for qdf mode since fix-qdf uses them for storing  
2358 - // recomputed stream length data. Certain streams such as object streams, xref streams, and  
2359 - // hint streams always get direct stream lengths.  
2360 - direct_stream_lengths = false;  
2361 - }  
2362 -  
2363 - switch (object_stream_mode) { 2361 + switch (cfg.object_streams()) {
2364 case qpdf_o_disable: 2362 case qpdf_o_disable:
2365 initializeTables(); 2363 initializeTables();
2366 obj.streams_empty = true; 2364 obj.streams_empty = true;
@@ -2374,12 +2372,10 @@ QPDFWriter::Members::doWriteSetup() @@ -2374,12 +2372,10 @@ QPDFWriter::Members::doWriteSetup()
2374 case qpdf_o_generate: 2372 case qpdf_o_generate:
2375 generateObjectStreams(); 2373 generateObjectStreams();
2376 break; 2374 break;
2377 -  
2378 - // no default so gcc will warn for missing case tag  
2379 } 2375 }
2380 2376
2381 if (!obj.streams_empty) { 2377 if (!obj.streams_empty) {
2382 - if (linearized) { 2378 + if (cfg.linearize()) {
2383 // Page dictionaries are not allowed to be compressed objects. 2379 // Page dictionaries are not allowed to be compressed objects.
2384 for (auto& page: pages) { 2380 for (auto& page: pages) {
2385 if (obj[page].object_stream > 0) { 2381 if (obj[page].object_stream > 0) {
@@ -2388,9 +2384,9 @@ QPDFWriter::Members::doWriteSetup() @@ -2388,9 +2384,9 @@ QPDFWriter::Members::doWriteSetup()
2388 } 2384 }
2389 } 2385 }
2390 2386
2391 - if (linearized || encryption) {  
2392 - // The document catalog is not allowed to be compressed in linearized files either. It  
2393 - // also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to 2387 + if (cfg.linearize() || encryption) {
  2388 + // The document catalog is not allowed to be compressed in cfg.linearized_ files either.
  2389 + // It also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to
2394 // handle encrypted files with compressed document catalogs, so we disable them in that 2390 // handle encrypted files with compressed document catalogs, so we disable them in that
2395 // case as well. 2391 // case as well.
2396 if (obj[root_og].object_stream > 0) { 2392 if (obj[root_og].object_stream > 0) {
@@ -2413,16 +2409,16 @@ QPDFWriter::Members::doWriteSetup() @@ -2413,16 +2409,16 @@ QPDFWriter::Members::doWriteSetup()
2413 if (object_stream_to_objects.empty()) { 2409 if (object_stream_to_objects.empty()) {
2414 obj.streams_empty = true; 2410 obj.streams_empty = true;
2415 } else { 2411 } else {
2416 - w.setMinimumPDFVersion("1.5"); 2412 + setMinimumPDFVersion("1.5");
2417 } 2413 }
2418 } 2414 }
2419 2415
2420 setMinimumPDFVersion(qpdf.getPDFVersion(), qpdf.getExtensionLevel()); 2416 setMinimumPDFVersion(qpdf.getPDFVersion(), qpdf.getExtensionLevel());
2421 final_pdf_version = min_pdf_version; 2417 final_pdf_version = min_pdf_version;
2422 final_extension_level = min_extension_level; 2418 final_extension_level = min_extension_level;
2423 - if (!forced_pdf_version.empty()) {  
2424 - final_pdf_version = forced_pdf_version;  
2425 - final_extension_level = forced_extension_level; 2419 + if (!cfg.forced_pdf_version().empty()) {
  2420 + final_pdf_version = cfg.forced_pdf_version();
  2421 + final_extension_level = cfg.forced_extension_level();
2426 } 2422 }
2427 } 2423 }
2428 2424
@@ -2433,17 +2429,17 @@ QPDFWriter::write() @@ -2433,17 +2429,17 @@ QPDFWriter::write()
2433 } 2429 }
2434 2430
2435 void 2431 void
2436 -QPDFWriter::Members::write() 2432 +impl::Writer::write()
2437 { 2433 {
2438 doWriteSetup(); 2434 doWriteSetup();
2439 2435
2440 // Set up progress reporting. For linearized files, we write two passes. events_expected is an 2436 // Set up progress reporting. For linearized files, we write two passes. events_expected is an
2441 // approximation, but it's good enough for progress reporting, which is mostly a guess anyway. 2437 // approximation, but it's good enough for progress reporting, which is mostly a guess anyway.
2442 - events_expected = QIntC::to_int(qpdf.getObjectCount() * (linearized ? 2 : 1)); 2438 + events_expected = QIntC::to_int(qpdf.getObjectCount() * (cfg.linearize() ? 2 : 1));
2443 2439
2444 prepareFileForWrite(); 2440 prepareFileForWrite();
2445 2441
2446 - if (linearized) { 2442 + if (cfg.linearize()) {
2447 writeLinearized(); 2443 writeLinearized();
2448 } else { 2444 } else {
2449 writeStandard(); 2445 writeStandard();
@@ -2474,7 +2470,7 @@ QPDFWriter::getWrittenXRefTable() @@ -2474,7 +2470,7 @@ QPDFWriter::getWrittenXRefTable()
2474 } 2470 }
2475 2471
2476 std::map<QPDFObjGen, QPDFXRefEntry> 2472 std::map<QPDFObjGen, QPDFXRefEntry>
2477 -QPDFWriter::Members::getWrittenXRefTable() 2473 +impl::Writer::getWrittenXRefTable()
2478 { 2474 {
2479 std::map<QPDFObjGen, QPDFXRefEntry> result; 2475 std::map<QPDFObjGen, QPDFXRefEntry> result;
2480 2476
@@ -2488,7 +2484,7 @@ QPDFWriter::Members::getWrittenXRefTable() @@ -2488,7 +2484,7 @@ QPDFWriter::Members::getWrittenXRefTable()
2488 } 2484 }
2489 2485
2490 void 2486 void
2491 -QPDFWriter::Members::enqueuePart(std::vector<QPDFObjectHandle>& part) 2487 +impl::Writer::enqueuePart(std::vector<QPDFObjectHandle>& part)
2492 { 2488 {
2493 for (auto const& oh: part) { 2489 for (auto const& oh: part) {
2494 enqueue(oh); 2490 enqueue(oh);
@@ -2496,7 +2492,7 @@ QPDFWriter::Members::enqueuePart(std::vector&lt;QPDFObjectHandle&gt;&amp; part) @@ -2496,7 +2492,7 @@ QPDFWriter::Members::enqueuePart(std::vector&lt;QPDFObjectHandle&gt;&amp; part)
2496 } 2492 }
2497 2493
2498 void 2494 void
2499 -QPDFWriter::Members::writeEncryptionDictionary() 2495 +impl::Writer::writeEncryptionDictionary()
2500 { 2496 {
2501 encryption_dict_objid = openObject(encryption_dict_objid); 2497 encryption_dict_objid = openObject(encryption_dict_objid);
2502 auto& enc = *encryption; 2498 auto& enc = *encryption;
@@ -2505,10 +2501,10 @@ QPDFWriter::Members::writeEncryptionDictionary() @@ -2505,10 +2501,10 @@ QPDFWriter::Members::writeEncryptionDictionary()
2505 write("<<"); 2501 write("<<");
2506 if (V >= 4) { 2502 if (V >= 4) {
2507 write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM "); 2503 write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM ");
2508 - write(encrypt_use_aes ? (V < 5 ? "/AESV2" : "/AESV3") : "/V2"); 2504 + write(cfg.encrypt_use_aes() ? (V < 5 ? "/AESV2" : "/AESV3") : "/V2");
2509 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of 2505 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
2510 // MacOS won't open encrypted files without it. 2506 // MacOS won't open encrypted files without it.
2511 - write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>"); 2507 + write(V < 5 ? " /Length 16 >> >>" : " /Length 32 >> >>");
2512 if (!encryption->getEncryptMetadata()) { 2508 if (!encryption->getEncryptMetadata()) {
2513 write(" /EncryptMetadata false"); 2509 write(" /EncryptMetadata false");
2514 } 2510 }
@@ -2543,10 +2539,10 @@ QPDFWriter::getFinalVersion() @@ -2543,10 +2539,10 @@ QPDFWriter::getFinalVersion()
2543 } 2539 }
2544 2540
2545 void 2541 void
2546 -QPDFWriter::Members::writeHeader() 2542 +impl::Writer::writeHeader()
2547 { 2543 {
2548 write("%PDF-").write(final_pdf_version); 2544 write("%PDF-").write(final_pdf_version);
2549 - if (pclm) { 2545 + if (cfg.pclm()) {
2550 // PCLm version 2546 // PCLm version
2551 write("\n%PCLm 1.0\n"); 2547 write("\n%PCLm 1.0\n");
2552 } else { 2548 } else {
@@ -2563,13 +2559,13 @@ QPDFWriter::Members::writeHeader() @@ -2563,13 +2559,13 @@ QPDFWriter::Members::writeHeader()
2563 } 2559 }
2564 2560
2565 void 2561 void
2566 -QPDFWriter::Members::writeHintStream(int hint_id) 2562 +impl::Writer::writeHintStream(int hint_id)
2567 { 2563 {
2568 std::string hint_buffer; 2564 std::string hint_buffer;
2569 int S = 0; 2565 int S = 0;
2570 int O = 0; 2566 int O = 0;
2571 - bool compressed = compress_streams;  
2572 - generateHintStream(new_obj, obj, hint_buffer, S, O, compressed); 2567 + bool compressed = cfg.compress_streams();
  2568 + lin.generateHintStream(new_obj, obj, hint_buffer, S, O, compressed);
2573 2569
2574 openObject(hint_id); 2570 openObject(hint_id);
2575 setDataKey(hint_id); 2571 setDataKey(hint_id);
@@ -2597,7 +2593,7 @@ QPDFWriter::Members::writeHintStream(int hint_id) @@ -2597,7 +2593,7 @@ QPDFWriter::Members::writeHintStream(int hint_id)
2597 } 2593 }
2598 2594
2599 qpdf_offset_t 2595 qpdf_offset_t
2600 -QPDFWriter::Members::writeXRefTable(trailer_e which, int first, int last, int size) 2596 +impl::Writer::writeXRefTable(trailer_e which, int first, int last, int size)
2601 { 2597 {
2602 // There are too many extra arguments to replace overloaded function with defaults in the header 2598 // There are too many extra arguments to replace overloaded function with defaults in the header
2603 // file...too much risk of leaving something off. 2599 // file...too much risk of leaving something off.
@@ -2605,7 +2601,7 @@ QPDFWriter::Members::writeXRefTable(trailer_e which, int first, int last, int si @@ -2605,7 +2601,7 @@ QPDFWriter::Members::writeXRefTable(trailer_e which, int first, int last, int si
2605 } 2601 }
2606 2602
2607 qpdf_offset_t 2603 qpdf_offset_t
2608 -QPDFWriter::Members::writeXRefTable( 2604 +impl::Writer::writeXRefTable(
2609 trailer_e which, 2605 trailer_e which,
2610 int first, 2606 int first,
2611 int last, 2607 int last,
@@ -2640,7 +2636,7 @@ QPDFWriter::Members::writeXRefTable( @@ -2640,7 +2636,7 @@ QPDFWriter::Members::writeXRefTable(
2640 } 2636 }
2641 2637
2642 qpdf_offset_t 2638 qpdf_offset_t
2643 -QPDFWriter::Members::writeXRefStream( 2639 +impl::Writer::writeXRefStream(
2644 int objid, int max_id, qpdf_offset_t max_offset, trailer_e which, int first, int last, int size) 2640 int objid, int max_id, qpdf_offset_t max_offset, trailer_e which, int first, int last, int size)
2645 { 2641 {
2646 // There are too many extra arguments to replace overloaded function with defaults in the header 2642 // There are too many extra arguments to replace overloaded function with defaults in the header
@@ -2650,7 +2646,7 @@ QPDFWriter::Members::writeXRefStream( @@ -2650,7 +2646,7 @@ QPDFWriter::Members::writeXRefStream(
2650 } 2646 }
2651 2647
2652 qpdf_offset_t 2648 qpdf_offset_t
2653 -QPDFWriter::Members::writeXRefStream( 2649 +impl::Writer::writeXRefStream(
2654 int xref_id, 2650 int xref_id,
2655 int max_id, 2651 int max_id,
2656 qpdf_offset_t max_offset, 2652 qpdf_offset_t max_offset,
@@ -2681,7 +2677,7 @@ QPDFWriter::Members::writeXRefStream( @@ -2681,7 +2677,7 @@ QPDFWriter::Members::writeXRefStream(
2681 new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount()); 2677 new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount());
2682 2678
2683 std::string xref_data; 2679 std::string xref_data;
2684 - const bool compressed = compress_streams && !qdf_mode; 2680 + const bool compressed = cfg.compress_streams() && !cfg.qdf();
2685 { 2681 {
2686 auto pp_xref = pipeline_stack.activate(xref_data); 2682 auto pp_xref = pipeline_stack.activate(xref_data);
2687 2683
@@ -2746,7 +2742,7 @@ QPDFWriter::Members::writeXRefStream( @@ -2746,7 +2742,7 @@ QPDFWriter::Members::writeXRefStream(
2746 } 2742 }
2747 2743
2748 size_t 2744 size_t
2749 -QPDFWriter::Members::calculateXrefStreamPadding(qpdf_offset_t xref_bytes) 2745 +impl::Writer::calculateXrefStreamPadding(qpdf_offset_t xref_bytes)
2750 { 2746 {
2751 // This routine is called right after a linearization first pass xref stream has been written 2747 // This routine is called right after a linearization first pass xref stream has been written
2752 // without compression. Calculate the amount of padding that would be required in the worst 2748 // without compression. Calculate the amount of padding that would be required in the worst
@@ -2758,7 +2754,7 @@ QPDFWriter::Members::calculateXrefStreamPadding(qpdf_offset_t xref_bytes) @@ -2758,7 +2754,7 @@ QPDFWriter::Members::calculateXrefStreamPadding(qpdf_offset_t xref_bytes)
2758 } 2754 }
2759 2755
2760 void 2756 void
2761 -QPDFWriter::Members::writeLinearized() 2757 +impl::Writer::writeLinearized()
2762 { 2758 {
2763 // Optimize file and enqueue objects in order 2759 // Optimize file and enqueue objects in order
2764 2760
@@ -2772,14 +2768,14 @@ QPDFWriter::Members::writeLinearized() @@ -2772,14 +2768,14 @@ QPDFWriter::Members::writeLinearized()
2772 } 2768 }
2773 }; 2769 };
2774 2770
2775 - optimize(obj, skip_stream_parameters); 2771 + lin.optimize(obj, skip_stream_parameters);
2776 2772
2777 std::vector<QPDFObjectHandle> part4; 2773 std::vector<QPDFObjectHandle> part4;
2778 std::vector<QPDFObjectHandle> part6; 2774 std::vector<QPDFObjectHandle> part6;
2779 std::vector<QPDFObjectHandle> part7; 2775 std::vector<QPDFObjectHandle> part7;
2780 std::vector<QPDFObjectHandle> part8; 2776 std::vector<QPDFObjectHandle> part8;
2781 std::vector<QPDFObjectHandle> part9; 2777 std::vector<QPDFObjectHandle> part9;
2782 - getLinearizedParts(obj, part4, part6, part7, part8, part9); 2778 + lin.parts(obj, part4, part6, part7, part8, part9);
2783 2779
2784 // Object number sequence: 2780 // Object number sequence:
2785 // 2781 //
@@ -2871,7 +2867,7 @@ QPDFWriter::Members::writeLinearized() @@ -2871,7 +2867,7 @@ QPDFWriter::Members::writeLinearized()
2871 enqueuePart(part8); 2867 enqueuePart(part8);
2872 enqueuePart(part9); 2868 enqueuePart(part9);
2873 if (next_objid != after_second_half) { 2869 if (next_objid != after_second_half) {
2874 - throw std::runtime_error("error encountered after writing part 9 of linearized data"); 2870 + throw std::runtime_error("error encountered after writing part 9 of cfg.linearized_ data");
2875 } 2871 }
2876 2872
2877 qpdf_offset_t hint_length = 0; 2873 qpdf_offset_t hint_length = 0;
@@ -2884,15 +2880,15 @@ QPDFWriter::Members::writeLinearized() @@ -2884,15 +2880,15 @@ QPDFWriter::Members::writeLinearized()
2884 auto pp_md5 = pipeline_stack.popper(); 2880 auto pp_md5 = pipeline_stack.popper();
2885 for (int pass: {1, 2}) { 2881 for (int pass: {1, 2}) {
2886 if (pass == 1) { 2882 if (pass == 1) {
2887 - if (!lin_pass1_filename.empty()) {  
2888 - lin_pass1_file = QUtil::safe_fopen(lin_pass1_filename.c_str(), "wb"); 2883 + if (!cfg.linearize_pass1().empty()) {
  2884 + lin_pass1_file = QUtil::safe_fopen(cfg.linearize_pass1().data(), "wb");
2889 pipeline_stack.activate( 2885 pipeline_stack.activate(
2890 pp_pass1, 2886 pp_pass1,
2891 std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file)); 2887 std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file));
2892 } else { 2888 } else {
2893 pipeline_stack.activate(pp_pass1, true); 2889 pipeline_stack.activate(pp_pass1, true);
2894 } 2890 }
2895 - if (deterministic_id) { 2891 + if (cfg.deterministic_id()) {
2896 pipeline_stack.activate_md5(pp_md5); 2892 pipeline_stack.activate_md5(pp_md5);
2897 } 2893 }
2898 } 2894 }
@@ -2927,7 +2923,7 @@ QPDFWriter::Members::writeLinearized() @@ -2927,7 +2923,7 @@ QPDFWriter::Members::writeLinearized()
2927 2923
2928 // If the user supplied any additional header text, write it here after the linearization 2924 // If the user supplied any additional header text, write it here after the linearization
2929 // parameter dictionary. 2925 // parameter dictionary.
2930 - write(extra_header_text); 2926 + write(cfg.extra_header_text());
2931 2927
2932 // Part 3: first page cross reference table and trailer. 2928 // Part 3: first page cross reference table and trailer.
2933 2929
@@ -3062,7 +3058,7 @@ QPDFWriter::Members::writeLinearized() @@ -3062,7 +3058,7 @@ QPDFWriter::Members::writeLinearized()
3062 write("startxref\n").write(first_xref_offset).write("\n%%EOF\n"); 3058 write("startxref\n").write(first_xref_offset).write("\n%%EOF\n");
3063 3059
3064 if (pass == 1) { 3060 if (pass == 1) {
3065 - if (deterministic_id) { 3061 + if (cfg.deterministic_id()) {
3066 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1); 3062 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1);
3067 computeDeterministicIDData(); 3063 computeDeterministicIDData();
3068 pp_md5.pop(); 3064 pp_md5.pop();
@@ -3105,9 +3101,9 @@ QPDFWriter::Members::writeLinearized() @@ -3105,9 +3101,9 @@ QPDFWriter::Members::writeLinearized()
3105 } 3101 }
3106 3102
3107 void 3103 void
3108 -QPDFWriter::Members::enqueueObjectsStandard() 3104 +impl::Writer::enqueueObjectsStandard()
3109 { 3105 {
3110 - if (preserve_unreferenced_objects) { 3106 + if (cfg.preserve_unreferenced()) {
3111 for (auto const& oh: qpdf.getAllObjects()) { 3107 for (auto const& oh: qpdf.getAllObjects()) {
3112 enqueue(oh); 3108 enqueue(oh);
3113 } 3109 }
@@ -3127,7 +3123,7 @@ QPDFWriter::Members::enqueueObjectsStandard() @@ -3127,7 +3123,7 @@ QPDFWriter::Members::enqueueObjectsStandard()
3127 } 3123 }
3128 3124
3129 void 3125 void
3130 -QPDFWriter::Members::enqueueObjectsPCLm() 3126 +impl::Writer::enqueueObjectsPCLm()
3131 { 3127 {
3132 // Image transform stream content for page strip images. Each of this new stream has to come 3128 // Image transform stream content for page strip images. Each of this new stream has to come
3133 // after every page image strip written in the pclm file. 3129 // after every page image strip written in the pclm file.
@@ -3151,7 +3147,7 @@ QPDFWriter::Members::enqueueObjectsPCLm() @@ -3151,7 +3147,7 @@ QPDFWriter::Members::enqueueObjectsPCLm()
3151 } 3147 }
3152 3148
3153 void 3149 void
3154 -QPDFWriter::Members::indicateProgress(bool decrement, bool finished) 3150 +impl::Writer::indicateProgress(bool decrement, bool finished)
3155 { 3151 {
3156 if (decrement) { 3152 if (decrement) {
3157 --events_seen; 3153 --events_seen;
@@ -3185,19 +3181,19 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr) @@ -3185,19 +3181,19 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr)
3185 } 3181 }
3186 3182
3187 void 3183 void
3188 -QPDFWriter::Members::writeStandard() 3184 +impl::Writer::writeStandard()
3189 { 3185 {
3190 auto pp_md5 = pipeline_stack.popper(); 3186 auto pp_md5 = pipeline_stack.popper();
3191 - if (deterministic_id) { 3187 + if (cfg.deterministic_id()) {
3192 pipeline_stack.activate_md5(pp_md5); 3188 pipeline_stack.activate_md5(pp_md5);
3193 } 3189 }
3194 3190
3195 // Start writing 3191 // Start writing
3196 3192
3197 writeHeader(); 3193 writeHeader();
3198 - write(extra_header_text); 3194 + write(cfg.extra_header_text());
3199 3195
3200 - if (pclm) { 3196 + if (cfg.pclm()) {
3201 enqueueObjectsPCLm(); 3197 enqueueObjectsPCLm();
3202 } else { 3198 } else {
3203 enqueueObjectsStandard(); 3199 enqueueObjectsStandard();
@@ -3227,7 +3223,7 @@ QPDFWriter::Members::writeStandard() @@ -3227,7 +3223,7 @@ QPDFWriter::Members::writeStandard()
3227 } 3223 }
3228 write("startxref\n").write(xref_offset).write("\n%%EOF\n"); 3224 write("startxref\n").write(xref_offset).write("\n%%EOF\n");
3229 3225
3230 - if (deterministic_id) { 3226 + if (cfg.deterministic_id()) {
3231 QTC::TC( 3227 QTC::TC(
3232 "qpdf", 3228 "qpdf",
3233 "QPDFWriter standard deterministic ID", 3229 "QPDFWriter standard deterministic ID",
libqpdf/QPDF_linearization.cc
@@ -1681,7 +1681,7 @@ Lin::pushOutlinesToPart( @@ -1681,7 +1681,7 @@ Lin::pushOutlinesToPart(
1681 } 1681 }
1682 1682
1683 void 1683 void
1684 -Lin::getLinearizedParts( 1684 +Lin::parts(
1685 QPDFWriter::ObjTable const& obj, 1685 QPDFWriter::ObjTable const& obj,
1686 std::vector<QPDFObjectHandle>& part4, 1686 std::vector<QPDFObjectHandle>& part4,
1687 std::vector<QPDFObjectHandle>& part6, 1687 std::vector<QPDFObjectHandle>& part6,
libqpdf/QPDF_objects.cc
@@ -1916,7 +1916,7 @@ QPDF::swapObjects(QPDFObjGen og1, QPDFObjGen og2) @@ -1916,7 +1916,7 @@ QPDF::swapObjects(QPDFObjGen og1, QPDFObjGen og2)
1916 } 1916 }
1917 1917
1918 size_t 1918 size_t
1919 -Objects::tableSize() 1919 +Objects::table_size()
1920 { 1920 {
1921 // If obj_cache is dense, accommodate all object in tables,else accommodate only original 1921 // If obj_cache is dense, accommodate all object in tables,else accommodate only original
1922 // objects. 1922 // objects.
@@ -1936,20 +1936,20 @@ Objects::tableSize() @@ -1936,20 +1936,20 @@ Objects::tableSize()
1936 } 1936 }
1937 1937
1938 std::vector<QPDFObjGen> 1938 std::vector<QPDFObjGen>
1939 -Objects::getCompressibleObjVector() 1939 +Objects::compressible_vector()
1940 { 1940 {
1941 - return getCompressibleObjGens<QPDFObjGen>(); 1941 + return compressible<QPDFObjGen>();
1942 } 1942 }
1943 1943
1944 std::vector<bool> 1944 std::vector<bool>
1945 -Objects::getCompressibleObjSet() 1945 +Objects::compressible_set()
1946 { 1946 {
1947 - return getCompressibleObjGens<bool>(); 1947 + return compressible<bool>();
1948 } 1948 }
1949 1949
1950 template <typename T> 1950 template <typename T>
1951 std::vector<T> 1951 std::vector<T>
1952 -Objects::getCompressibleObjGens() 1952 +Objects::compressible()
1953 { 1953 {
1954 // Return a list of objects that are allowed to be in object streams. Walk through the objects 1954 // Return a list of objects that are allowed to be in object streams. Walk through the objects
1955 // by traversing the document from the root, including a traversal of the pages tree. This 1955 // by traversing the document from the root, including a traversal of the pages tree. This
@@ -1969,10 +1969,9 @@ Objects::getCompressibleObjGens() @@ -1969,10 +1969,9 @@ Objects::getCompressibleObjGens()
1969 std::vector<T> result; 1969 std::vector<T> result;
1970 if constexpr (std::is_same_v<T, QPDFObjGen>) { 1970 if constexpr (std::is_same_v<T, QPDFObjGen>) {
1971 result.reserve(m->obj_cache.size()); 1971 result.reserve(m->obj_cache.size());
1972 - } else if constexpr (std::is_same_v<T, bool>) {  
1973 - result.resize(max_obj + 1U, false);  
1974 } else { 1972 } else {
1975 - throw std::logic_error("Unsupported type in QPDF::getCompressibleObjGens"); 1973 + qpdf_static_expect(std::is_same_v<T, bool>);
  1974 + result.resize(max_obj + 1U, false);
1976 } 1975 }
1977 while (!queue.empty()) { 1976 while (!queue.empty()) {
1978 auto obj = queue.back(); 1977 auto obj = queue.back();
libqpdf/qpdf/ObjTable.hh
@@ -75,7 +75,6 @@ class ObjTable: public std::vector&lt;T&gt; @@ -75,7 +75,6 @@ class ObjTable: public std::vector&lt;T&gt;
75 return contains(static_cast<size_t>(oh.getObjectID())); 75 return contains(static_cast<size_t>(oh.getObjectID()));
76 } 76 }
77 77
78 - protected:  
79 inline T& 78 inline T&
80 operator[](int id) 79 operator[](int id)
81 { 80 {
libqpdf/qpdf/QPDFJob_private.hh
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 5
6 #include <qpdf/ClosedFileInputSource.hh> 6 #include <qpdf/ClosedFileInputSource.hh>
7 #include <qpdf/QPDFLogger.hh> 7 #include <qpdf/QPDFLogger.hh>
  8 +#include <qpdf/QPDFWriter_private.hh>
8 #include <qpdf/QPDF_private.hh> 9 #include <qpdf/QPDF_private.hh>
9 10
10 // A selection of pages from a single input PDF to be included in the output. This corresponds to a 11 // A selection of pages from a single input PDF to be included in the output. This corresponds to a
@@ -150,7 +151,7 @@ class QPDFJob::Members @@ -150,7 +151,7 @@ class QPDFJob::Members
150 151
151 public: 152 public:
152 Members(QPDFJob& job) : 153 Members(QPDFJob& job) :
153 - log(qcf.log()), 154 + log(d_cfg.log()),
154 inputs(job) 155 inputs(job)
155 { 156 {
156 } 157 }
@@ -168,14 +169,14 @@ class QPDFJob::Members @@ -168,14 +169,14 @@ class QPDFJob::Members
168 static int constexpr DEFAULT_OI_MIN_AREA = 16384; 169 static int constexpr DEFAULT_OI_MIN_AREA = 16384;
169 static int constexpr DEFAULT_II_MIN_BYTES = 1024; 170 static int constexpr DEFAULT_II_MIN_BYTES = 1024;
170 171
171 - qpdf::Doc::Config qcf; 172 + qpdf::Doc::Config d_cfg;
  173 + qpdf::Writer::Config w_cfg;
172 std::shared_ptr<QPDFLogger> log; 174 std::shared_ptr<QPDFLogger> log;
173 std::string message_prefix{"qpdf"}; 175 std::string message_prefix{"qpdf"};
174 bool warnings{false}; 176 bool warnings{false};
175 unsigned long encryption_status{0}; 177 unsigned long encryption_status{0};
176 bool verbose{false}; 178 bool verbose{false};
177 std::string password; 179 std::string password;
178 - bool linearize{false};  
179 bool decrypt{false}; 180 bool decrypt{false};
180 bool remove_restrictions{false}; 181 bool remove_restrictions{false};
181 int split_pages{0}; 182 int split_pages{0};
@@ -206,25 +207,9 @@ class QPDFJob::Members @@ -206,25 +207,9 @@ class QPDFJob::Members
206 bool force_R5{false}; 207 bool force_R5{false};
207 bool cleartext_metadata{false}; 208 bool cleartext_metadata{false};
208 bool use_aes{false}; 209 bool use_aes{false};
209 - bool stream_data_set{false};  
210 - qpdf_stream_data_e stream_data_mode{qpdf_s_compress};  
211 - bool compress_streams{true};  
212 - bool compress_streams_set{false};  
213 - bool recompress_flate{false};  
214 - bool recompress_flate_set{false};  
215 int compression_level{-1}; 210 int compression_level{-1};
216 int jpeg_quality{-1}; 211 int jpeg_quality{-1};
217 - qpdf_stream_decode_level_e decode_level{qpdf_dl_generalized};  
218 - bool decode_level_set{false};  
219 - bool normalize_set{false};  
220 - bool normalize{false};  
221 - bool object_stream_set{false};  
222 - qpdf_object_stream_e object_stream_mode{qpdf_o_preserve};  
223 - bool qdf_mode{false};  
224 - bool preserve_unreferenced_objects{false};  
225 remove_unref_e remove_unreferenced_page_resources{re_auto}; 212 remove_unref_e remove_unreferenced_page_resources{re_auto};
226 - bool newline_before_endstream{false};  
227 - std::string linearize_pass1;  
228 bool coalesce_contents{false}; 213 bool coalesce_contents{false};
229 bool flatten_annotations{false}; 214 bool flatten_annotations{false};
230 int flatten_annotations_required{0}; 215 int flatten_annotations_required{0};
@@ -234,10 +219,7 @@ class QPDFJob::Members @@ -234,10 +219,7 @@ class QPDFJob::Members
234 std::string min_version; 219 std::string min_version;
235 std::string force_version; 220 std::string force_version;
236 bool show_npages{false}; 221 bool show_npages{false};
237 - bool deterministic_id{false};  
238 - bool static_id{false};  
239 bool static_aes_iv{false}; 222 bool static_aes_iv{false};
240 - bool suppress_original_object_id{false};  
241 bool show_encryption{false}; 223 bool show_encryption{false};
242 bool show_encryption_key{false}; 224 bool show_encryption_key{false};
243 bool check_linearization{false}; 225 bool check_linearization{false};
libqpdf/qpdf/QPDFObject_private.hh
@@ -30,6 +30,11 @@ namespace qpdf @@ -30,6 +30,11 @@ namespace qpdf
30 class Integer; 30 class Integer;
31 class Name; 31 class Name;
32 class Stream; 32 class Stream;
  33 +
  34 + namespace impl
  35 + {
  36 + class Writer;
  37 + }
33 } // namespace qpdf 38 } // namespace qpdf
34 39
35 class QPDF_Array final 40 class QPDF_Array final
@@ -256,7 +261,7 @@ class QPDF_String final @@ -256,7 +261,7 @@ class QPDF_String final
256 { 261 {
257 friend class QPDFObject; 262 friend class QPDFObject;
258 friend class qpdf::BaseHandle; 263 friend class qpdf::BaseHandle;
259 - friend class QPDFWriter; 264 + friend class qpdf::impl::Writer;
260 265
261 public: 266 public:
262 static std::shared_ptr<QPDFObject> create_utf16(std::string const& utf8_val); 267 static std::shared_ptr<QPDFObject> create_utf16(std::string const& utf8_val);
libqpdf/qpdf/QPDFWriter_private.hh
@@ -5,10 +5,332 @@ @@ -5,10 +5,332 @@
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>
  9 +#include <qpdf/QPDFUsage.hh>
8 10
9 // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization 11 // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization
10 // only. 12 // only.
11 13
  14 +namespace qpdf
  15 +{
  16 + namespace impl
  17 + {
  18 + class Writer;
  19 + }
  20 +
  21 + class Writer: public QPDFWriter
  22 + {
  23 + public:
  24 + class Config
  25 + {
  26 + public:
  27 + Config() = default;
  28 + Config(Config const&) = default;
  29 + Config(Config&&) = delete;
  30 + Config& operator=(Config const&) = default;
  31 + Config& operator=(Config&&) = delete;
  32 + ~Config() = default;
  33 +
  34 + Config(bool permissive) :
  35 + permissive_(permissive)
  36 + {
  37 + }
  38 +
  39 + bool
  40 + linearize() const
  41 + {
  42 + return linearize_;
  43 + }
  44 +
  45 + Config& linearize(bool val);
  46 +
  47 + std::string const&
  48 + linearize_pass1() const
  49 + {
  50 + return linearize_pass1_;
  51 + }
  52 +
  53 + Config&
  54 + linearize_pass1(std::string const& val)
  55 + {
  56 + linearize_pass1_ = val;
  57 + return *this;
  58 + }
  59 +
  60 + bool
  61 + preserve_encryption() const
  62 + {
  63 + return preserve_encryption_;
  64 + }
  65 +
  66 + Config&
  67 + preserve_encryption(bool val)
  68 + {
  69 + preserve_encryption_ = val;
  70 + return *this;
  71 + }
  72 +
  73 + bool
  74 + encrypt_use_aes() const
  75 + {
  76 + return encrypt_use_aes_;
  77 + }
  78 +
  79 + Config&
  80 + encrypt_use_aes(bool val)
  81 + {
  82 + encrypt_use_aes_ = val;
  83 + return *this;
  84 + }
  85 +
  86 + Config&
  87 + default_decode_level(qpdf_stream_decode_level_e val)
  88 + {
  89 + if (!decode_level_set_) {
  90 + decode_level_ = val;
  91 + }
  92 + return *this;
  93 + }
  94 +
  95 + qpdf_stream_decode_level_e
  96 + decode_level() const
  97 + {
  98 + return decode_level_;
  99 + }
  100 +
  101 + Config& decode_level(qpdf_stream_decode_level_e val);
  102 +
  103 + qpdf_object_stream_e
  104 + object_streams() const
  105 + {
  106 + return object_streams_;
  107 + }
  108 +
  109 + Config&
  110 + object_streams(qpdf_object_stream_e val)
  111 + {
  112 + object_streams_ = val;
  113 + return *this;
  114 + }
  115 +
  116 + bool
  117 + compress_streams() const
  118 + {
  119 + return compress_streams_;
  120 + }
  121 +
  122 + Config& compress_streams(bool val);
  123 +
  124 + bool
  125 + direct_stream_lengths() const
  126 + {
  127 + return direct_stream_lengths_;
  128 + }
  129 +
  130 + bool
  131 + newline_before_endstream() const
  132 + {
  133 + return newline_before_endstream_;
  134 + }
  135 +
  136 + Config&
  137 + newline_before_endstream(bool val)
  138 + {
  139 + newline_before_endstream_ = val;
  140 + return *this;
  141 + }
  142 +
  143 + bool
  144 + recompress_flate() const
  145 + {
  146 + return recompress_flate_;
  147 + }
  148 +
  149 + Config&
  150 + recompress_flate(bool val)
  151 + {
  152 + recompress_flate_ = val;
  153 + return *this;
  154 + }
  155 +
  156 + Config& stream_data(qpdf_stream_data_e val);
  157 +
  158 + std::string const&
  159 + forced_pdf_version() const
  160 + {
  161 + return forced_pdf_version_;
  162 + }
  163 +
  164 + Config&
  165 + forced_pdf_version(std::string const& val)
  166 + {
  167 + forced_pdf_version_ = val;
  168 + return *this;
  169 + }
  170 +
  171 + Config&
  172 + forced_pdf_version(std::string const& val, int ext)
  173 + {
  174 + forced_pdf_version_ = val;
  175 + forced_extension_level_ = ext;
  176 + return *this;
  177 + }
  178 +
  179 + int
  180 + forced_extension_level() const
  181 + {
  182 + return forced_extension_level_;
  183 + }
  184 +
  185 + Config&
  186 + forced_extension_level(int val)
  187 + {
  188 + forced_extension_level_ = val;
  189 + return *this;
  190 + }
  191 +
  192 + std::string const&
  193 + extra_header_text()
  194 + {
  195 + return extra_header_text_;
  196 + }
  197 +
  198 + Config& extra_header_text(std::string const& val);
  199 +
  200 + bool
  201 + preserve_unreferenced() const
  202 + {
  203 + return preserve_unreferenced_;
  204 + }
  205 +
  206 + Config&
  207 + preserve_unreferenced(bool val)
  208 + {
  209 + preserve_unreferenced_ = val;
  210 + return *this;
  211 + }
  212 +
  213 + bool
  214 + no_original_object_ids() const
  215 + {
  216 + return no_original_object_ids_;
  217 + }
  218 +
  219 + Config&
  220 + no_original_object_ids(bool val)
  221 + {
  222 + no_original_object_ids_ = val;
  223 + return *this;
  224 + }
  225 +
  226 + bool
  227 + qdf() const
  228 + {
  229 + return qdf_;
  230 + }
  231 +
  232 + Config& qdf(bool val);
  233 +
  234 + bool
  235 + normalize_content() const
  236 + {
  237 + return normalize_content_;
  238 + }
  239 +
  240 + Config&
  241 + normalize_content(bool val)
  242 + {
  243 + normalize_content_ = val;
  244 + normalize_content_set_ = true;
  245 + return *this;
  246 + }
  247 +
  248 + bool
  249 + deterministic_id() const
  250 + {
  251 + return deterministic_id_;
  252 + }
  253 +
  254 + Config&
  255 + deterministic_id(bool val)
  256 + {
  257 + deterministic_id_ = val;
  258 + return *this;
  259 + }
  260 +
  261 + bool
  262 + static_id() const
  263 + {
  264 + return static_id_;
  265 + }
  266 +
  267 + Config&
  268 + static_id(bool val)
  269 + {
  270 + static_id_ = val;
  271 + return *this;
  272 + }
  273 +
  274 + bool
  275 + pclm() const
  276 + {
  277 + return pclm_;
  278 + }
  279 +
  280 + Config& pclm(bool val);
  281 +
  282 + private:
  283 + void
  284 + usage(std::string const& msg) const
  285 + {
  286 + if (!permissive_) {
  287 + throw QPDFUsage(msg);
  288 + }
  289 + }
  290 +
  291 + std::string forced_pdf_version_;
  292 + std::string extra_header_text_;
  293 + // For linearization only
  294 + std::string linearize_pass1_;
  295 +
  296 + qpdf_object_stream_e object_streams_{qpdf_o_preserve};
  297 + qpdf_stream_decode_level_e decode_level_{qpdf_dl_generalized};
  298 +
  299 + int forced_extension_level_{0};
  300 +
  301 + bool normalize_content_set_{false};
  302 + bool normalize_content_{false};
  303 + bool compress_streams_{true};
  304 + bool compress_streams_set_{false};
  305 + bool decode_level_set_{false};
  306 + bool recompress_flate_{false};
  307 + bool qdf_{false};
  308 + bool preserve_unreferenced_{false};
  309 + bool newline_before_endstream_{false};
  310 + bool deterministic_id_{false};
  311 + bool static_id_{false};
  312 + bool no_original_object_ids_{false};
  313 + bool direct_stream_lengths_{true};
  314 + bool preserve_encryption_{true};
  315 + bool linearize_{false};
  316 + bool pclm_{false};
  317 + bool encrypt_use_aes_{false};
  318 +
  319 + bool permissive_{true};
  320 + }; // class Writer::Config
  321 +
  322 + Writer() = delete;
  323 + Writer(Writer const&) = delete;
  324 + Writer(Writer&&) = delete;
  325 + Writer& operator=(Writer const&) = delete;
  326 + Writer& operator=(Writer&&) = delete;
  327 + ~Writer() = default;
  328 +
  329 + Writer(QPDF& qpdf, Config cfg);
  330 +
  331 + }; // class Writer
  332 +} // namespace qpdf
  333 +
12 struct QPDFWriter::Object 334 struct QPDFWriter::Object
13 { 335 {
14 int renumber{0}; 336 int renumber{0};
@@ -24,7 +346,7 @@ struct QPDFWriter::NewObject @@ -24,7 +346,7 @@ struct QPDFWriter::NewObject
24 346
25 class QPDFWriter::ObjTable: public ::ObjTable<QPDFWriter::Object> 347 class QPDFWriter::ObjTable: public ::ObjTable<QPDFWriter::Object>
26 { 348 {
27 - friend class QPDFWriter; 349 + friend class qpdf::impl::Writer;
28 350
29 public: 351 public:
30 bool 352 bool
libqpdf/qpdf/QPDF_private.hh
@@ -22,6 +22,11 @@ namespace qpdf @@ -22,6 +22,11 @@ namespace qpdf
22 class OffsetBuffer; 22 class OffsetBuffer;
23 } // namespace is 23 } // namespace is
24 24
  25 + namespace impl
  26 + {
  27 + using Doc = QPDF::Doc;
  28 + }
  29 +
25 class Doc: public QPDF 30 class Doc: public QPDF
26 { 31 {
27 public: 32 public:
@@ -271,7 +276,6 @@ class QPDF::Doc @@ -271,7 +276,6 @@ class QPDF::Doc
271 class Pages; 276 class Pages;
272 class ParseGuard; 277 class ParseGuard;
273 class Resolver; 278 class Resolver;
274 - class Writer;  
275 279
276 // This is the common base-class for all document components. It is used by the other document 280 // This is the common base-class for all document components. It is used by the other document
277 // components to access common functionality. It is not meant to be used directly by the user. 281 // components to access common functionality. It is not meant to be used directly by the user.
@@ -286,6 +290,7 @@ class QPDF::Doc @@ -286,6 +290,7 @@ class QPDF::Doc
286 ~Common() = default; 290 ~Common() = default;
287 291
288 inline Common(QPDF& qpdf, QPDF::Members* m); 292 inline Common(QPDF& qpdf, QPDF::Members* m);
  293 + inline Common(Doc& doc);
289 294
290 void stopOnError(std::string const& message); 295 void stopOnError(std::string const& message);
291 void warn(QPDFExc const& e); 296 void warn(QPDFExc const& e);
@@ -546,7 +551,7 @@ class QPDF::Doc::Linearization: Common @@ -546,7 +551,7 @@ class QPDF::Doc::Linearization: Common
546 ~Linearization() = default; 551 ~Linearization() = default;
547 552
548 Linearization(Doc& doc) : 553 Linearization(Doc& doc) :
549 - Common(doc.qpdf, doc.m) 554 + Common(doc)
550 { 555 {
551 } 556 }
552 557
@@ -567,7 +572,7 @@ class QPDF::Doc::Linearization: Common @@ -567,7 +572,7 @@ class QPDF::Doc::Linearization: Common
567 572
568 // Get lists of all objects in order according to the part of a linearized file that they 573 // Get lists of all objects in order according to the part of a linearized file that they
569 // belong to. 574 // belong to.
570 - void getLinearizedParts( 575 + void parts(
571 QPDFWriter::ObjTable const& obj, 576 QPDFWriter::ObjTable const& obj,
572 std::vector<QPDFObjectHandle>& part4, 577 std::vector<QPDFObjectHandle>& part4,
573 std::vector<QPDFObjectHandle>& part6, 578 std::vector<QPDFObjectHandle>& part6,
@@ -940,7 +945,7 @@ class QPDF::Doc::Objects: Common @@ -940,7 +945,7 @@ class QPDF::Doc::Objects: Common
940 ~Objects() = default; 945 ~Objects() = default;
941 946
942 Objects(Doc& doc) : 947 Objects(Doc& doc) :
943 - Common(doc.qpdf, doc.m), 948 + Common(doc),
944 foreign_(*this), 949 foreign_(*this),
945 streams_(*this) 950 streams_(*this)
946 { 951 {
@@ -987,18 +992,19 @@ class QPDF::Doc::Objects: Common @@ -987,18 +992,19 @@ class QPDF::Doc::Objects: Common
987 QPDFObjectHandle makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj); 992 QPDFObjectHandle makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj);
988 std::shared_ptr<QPDFObject> getObjectForParser(int id, int gen, bool parse_pdf); 993 std::shared_ptr<QPDFObject> getObjectForParser(int id, int gen, bool parse_pdf);
989 std::shared_ptr<QPDFObject> getObjectForJSON(int id, int gen); 994 std::shared_ptr<QPDFObject> getObjectForJSON(int id, int gen);
990 - size_t tableSize(); 995 + size_t table_size();
991 996
992 // For QPDFWriter: 997 // For QPDFWriter:
993 998
994 - std::map<QPDFObjGen, QPDFXRefEntry> const& getXRefTableInternal(); 999 + std::map<QPDFObjGen, QPDFXRefEntry> const& xref_table();
  1000 + std::vector<QPDFObjGen> compressible_vector();
  1001 + std::vector<bool> compressible_set();
  1002 +
  1003 + private:
995 // Get a list of objects that would be permitted in an object stream. 1004 // Get a list of objects that would be permitted in an object stream.
996 template <typename T> 1005 template <typename T>
997 - std::vector<T> getCompressibleObjGens();  
998 - std::vector<QPDFObjGen> getCompressibleObjVector();  
999 - std::vector<bool> getCompressibleObjSet(); 1006 + std::vector<T> compressible();
1000 1007
1001 - private:  
1002 void setTrailer(QPDFObjectHandle obj); 1008 void setTrailer(QPDFObjectHandle obj);
1003 void reconstruct_xref(QPDFExc& e, bool found_startxref = true); 1009 void reconstruct_xref(QPDFExc& e, bool found_startxref = true);
1004 void read_xref(qpdf_offset_t offset, bool in_stream_recovery = false); 1010 void read_xref(qpdf_offset_t offset, bool in_stream_recovery = false);
@@ -1060,7 +1066,7 @@ class QPDF::Doc::Pages: Common @@ -1060,7 +1066,7 @@ class QPDF::Doc::Pages: Common
1060 ~Pages() = default; 1066 ~Pages() = default;
1061 1067
1062 Pages(Doc& doc) : 1068 Pages(Doc& doc) :
1063 - Common(doc.qpdf, doc.m) 1069 + Common(doc)
1064 { 1070 {
1065 } 1071 }
1066 1072
@@ -1214,6 +1220,11 @@ inline QPDF::Doc::Common::Common(QPDF&amp; qpdf, QPDF::Members* m) : @@ -1214,6 +1220,11 @@ inline QPDF::Doc::Common::Common(QPDF&amp; qpdf, QPDF::Members* m) :
1214 { 1220 {
1215 } 1221 }
1216 1222
  1223 +inline QPDF::Doc::Common::Common(Doc& doc) :
  1224 + Common(doc.qpdf, doc.m)
  1225 +{
  1226 +}
  1227 +
1217 inline QPDF::Doc::Linearization& 1228 inline QPDF::Doc::Linearization&
1218 QPDF::Doc::linearization() 1229 QPDF::Doc::linearization()
1219 { 1230 {
@@ -1245,7 +1256,7 @@ QPDF::doc() @@ -1245,7 +1256,7 @@ QPDF::doc()
1245 } 1256 }
1246 1257
1247 inline QPDF::Doc::Objects::Foreign::Copier::Copier(QPDF& qpdf) : 1258 inline QPDF::Doc::Objects::Foreign::Copier::Copier(QPDF& qpdf) :
1248 - Common(qpdf, qpdf.doc().m) 1259 + Common(qpdf.doc())
1249 { 1260 {
1250 } 1261 }
1251 1262
qpdf/qpdf.testcov
@@ -46,7 +46,6 @@ QPDFObjectHandle copy stream 1 @@ -46,7 +46,6 @@ QPDFObjectHandle copy stream 1
46 QPDF ignoring XRefStm in trailer 0 46 QPDF ignoring XRefStm in trailer 0
47 SF_FlateLzwDecode PNG filter 0 47 SF_FlateLzwDecode PNG filter 0
48 QPDF xref /Index is array 1 48 QPDF xref /Index is array 1
49 -QPDFWriter encrypt object stream 0  
50 QPDF exclude indirect length 0 49 QPDF exclude indirect length 0
51 QPDF exclude encryption dictionary 0 50 QPDF exclude encryption dictionary 0
52 QPDF_Stream pipeStreamData with null pipeline 0 51 QPDF_Stream pipeStreamData with null pipeline 0