Commit 2ef866db3c00e5b1617cff7b3774bf5addcd4ca1
Committed by
GitHub
Merge pull request #1565 from m-holger/writer
Refactor QPDFWriter configuration
Showing
14 changed files
with
930 additions
and
663 deletions
include/qpdf/QPDFJob.hh
| ... | ... | @@ -490,7 +490,7 @@ class QPDFJob |
| 490 | 490 | |
| 491 | 491 | // Output generation |
| 492 | 492 | void doSplitPages(QPDF& pdf); |
| 493 | - void setWriterOptions(QPDFWriter&); | |
| 493 | + void setWriterOptions(qpdf::Writer&); | |
| 494 | 494 | void setEncryptionOptions(QPDFWriter&); |
| 495 | 495 | void maybeFixWritePassword(int R, std::string& password); |
| 496 | 496 | void writeOutfile(QPDF& pdf); | ... | ... |
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -43,6 +43,11 @@ |
| 43 | 43 | #include <string_view> |
| 44 | 44 | #include <vector> |
| 45 | 45 | |
| 46 | +namespace qpdf | |
| 47 | +{ | |
| 48 | + class Writer; | |
| 49 | +} | |
| 50 | + | |
| 46 | 51 | class QPDF; |
| 47 | 52 | |
| 48 | 53 | // This class implements a simple writer for saving QPDF objects to new PDF files. See comments |
| ... | ... | @@ -440,6 +445,8 @@ class QPDFWriter |
| 440 | 445 | class NewObjTable; |
| 441 | 446 | |
| 442 | 447 | private: |
| 448 | + friend class qpdf::Writer; | |
| 449 | + | |
| 443 | 450 | class Members; |
| 444 | 451 | |
| 445 | 452 | std::shared_ptr<Members> m; | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -27,9 +27,8 @@ |
| 27 | 27 | using namespace qpdf; |
| 28 | 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 | 32 | using Foreign = Objects::Foreign; |
| 34 | 33 | using Streams = Objects::Streams; |
| 35 | 34 | |
| ... | ... | @@ -724,11 +723,11 @@ QPDF::getRoot() |
| 724 | 723 | std::map<QPDFObjGen, QPDFXRefEntry> |
| 725 | 724 | QPDF::getXRefTable() |
| 726 | 725 | { |
| 727 | - return m->objects.getXRefTableInternal(); | |
| 726 | + return m->objects.xref_table(); | |
| 728 | 727 | } |
| 729 | 728 | |
| 730 | 729 | std::map<QPDFObjGen, QPDFXRefEntry> const& |
| 731 | -Objects::getXRefTableInternal() | |
| 730 | +Objects::xref_table() | |
| 732 | 731 | { |
| 733 | 732 | if (!m->parsed) { |
| 734 | 733 | throw std::logic_error("QPDF::getXRefTable called before parsing."); | ... | ... |
libqpdf/QPDFJob.cc
| ... | ... | @@ -20,7 +20,7 @@ |
| 20 | 20 | #include <qpdf/QPDFPageObjectHelper.hh> |
| 21 | 21 | #include <qpdf/QPDFSystemError.hh> |
| 22 | 22 | #include <qpdf/QPDFUsage.hh> |
| 23 | -#include <qpdf/QPDFWriter.hh> | |
| 23 | +#include <qpdf/QPDFWriter_private.hh> | |
| 24 | 24 | #include <qpdf/QPDF_private.hh> |
| 25 | 25 | #include <qpdf/QTC.hh> |
| 26 | 26 | #include <qpdf/QUtil.hh> |
| ... | ... | @@ -30,8 +30,7 @@ |
| 30 | 30 | |
| 31 | 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 | 35 | namespace |
| 37 | 36 | { |
| ... | ... | @@ -477,7 +476,7 @@ QPDFJob::writeQPDF(QPDF& pdf) |
| 477 | 476 | if (!pdf.getWarnings().empty()) { |
| 478 | 477 | m->warnings = true; |
| 479 | 478 | } |
| 480 | - if (m->warnings && !m->qcf.suppress_warnings()) { | |
| 479 | + if (m->warnings && !m->d_cfg.suppress_warnings()) { | |
| 481 | 480 | if (createsOutput()) { |
| 482 | 481 | *m->log->getWarn() |
| 483 | 482 | << m->message_prefix |
| ... | ... | @@ -745,7 +744,8 @@ QPDFJob::doCheck(QPDF& pdf) |
| 745 | 744 | |
| 746 | 745 | // Write the file to nowhere, uncompressing streams. This causes full file traversal and |
| 747 | 746 | // decoding of all streams we can decode. |
| 748 | - QPDFWriter w(pdf); | |
| 747 | + Writer::Config cfg; | |
| 748 | + Writer w(pdf, cfg); | |
| 749 | 749 | Pl_Discard discard; |
| 750 | 750 | w.setOutputPipeline(&discard); |
| 751 | 751 | w.setDecodeLevel(qpdf_dl_all); |
| ... | ... | @@ -804,7 +804,7 @@ QPDFJob::doShowObj(QPDF& pdf) |
| 804 | 804 | m->log->saveToStandardOutput(true); |
| 805 | 805 | obj.pipeStreamData( |
| 806 | 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 | 808 | filter ? qpdf_dl_all : qpdf_dl_none); |
| 809 | 809 | } |
| 810 | 810 | } else { |
| ... | ... | @@ -974,7 +974,7 @@ QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf) |
| 974 | 974 | p, |
| 975 | 975 | false, |
| 976 | 976 | first, |
| 977 | - m->decode_level, | |
| 977 | + m->w_cfg.decode_level(), | |
| 978 | 978 | m->json_stream_data, |
| 979 | 979 | m->json_stream_prefix, |
| 980 | 980 | json_objects); |
| ... | ... | @@ -1052,7 +1052,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool& first, QPDF& pdf) |
| 1052 | 1052 | j_image.addDictionaryMember("decodeparms", dp_array.getJSON(m->json_version)); |
| 1053 | 1053 | j_image.addDictionaryMember( |
| 1054 | 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 | 1057 | j_page.addDictionaryMember("images", j_images); |
| 1058 | 1058 | JSON j_contents = j_page.addDictionaryMember("contents", JSON::makeArray()); |
| ... | ... | @@ -1569,7 +1569,7 @@ QPDFJob::doJSON(QPDF& pdf, Pipeline* p) |
| 1569 | 1569 | JSON::writeDictionaryItem(p, first, "version", JSON::makeInt(m->json_version), 1); |
| 1570 | 1570 | JSON j_params = JSON::makeDictionary(); |
| 1571 | 1571 | std::string decode_level_str; |
| 1572 | - switch (m->decode_level) { | |
| 1572 | + switch (m->w_cfg.decode_level()) { | |
| 1573 | 1573 | case qpdf_dl_none: |
| 1574 | 1574 | decode_level_str = "none"; |
| 1575 | 1575 | break; |
| ... | ... | @@ -1709,7 +1709,7 @@ QPDFJob::doProcessOnce( |
| 1709 | 1709 | bool main_input) |
| 1710 | 1710 | { |
| 1711 | 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 | 1713 | if (empty) { |
| 1714 | 1714 | pdf->emptyPDF(); |
| 1715 | 1715 | } else if (main_input && m->json_input) { |
| ... | ... | @@ -1739,7 +1739,7 @@ QPDFJob::doProcess( |
| 1739 | 1739 | // was incorrectly encoded, there's a good chance we'd succeed here. |
| 1740 | 1740 | |
| 1741 | 1741 | std::string ptemp; |
| 1742 | - if (password && !m->qcf.password_is_hex_key()) { | |
| 1742 | + if (password && !m->d_cfg.password_is_hex_key()) { | |
| 1743 | 1743 | if (m->password_mode == QPDFJob::pm_hex_bytes) { |
| 1744 | 1744 | // Special case: handle --password-mode=hex-bytes for input password as well as output |
| 1745 | 1745 | // password |
| ... | ... | @@ -1747,7 +1747,7 @@ QPDFJob::doProcess( |
| 1747 | 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 | 1751 | // There is no password, or we're not doing recovery, so just do the normal processing with |
| 1752 | 1752 | // the supplied password. |
| 1753 | 1753 | doProcessOnce(pdf, fn, password, empty, used_for_input, main_input); |
| ... | ... | @@ -2879,50 +2879,17 @@ parse_version(std::string const& full_version_string, std::string& version, int& |
| 2879 | 2879 | } |
| 2880 | 2880 | |
| 2881 | 2881 | void |
| 2882 | -QPDFJob::setWriterOptions(QPDFWriter& w) | |
| 2882 | +QPDFJob::setWriterOptions(Writer& w) | |
| 2883 | 2883 | { |
| 2884 | 2884 | if (m->compression_level >= 0) { |
| 2885 | 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 | 2887 | if (m->decrypt) { |
| 2912 | 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 | 2890 | if (m->static_aes_iv) { |
| 2921 | 2891 | w.setStaticAesIV(true); |
| 2922 | 2892 | } |
| 2923 | - if (m->suppress_original_object_id) { | |
| 2924 | - w.setSuppressOriginalObjectIDs(true); | |
| 2925 | - } | |
| 2926 | 2893 | if (m->copy_encryption) { |
| 2927 | 2894 | std::unique_ptr<QPDF> encryption_pdf; |
| 2928 | 2895 | processFile( |
| ... | ... | @@ -2936,15 +2903,6 @@ QPDFJob::setWriterOptions(QPDFWriter& w) |
| 2936 | 2903 | if (m->encrypt) { |
| 2937 | 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 | 2906 | w.setMinimumPDFVersion(m->max_input_version); |
| 2949 | 2907 | if (!m->min_version.empty()) { |
| 2950 | 2908 | std::string version; |
| ... | ... | @@ -2961,15 +2919,13 @@ QPDFJob::setWriterOptions(QPDFWriter& w) |
| 2961 | 2919 | if (m->progress) { |
| 2962 | 2920 | if (m->progress_handler) { |
| 2963 | 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 | 2923 | } else { |
| 2967 | 2924 | char const* outfilename = |
| 2968 | 2925 | !m->outfilename.empty() ? m->outfilename.data() : "standard output"; |
| 2969 | 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& pdf) |
| 3014 | 2970 | last = num_pages; |
| 3015 | 2971 | } |
| 3016 | 2972 | QPDF outpdf; |
| 3017 | - outpdf.doc().config(m->qcf); | |
| 2973 | + outpdf.doc().config(m->d_cfg); | |
| 3018 | 2974 | outpdf.emptyPDF(); |
| 3019 | 2975 | QPDFAcroFormDocumentHelper* out_afdh = |
| 3020 | 2976 | afdh.hasAcroForm() ? &outpdf.doc().acroform() : nullptr; |
| ... | ... | @@ -3052,7 +3008,8 @@ QPDFJob::doSplitPages(QPDF& pdf) |
| 3052 | 3008 | if (QUtil::same_file(m->infile_nm(), outfile.data())) { |
| 3053 | 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 | 3013 | setWriterOptions(w); |
| 3057 | 3014 | w.write(); |
| 3058 | 3015 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
| ... | ... | @@ -3077,9 +3034,8 @@ QPDFJob::writeOutfile(QPDF& pdf) |
| 3077 | 3034 | if (m->json_version) { |
| 3078 | 3035 | writeJSON(pdf); |
| 3079 | 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 | 3039 | if (!m->outfilename.empty()) { |
| 3084 | 3040 | w.setOutputFilename(m->outfilename.data()); |
| 3085 | 3041 | } else { | ... | ... |
libqpdf/QPDFJob_config.cc
| ... | ... | @@ -68,7 +68,7 @@ QPDFJob::Config* |
| 68 | 68 | QPDFJob::Config::check() |
| 69 | 69 | { |
| 70 | 70 | o.m->check = true; |
| 71 | - o.m->qcf.check_mode(true); | |
| 71 | + o.m->d_cfg.check_mode(true); | |
| 72 | 72 | o.m->require_outfile = false; |
| 73 | 73 | return this; |
| 74 | 74 | } |
| ... | ... | @@ -124,8 +124,7 @@ QPDFJob::Config::collate(std::string const& parameter) |
| 124 | 124 | QPDFJob::Config* |
| 125 | 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 | 128 | return this; |
| 130 | 129 | } |
| 131 | 130 | |
| ... | ... | @@ -146,7 +145,7 @@ QPDFJob::Config::jpegQuality(std::string const& parameter) |
| 146 | 145 | QPDFJob::Config* |
| 147 | 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 | 149 | usage("the deterministic-id option is incompatible with encrypted output files"); |
| 151 | 150 | } |
| 152 | 151 | o.m->inputs.encryption_file = parameter; |
| ... | ... | @@ -171,7 +170,7 @@ QPDFJob::Config::deterministicId() |
| 171 | 170 | if (o.m->encrypt || o.m->copy_encryption) { |
| 172 | 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 | 174 | return this; |
| 176 | 175 | } |
| 177 | 176 | |
| ... | ... | @@ -234,7 +233,7 @@ QPDFJob::Config::generateAppearances() |
| 234 | 233 | QPDFJob::Config* |
| 235 | 234 | QPDFJob::Config::ignoreXrefStreams() |
| 236 | 235 | { |
| 237 | - o.m->qcf.ignore_xref_streams(true); | |
| 236 | + o.m->d_cfg.ignore_xref_streams(true); | |
| 238 | 237 | return this; |
| 239 | 238 | } |
| 240 | 239 | |
| ... | ... | @@ -327,9 +326,7 @@ QPDFJob::Config::jsonOutput(std::string const& parameter) |
| 327 | 326 | // No need to set json_stream_data_set -- that indicates explicit use of --json-stream-data. |
| 328 | 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 | 330 | o.m->json_keys.insert("qpdf"); |
| 334 | 331 | return this; |
| 335 | 332 | } |
| ... | ... | @@ -373,14 +370,14 @@ QPDFJob::Config::keepInlineImages() |
| 373 | 370 | QPDFJob::Config* |
| 374 | 371 | QPDFJob::Config::linearize() |
| 375 | 372 | { |
| 376 | - o.m->linearize = true; | |
| 373 | + o.m->w_cfg.linearize(true); | |
| 377 | 374 | return this; |
| 378 | 375 | } |
| 379 | 376 | |
| 380 | 377 | QPDFJob::Config* |
| 381 | 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 | 381 | return this; |
| 385 | 382 | } |
| 386 | 383 | |
| ... | ... | @@ -402,29 +399,28 @@ QPDFJob::Config::minVersion(std::string const& parameter) |
| 402 | 399 | QPDFJob::Config* |
| 403 | 400 | QPDFJob::Config::newlineBeforeEndstream() |
| 404 | 401 | { |
| 405 | - o.m->newline_before_endstream = true; | |
| 402 | + o.m->w_cfg.newline_before_endstream(true); | |
| 406 | 403 | return this; |
| 407 | 404 | } |
| 408 | 405 | |
| 409 | 406 | QPDFJob::Config* |
| 410 | 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 | 410 | return this; |
| 414 | 411 | } |
| 415 | 412 | |
| 416 | 413 | QPDFJob::Config* |
| 417 | 414 | QPDFJob::Config::noWarn() |
| 418 | 415 | { |
| 419 | - o.m->qcf.suppress_warnings(true); | |
| 416 | + o.m->d_cfg.suppress_warnings(true); | |
| 420 | 417 | return this; |
| 421 | 418 | } |
| 422 | 419 | |
| 423 | 420 | QPDFJob::Config* |
| 424 | 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 | 424 | return this; |
| 429 | 425 | } |
| 430 | 426 | |
| ... | ... | @@ -466,14 +462,14 @@ QPDFJob::Config::password(std::string const& parameter) |
| 466 | 462 | QPDFJob::Config* |
| 467 | 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 | 466 | return this; |
| 471 | 467 | } |
| 472 | 468 | |
| 473 | 469 | QPDFJob::Config* |
| 474 | 470 | QPDFJob::Config::preserveUnreferenced() |
| 475 | 471 | { |
| 476 | - o.m->preserve_unreferenced_objects = true; | |
| 472 | + o.m->w_cfg.preserve_unreferenced(true); | |
| 477 | 473 | return this; |
| 478 | 474 | } |
| 479 | 475 | |
| ... | ... | @@ -494,7 +490,7 @@ QPDFJob::Config::progress() |
| 494 | 490 | QPDFJob::Config* |
| 495 | 491 | QPDFJob::Config::qdf() |
| 496 | 492 | { |
| 497 | - o.m->qdf_mode = true; | |
| 493 | + o.m->w_cfg.qdf(true); | |
| 498 | 494 | return this; |
| 499 | 495 | } |
| 500 | 496 | |
| ... | ... | @@ -508,8 +504,7 @@ QPDFJob::Config::rawStreamData() |
| 508 | 504 | QPDFJob::Config* |
| 509 | 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 | 508 | return this; |
| 514 | 509 | } |
| 515 | 510 | |
| ... | ... | @@ -649,7 +644,7 @@ QPDFJob::Config::staticAesIv() |
| 649 | 644 | QPDFJob::Config* |
| 650 | 645 | QPDFJob::Config::staticId() |
| 651 | 646 | { |
| 652 | - o.m->static_id = true; | |
| 647 | + o.m->w_cfg.static_id(true); | |
| 653 | 648 | return this; |
| 654 | 649 | } |
| 655 | 650 | |
| ... | ... | @@ -663,7 +658,7 @@ QPDFJob::Config::suppressPasswordRecovery() |
| 663 | 658 | QPDFJob::Config* |
| 664 | 659 | QPDFJob::Config::suppressRecovery() |
| 665 | 660 | { |
| 666 | - o.m->qcf.surpress_recovery(true); | |
| 661 | + o.m->d_cfg.surpress_recovery(true); | |
| 667 | 662 | return this; |
| 668 | 663 | } |
| 669 | 664 | |
| ... | ... | @@ -731,13 +726,12 @@ QPDFJob::Config::passwordMode(std::string const& parameter) |
| 731 | 726 | QPDFJob::Config* |
| 732 | 727 | QPDFJob::Config::streamData(std::string const& parameter) |
| 733 | 728 | { |
| 734 | - o.m->stream_data_set = true; | |
| 735 | 729 | if (parameter == "compress") { |
| 736 | - o.m->stream_data_mode = qpdf_s_compress; | |
| 730 | + o.m->w_cfg.stream_data(qpdf_s_compress); | |
| 737 | 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 | 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 | 735 | } else { |
| 742 | 736 | usage("invalid stream-data option"); |
| 743 | 737 | } |
| ... | ... | @@ -747,15 +741,14 @@ QPDFJob::Config::streamData(std::string const& parameter) |
| 747 | 741 | QPDFJob::Config* |
| 748 | 742 | QPDFJob::Config::decodeLevel(std::string const& parameter) |
| 749 | 743 | { |
| 750 | - o.m->decode_level_set = true; | |
| 751 | 744 | if (parameter == "none") { |
| 752 | - o.m->decode_level = qpdf_dl_none; | |
| 745 | + o.m->w_cfg.decode_level(qpdf_dl_none); | |
| 753 | 746 | } else if (parameter == "generalized") { |
| 754 | - o.m->decode_level = qpdf_dl_generalized; | |
| 747 | + o.m->w_cfg.decode_level(qpdf_dl_generalized); | |
| 755 | 748 | } else if (parameter == "specialized") { |
| 756 | - o.m->decode_level = qpdf_dl_specialized; | |
| 749 | + o.m->w_cfg.decode_level(qpdf_dl_specialized); | |
| 757 | 750 | } else if (parameter == "all") { |
| 758 | - o.m->decode_level = qpdf_dl_all; | |
| 751 | + o.m->w_cfg.decode_level(qpdf_dl_all); | |
| 759 | 752 | } else { |
| 760 | 753 | usage("invalid option"); |
| 761 | 754 | } |
| ... | ... | @@ -765,13 +758,12 @@ QPDFJob::Config::decodeLevel(std::string const& parameter) |
| 765 | 758 | QPDFJob::Config* |
| 766 | 759 | QPDFJob::Config::objectStreams(std::string const& parameter) |
| 767 | 760 | { |
| 768 | - o.m->object_stream_set = true; | |
| 769 | 761 | if (parameter == "disable") { |
| 770 | - o.m->object_stream_mode = qpdf_o_disable; | |
| 762 | + o.m->w_cfg.object_streams(qpdf_o_disable); | |
| 771 | 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 | 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 | 767 | } else { |
| 776 | 768 | usage("invalid object stream mode"); |
| 777 | 769 | } |
| ... | ... | @@ -1111,7 +1103,7 @@ std::shared_ptr<QPDFJob::EncConfig> |
| 1111 | 1103 | QPDFJob::Config::encrypt( |
| 1112 | 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 | 1107 | usage("the deterministic-id option is incompatible with encrypted output files"); |
| 1116 | 1108 | } |
| 1117 | 1109 | o.m->keylen = keylen; | ... | ... |
libqpdf/QPDFWriter.cc
| ... | ... | @@ -27,8 +27,8 @@ |
| 27 | 27 | using namespace std::literals; |
| 28 | 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 | 33 | QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default) |
| 34 | 34 | { |
| ... | ... | @@ -262,300 +262,231 @@ Pl_stack::Popper::pop() |
| 262 | 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 | 475 | friend class QPDFWriter; |
| 476 | + friend class qpdf::Writer; | |
| 340 | 477 | |
| 341 | 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 | 490 | QPDFWriter::QPDFWriter(QPDF& pdf) : |
| 560 | 491 | m(std::make_shared<Members>(*this, pdf)) |
| 561 | 492 | { |
| ... | ... | @@ -632,75 +563,129 @@ QPDFWriter::setOutputPipeline(Pipeline* p) |
| 632 | 563 | void |
| 633 | 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 | 569 | void |
| 639 | 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 | 578 | switch (mode) { |
| 642 | 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 | 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 | 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 | 596 | void |
| 662 | 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 | 614 | void |
| 669 | 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 | 632 | void |
| 676 | 633 | QPDFWriter::setRecompressFlate(bool val) |
| 677 | 634 | { |
| 678 | - m->recompress_flate = val; | |
| 635 | + m->cfg.recompress_flate(val); | |
| 679 | 636 | } |
| 680 | 637 | |
| 681 | 638 | void |
| 682 | 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 | 644 | void |
| 689 | 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 | 679 | void |
| 695 | 680 | QPDFWriter::setPreserveUnreferencedObjects(bool val) |
| 696 | 681 | { |
| 697 | - m->preserve_unreferenced_objects = val; | |
| 682 | + m->cfg.preserve_unreferenced(val); | |
| 698 | 683 | } |
| 699 | 684 | |
| 700 | 685 | void |
| 701 | 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 | 691 | void |
| ... | ... | @@ -710,7 +695,7 @@ QPDFWriter::setMinimumPDFVersion(std::string const& version, int extension_level |
| 710 | 695 | } |
| 711 | 696 | |
| 712 | 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 | 700 | bool set_version = false; |
| 716 | 701 | bool set_extension_level = false; |
| ... | ... | @@ -756,31 +741,37 @@ QPDFWriter::setMinimumPDFVersion(PDFVersion const& v) |
| 756 | 741 | void |
| 757 | 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 | 747 | void |
| 764 | 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 | 759 | } else { |
| 770 | 760 | QTC::TC("qpdf", "QPDFWriter extra header text no newline"); |
| 771 | 761 | } |
| 762 | + return *this; | |
| 772 | 763 | } |
| 773 | 764 | |
| 774 | 765 | void |
| 775 | 766 | QPDFWriter::setStaticID(bool val) |
| 776 | 767 | { |
| 777 | - m->static_id = val; | |
| 768 | + m->cfg.static_id(val); | |
| 778 | 769 | } |
| 779 | 770 | |
| 780 | 771 | void |
| 781 | 772 | QPDFWriter::setDeterministicID(bool val) |
| 782 | 773 | { |
| 783 | - m->deterministic_id = val; | |
| 774 | + m->cfg.deterministic_id(val); | |
| 784 | 775 | } |
| 785 | 776 | |
| 786 | 777 | void |
| ... | ... | @@ -794,37 +785,61 @@ QPDFWriter::setStaticAesIV(bool val) |
| 794 | 785 | void |
| 795 | 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 | 791 | void |
| 801 | 792 | QPDFWriter::setPreserveEncryption(bool val) |
| 802 | 793 | { |
| 803 | - m->preserve_encryption = val; | |
| 794 | + m->cfg.preserve_encryption(val); | |
| 804 | 795 | } |
| 805 | 796 | |
| 806 | 797 | void |
| 807 | 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 | 814 | void |
| 816 | 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 | 820 | void |
| 822 | 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 | 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 | 845 | void |
| ... | ... | @@ -892,7 +907,7 @@ QPDFWriter::setR4EncryptionParametersInsecure( |
| 892 | 907 | bool use_aes) |
| 893 | 908 | { |
| 894 | 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 | 911 | m->interpretR3EncryptionParameters( |
| 897 | 912 | allow_accessibility, |
| 898 | 913 | allow_extract, |
| ... | ... | @@ -919,7 +934,7 @@ QPDFWriter::setR5EncryptionParameters( |
| 919 | 934 | bool encrypt_metadata) |
| 920 | 935 | { |
| 921 | 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 | 938 | m->interpretR3EncryptionParameters( |
| 924 | 939 | allow_accessibility, |
| 925 | 940 | allow_extract, |
| ... | ... | @@ -955,12 +970,12 @@ QPDFWriter::setR6EncryptionParameters( |
| 955 | 970 | allow_modify_other, |
| 956 | 971 | print, |
| 957 | 972 | qpdf_r3m_all); |
| 958 | - m->encrypt_use_aes = true; | |
| 973 | + m->cfg.encrypt_use_aes(true); | |
| 959 | 974 | m->setEncryptionParameters(user_password, owner_password); |
| 960 | 975 | } |
| 961 | 976 | |
| 962 | 977 | void |
| 963 | -QPDFWriter::Members::interpretR3EncryptionParameters( | |
| 978 | +impl::Writer::interpretR3EncryptionParameters( | |
| 964 | 979 | bool allow_accessibility, |
| 965 | 980 | bool allow_extract, |
| 966 | 981 | bool allow_assemble, |
| ... | ... | @@ -1059,7 +1074,7 @@ QPDFWriter::Members::interpretR3EncryptionParameters( |
| 1059 | 1074 | } |
| 1060 | 1075 | |
| 1061 | 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 | 1079 | generateID(true); |
| 1065 | 1080 | encryption->setId1(id1); |
| ... | ... | @@ -1074,9 +1089,9 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf) |
| 1074 | 1089 | } |
| 1075 | 1090 | |
| 1076 | 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 | 1095 | QPDFObjectHandle trailer = qpdf.getTrailer(); |
| 1081 | 1096 | if (trailer.hasKey("/Encrypt")) { |
| 1082 | 1097 | generateID(true); |
| ... | ... | @@ -1096,10 +1111,10 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF& qpdf) |
| 1096 | 1111 | // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of |
| 1097 | 1112 | // figuring out whether AES is used or not is complicated with /StmF, /StrF, and /EFF |
| 1098 | 1113 | // all potentially having different values. |
| 1099 | - encrypt_use_aes = true; | |
| 1114 | + cfg.encrypt_use_aes(true); | |
| 1100 | 1115 | } |
| 1101 | 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 | 1119 | encryption = std::make_unique<Encryption>( |
| 1105 | 1120 | V, |
| ... | ... | @@ -1120,7 +1135,7 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF& qpdf) |
| 1120 | 1135 | } |
| 1121 | 1136 | |
| 1122 | 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 | 1140 | if (!encryption) { |
| 1126 | 1141 | return; |
| ... | ... | @@ -1140,7 +1155,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext |
| 1140 | 1155 | encryption = nullptr; |
| 1141 | 1156 | } |
| 1142 | 1157 | } else if (compareVersions(major, minor, 1, 6) < 0) { |
| 1143 | - if (encrypt_use_aes) { | |
| 1158 | + if (cfg.encrypt_use_aes()) { | |
| 1144 | 1159 | encryption = nullptr; |
| 1145 | 1160 | } |
| 1146 | 1161 | } else if ( |
| ... | ... | @@ -1157,7 +1172,7 @@ QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int ext |
| 1157 | 1172 | } |
| 1158 | 1173 | |
| 1159 | 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 | 1177 | major = QUtil::string_to_int(version.c_str()); |
| 1163 | 1178 | minor = 0; |
| ... | ... | @@ -1174,7 +1189,7 @@ QPDFWriter::Members::parseVersion(std::string const& version, int& major, int& m |
| 1174 | 1189 | } |
| 1175 | 1190 | |
| 1176 | 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 | 1194 | if (major1 < major2) { |
| 1180 | 1195 | return -1; |
| ... | ... | @@ -1189,33 +1204,38 @@ QPDFWriter::Members::compareVersions(int major1, int minor1, int major2, int min |
| 1189 | 1204 | } |
| 1190 | 1205 | |
| 1191 | 1206 | void |
| 1192 | -QPDFWriter::Members::setEncryptionMinimumVersion() | |
| 1207 | +impl::Writer::setEncryptionMinimumVersion() | |
| 1193 | 1208 | { |
| 1194 | 1209 | auto const R = encryption->getR(); |
| 1195 | 1210 | if (R >= 6) { |
| 1196 | - w.setMinimumPDFVersion("1.7", 8); | |
| 1211 | + setMinimumPDFVersion("1.7", 8); | |
| 1197 | 1212 | } else if (R == 5) { |
| 1198 | - w.setMinimumPDFVersion("1.7", 3); | |
| 1213 | + setMinimumPDFVersion("1.7", 3); | |
| 1199 | 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 | 1216 | } else if (R == 3) { |
| 1202 | - w.setMinimumPDFVersion("1.4"); | |
| 1217 | + setMinimumPDFVersion("1.4"); | |
| 1203 | 1218 | } else { |
| 1204 | - w.setMinimumPDFVersion("1.3"); | |
| 1219 | + setMinimumPDFVersion("1.3"); | |
| 1205 | 1220 | } |
| 1206 | 1221 | } |
| 1207 | 1222 | |
| 1208 | 1223 | void |
| 1209 | -QPDFWriter::Members::setDataKey(int objid) | |
| 1224 | +impl::Writer::setDataKey(int objid) | |
| 1210 | 1225 | { |
| 1211 | 1226 | if (encryption) { |
| 1212 | 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 | 1237 | unsigned int |
| 1218 | -QPDFWriter::Members::bytesNeeded(long long n) | |
| 1238 | +impl::Writer::bytesNeeded(long long n) | |
| 1219 | 1239 | { |
| 1220 | 1240 | unsigned int bytes = 0; |
| 1221 | 1241 | while (n) { |
| ... | ... | @@ -1226,7 +1246,7 @@ QPDFWriter::Members::bytesNeeded(long long n) |
| 1226 | 1246 | } |
| 1227 | 1247 | |
| 1228 | 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 | 1251 | if (bytes > sizeof(unsigned long long)) { |
| 1232 | 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 | 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 | 1265 | pipeline->write(str); |
| 1246 | 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 | 1272 | pipeline->write(std::to_string(val)); |
| 1253 | 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 | 1279 | pipeline->write(count, c); |
| 1260 | 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 | 1286 | pipeline->write(Name::normalize(str)); |
| 1267 | 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 | 1293 | pipeline->write(QPDF_String(str).unparse(force_binary)); |
| 1274 | 1294 | return *this; |
| 1275 | 1295 | } |
| 1276 | 1296 | |
| 1277 | 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 | 1302 | pipeline->write(std::forward<Args>(args)...); |
| 1283 | 1303 | } |
| 1284 | 1304 | return *this; |
| 1285 | 1305 | } |
| 1286 | 1306 | |
| 1287 | 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 | 1312 | pipeline->write(std::forward<Args>(args)...); |
| 1293 | 1313 | } |
| 1294 | 1314 | return *this; |
| 1295 | 1315 | } |
| 1296 | 1316 | |
| 1297 | 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 | 1321 | // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will |
| 1302 | 1322 | // also be prepended by 16 bits of random data. |
| 1303 | 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 | 1330 | if (!(encryption && !cur_data_key.empty())) { |
| 1311 | 1331 | write(str); |
| 1312 | - } else if (encrypt_use_aes) { | |
| 1332 | + } else if (cfg.encrypt_use_aes()) { | |
| 1313 | 1333 | write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key)); |
| 1314 | 1334 | } else { |
| 1315 | 1335 | write(pl::pipe<Pl_RC4>(str, cur_data_key)); |
| ... | ... | @@ -1319,7 +1339,7 @@ QPDFWriter::Members::write_encrypted(std::string_view str) |
| 1319 | 1339 | } |
| 1320 | 1340 | |
| 1321 | 1341 | void |
| 1322 | -QPDFWriter::Members::computeDeterministicIDData() | |
| 1342 | +impl::Writer::computeDeterministicIDData() | |
| 1323 | 1343 | { |
| 1324 | 1344 | if (!id2.empty()) { |
| 1325 | 1345 | // Can't happen in the code |
| ... | ... | @@ -1331,7 +1351,7 @@ QPDFWriter::Members::computeDeterministicIDData() |
| 1331 | 1351 | } |
| 1332 | 1352 | |
| 1333 | 1353 | int |
| 1334 | -QPDFWriter::Members::openObject(int objid) | |
| 1354 | +impl::Writer::openObject(int objid) | |
| 1335 | 1355 | { |
| 1336 | 1356 | if (objid == 0) { |
| 1337 | 1357 | objid = next_objid++; |
| ... | ... | @@ -1342,7 +1362,7 @@ QPDFWriter::Members::openObject(int objid) |
| 1342 | 1362 | } |
| 1343 | 1363 | |
| 1344 | 1364 | void |
| 1345 | -QPDFWriter::Members::closeObject(int objid) | |
| 1365 | +impl::Writer::closeObject(int objid) | |
| 1346 | 1366 | { |
| 1347 | 1367 | // Write a newline before endobj as it makes the file easier to repair. |
| 1348 | 1368 | write("\nendobj\n").write_qdf("\n"); |
| ... | ... | @@ -1351,7 +1371,7 @@ QPDFWriter::Members::closeObject(int objid) |
| 1351 | 1371 | } |
| 1352 | 1372 | |
| 1353 | 1373 | void |
| 1354 | -QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og) | |
| 1374 | +impl::Writer::assignCompressedObjectNumbers(QPDFObjGen og) | |
| 1355 | 1375 | { |
| 1356 | 1376 | int objid = og.getObj(); |
| 1357 | 1377 | if (og.getGen() != 0 || !object_stream_to_objects.contains(objid)) { |
| ... | ... | @@ -1366,7 +1386,7 @@ QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og) |
| 1366 | 1386 | } |
| 1367 | 1387 | |
| 1368 | 1388 | void |
| 1369 | -QPDFWriter::Members::enqueue(QPDFObjectHandle const& object) | |
| 1389 | +impl::Writer::enqueue(QPDFObjectHandle const& object) | |
| 1370 | 1390 | { |
| 1371 | 1391 | if (object.indirect()) { |
| 1372 | 1392 | util::assertion( |
| ... | ... | @@ -1380,7 +1400,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const& object) |
| 1380 | 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 | 1404 | // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so |
| 1385 | 1405 | // will confuse fix-qdf, which expects to see only one XRef stream at the end of the |
| 1386 | 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& object) |
| 1406 | 1426 | if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) { |
| 1407 | 1427 | // For linearized files, uncompressed objects go at end, and we take care of |
| 1408 | 1428 | // assigning numbers to them elsewhere. |
| 1409 | - if (!linearized) { | |
| 1429 | + if (!cfg.linearize()) { | |
| 1410 | 1430 | assignCompressedObjectNumbers(og); |
| 1411 | 1431 | } |
| 1412 | - } else if (!direct_stream_lengths && object.isStream()) { | |
| 1432 | + } else if (!cfg.direct_stream_lengths() && object.isStream()) { | |
| 1413 | 1433 | // reserve next object ID for length |
| 1414 | 1434 | ++next_objid; |
| 1415 | 1435 | } |
| ... | ... | @@ -1418,7 +1438,7 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const& object) |
| 1418 | 1438 | return; |
| 1419 | 1439 | } |
| 1420 | 1440 | |
| 1421 | - if (linearized) { | |
| 1441 | + if (cfg.linearize()) { | |
| 1422 | 1442 | return; |
| 1423 | 1443 | } |
| 1424 | 1444 | |
| ... | ... | @@ -1437,9 +1457,9 @@ QPDFWriter::Members::enqueue(QPDFObjectHandle const& object) |
| 1437 | 1457 | } |
| 1438 | 1458 | |
| 1439 | 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 | 1463 | enqueue(child); |
| 1444 | 1464 | } |
| 1445 | 1465 | if (child.indirect()) { |
| ... | ... | @@ -1450,7 +1470,7 @@ QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, i |
| 1450 | 1470 | } |
| 1451 | 1471 | |
| 1452 | 1472 | void |
| 1453 | -QPDFWriter::Members::writeTrailer( | |
| 1473 | +impl::Writer::writeTrailer( | |
| 1454 | 1474 | trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass) |
| 1455 | 1475 | { |
| 1456 | 1476 | auto trailer = trimmed_trailer(); |
| ... | ... | @@ -1498,7 +1518,7 @@ QPDFWriter::Members::writeTrailer( |
| 1498 | 1518 | } |
| 1499 | 1519 | write("<00000000000000000000000000000000>"); |
| 1500 | 1520 | } else { |
| 1501 | - if (linearization_pass == 0 && deterministic_id) { | |
| 1521 | + if (linearization_pass == 0 && cfg.deterministic_id()) { | |
| 1502 | 1522 | computeDeterministicIDData(); |
| 1503 | 1523 | } |
| 1504 | 1524 | generateID(encryption.get()); |
| ... | ... | @@ -1517,7 +1537,7 @@ QPDFWriter::Members::writeTrailer( |
| 1517 | 1537 | } |
| 1518 | 1538 | |
| 1519 | 1539 | bool |
| 1520 | -QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream) | |
| 1540 | +impl::Writer::will_filter_stream(QPDFObjectHandle stream) | |
| 1521 | 1541 | { |
| 1522 | 1542 | std::string s; |
| 1523 | 1543 | [[maybe_unused]] auto [filter, ignore1, ignore2] = will_filter_stream(stream, &s); |
| ... | ... | @@ -1525,23 +1545,23 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream) |
| 1525 | 1545 | } |
| 1526 | 1546 | |
| 1527 | 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 | 1550 | const bool is_root_metadata = stream.isRootMetadata(); |
| 1531 | 1551 | bool filter = false; |
| 1532 | - auto decode_level = stream_decode_level; | |
| 1552 | + auto decode_level = cfg.decode_level(); | |
| 1533 | 1553 | int encode_flags = 0; |
| 1534 | 1554 | Dictionary stream_dict = stream.getDict(); |
| 1535 | 1555 | |
| 1536 | 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 | 1559 | // Don't filter if the stream is already compressed with FlateDecode. This way we don't |
| 1540 | 1560 | // make it worse if the original file used a better Flate algorithm, and we don't spend |
| 1541 | 1561 | // time and CPU cycles uncompressing and recompressing stuff. This can be overridden |
| 1542 | 1562 | // with setRecompressFlate(true). |
| 1543 | 1563 | Name Filter = stream_dict["/Filter"]; |
| 1544 | - if (Filter && !recompress_flate && !stream.isDataModified() && | |
| 1564 | + if (Filter && !cfg.recompress_flate() && !stream.isDataModified() && | |
| 1545 | 1565 | (Filter == "/FlateDecode" || Filter == "/Fl")) { |
| 1546 | 1566 | filter = false; |
| 1547 | 1567 | } |
| ... | ... | @@ -1549,10 +1569,10 @@ QPDFWriter::Members::will_filter_stream(QPDFObjectHandle stream, std::string* st |
| 1549 | 1569 | if (is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) { |
| 1550 | 1570 | filter = true; |
| 1551 | 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 | 1573 | encode_flags = qpdf_ef_normalize; |
| 1554 | 1574 | filter = true; |
| 1555 | - } else if (filter && compress_streams) { | |
| 1575 | + } else if (filter && cfg.compress_streams()) { | |
| 1556 | 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 | 1618 | } |
| 1599 | 1619 | |
| 1600 | 1620 | void |
| 1601 | -QPDFWriter::Members::unparseObject( | |
| 1621 | +impl::Writer::unparseObject( | |
| 1602 | 1622 | QPDFObjectHandle object, size_t level, int flags, size_t stream_length, bool compress) |
| 1603 | 1623 | { |
| 1604 | 1624 | QPDFObjGen old_og = object.getObjGen(); |
| ... | ... | @@ -1606,11 +1626,11 @@ QPDFWriter::Members::unparseObject( |
| 1606 | 1626 | // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they |
| 1607 | 1627 | // include the preceding newline. |
| 1608 | 1628 | std::string indent_large = " "; |
| 1609 | - if (qdf_mode) { | |
| 1629 | + if (cfg.qdf()) { | |
| 1610 | 1630 | indent_large.append(2 * (level + 1), ' '); |
| 1611 | 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 | 1635 | if (auto const tc = object.getTypeCode(); tc == ::ot_array) { |
| 1616 | 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 | 1686 | if (need_extensions_adbe) { |
| 1667 | 1687 | if (!(have_extensions_other || have_extensions_adbe)) { |
| 1668 | 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 | 1690 | extensions = object.replaceKeyAndGetNew( |
| 1671 | 1691 | "/Extensions", QPDFObjectHandle::newDictionary()); |
| 1672 | 1692 | } |
| ... | ... | @@ -1771,7 +1791,7 @@ QPDFWriter::Members::unparseObject( |
| 1771 | 1791 | if (flags & f_stream) { |
| 1772 | 1792 | write(indent_large).write("/Length "); |
| 1773 | 1793 | |
| 1774 | - if (direct_stream_lengths) { | |
| 1794 | + if (cfg.direct_stream_lengths()) { | |
| 1775 | 1795 | write(stream_length); |
| 1776 | 1796 | } else { |
| 1777 | 1797 | write(cur_stream_length_id).write(" 0 R"); |
| ... | ... | @@ -1784,7 +1804,7 @@ QPDFWriter::Members::unparseObject( |
| 1784 | 1804 | write(indent).write(">>"); |
| 1785 | 1805 | } else if (tc == ::ot_stream) { |
| 1786 | 1806 | // Write stream data to a buffer. |
| 1787 | - if (!direct_stream_lengths) { | |
| 1807 | + if (!cfg.direct_stream_lengths()) { | |
| 1788 | 1808 | cur_stream_length_id = obj[old_og].renumber + 1; |
| 1789 | 1809 | } |
| 1790 | 1810 | |
| ... | ... | @@ -1805,14 +1825,14 @@ QPDFWriter::Members::unparseObject( |
| 1805 | 1825 | unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream); |
| 1806 | 1826 | char last_char = stream_data.empty() ? '\0' : stream_data.back(); |
| 1807 | 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 | 1829 | write(added_newline ? "\nendstream" : "endstream"); |
| 1810 | 1830 | } else if (tc == ::ot_string) { |
| 1811 | 1831 | std::string val; |
| 1812 | 1832 | if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) && |
| 1813 | 1833 | !cur_data_key.empty()) { |
| 1814 | 1834 | val = object.getStringValue(); |
| 1815 | - if (encrypt_use_aes) { | |
| 1835 | + if (cfg.encrypt_use_aes()) { | |
| 1816 | 1836 | Pl_Buffer bufpl("encrypted string"); |
| 1817 | 1837 | Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key); |
| 1818 | 1838 | pl.writeString(val); |
| ... | ... | @@ -1841,7 +1861,7 @@ QPDFWriter::Members::unparseObject( |
| 1841 | 1861 | } |
| 1842 | 1862 | |
| 1843 | 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 | 1866 | qpdf_assert_debug(first_obj > 0); |
| 1847 | 1867 | bool is_first = true; |
| ... | ... | @@ -1860,7 +1880,7 @@ QPDFWriter::Members::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offset |
| 1860 | 1880 | } |
| 1861 | 1881 | |
| 1862 | 1882 | void |
| 1863 | -QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) | |
| 1883 | +impl::Writer::writeObjectStream(QPDFObjectHandle object) | |
| 1864 | 1884 | { |
| 1865 | 1885 | // Note: object might be null if this is a place-holder for an object stream that we are |
| 1866 | 1886 | // generating from scratch. |
| ... | ... | @@ -1878,7 +1898,7 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) |
| 1878 | 1898 | std::string stream_buffer_pass1; |
| 1879 | 1899 | std::string stream_buffer_pass2; |
| 1880 | 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 | 1903 | // Pass 1 |
| 1884 | 1904 | auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1); |
| ... | ... | @@ -1890,9 +1910,9 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) |
| 1890 | 1910 | if (first_obj == -1) { |
| 1891 | 1911 | first_obj = new_o; |
| 1892 | 1912 | } |
| 1893 | - if (qdf_mode) { | |
| 1913 | + if (cfg.qdf()) { | |
| 1894 | 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 | 1916 | write("; original object ID: ").write(og.getObj()); |
| 1897 | 1917 | // For compatibility, only write the generation if non-zero. While object |
| 1898 | 1918 | // streams only allow objects with generation 0, if we are generating object |
| ... | ... | @@ -1968,16 +1988,15 @@ QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object) |
| 1968 | 1988 | } |
| 1969 | 1989 | } |
| 1970 | 1990 | write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n").write_encrypted(stream_buffer_pass2); |
| 1991 | + write(cfg.newline_before_endstream() ? "\nendstream" : "endstream"); | |
| 1971 | 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 | 1995 | closeObject(new_stream_id); |
| 1977 | 1996 | } |
| 1978 | 1997 | |
| 1979 | 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 | 2001 | QPDFObjGen old_og = object.getObjGen(); |
| 1983 | 2002 | |
| ... | ... | @@ -1989,7 +2008,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde |
| 1989 | 2008 | |
| 1990 | 2009 | indicateProgress(false, false); |
| 1991 | 2010 | auto new_id = obj[old_og].renumber; |
| 1992 | - if (qdf_mode) { | |
| 2011 | + if (cfg.qdf()) { | |
| 1993 | 2012 | if (page_object_to_seq.contains(old_og)) { |
| 1994 | 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 | 2017 | } |
| 1999 | 2018 | } |
| 2000 | 2019 | if (object_stream_index == -1) { |
| 2001 | - if (qdf_mode && !suppress_original_object_ids) { | |
| 2020 | + if (cfg.qdf() && !cfg.no_original_object_ids()) { | |
| 2002 | 2021 | write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n"); |
| 2003 | 2022 | } |
| 2004 | 2023 | openObject(new_id); |
| ... | ... | @@ -2011,8 +2030,8 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde |
| 2011 | 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 | 2035 | if (added_newline) { |
| 2017 | 2036 | write("%QDF: ignore_newline\n"); |
| 2018 | 2037 | } |
| ... | ... | @@ -2024,7 +2043,7 @@ QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_inde |
| 2024 | 2043 | } |
| 2025 | 2044 | |
| 2026 | 2045 | std::string |
| 2027 | -QPDFWriter::Members::getOriginalID1() | |
| 2046 | +impl::Writer::getOriginalID1() | |
| 2028 | 2047 | { |
| 2029 | 2048 | QPDFObjectHandle trailer = qpdf.getTrailer(); |
| 2030 | 2049 | if (trailer.hasKey("/ID")) { |
| ... | ... | @@ -2035,7 +2054,7 @@ QPDFWriter::Members::getOriginalID1() |
| 2035 | 2054 | } |
| 2036 | 2055 | |
| 2037 | 2056 | void |
| 2038 | -QPDFWriter::Members::generateID(bool encrypted) | |
| 2057 | +impl::Writer::generateID(bool encrypted) | |
| 2039 | 2058 | { |
| 2040 | 2059 | // Generate the ID lazily so that we can handle the user's preference to use static or |
| 2041 | 2060 | // deterministic ID generation. |
| ... | ... | @@ -2048,7 +2067,7 @@ QPDFWriter::Members::generateID(bool encrypted) |
| 2048 | 2067 | |
| 2049 | 2068 | std::string result; |
| 2050 | 2069 | |
| 2051 | - if (static_id) { | |
| 2070 | + if (cfg.static_id()) { | |
| 2052 | 2071 | // For test suite use only... |
| 2053 | 2072 | static unsigned char tmp[] = { |
| 2054 | 2073 | 0x31, |
| ... | ... | @@ -2080,7 +2099,7 @@ QPDFWriter::Members::generateID(bool encrypted) |
| 2080 | 2099 | // that case, would have the same ID regardless of the output file's name. |
| 2081 | 2100 | |
| 2082 | 2101 | std::string seed; |
| 2083 | - if (deterministic_id) { | |
| 2102 | + if (cfg.deterministic_id()) { | |
| 2084 | 2103 | if (encrypted) { |
| 2085 | 2104 | throw std::runtime_error( |
| 2086 | 2105 | "QPDFWriter: unable to generated a deterministic ID because the file to be " |
| ... | ... | @@ -2125,7 +2144,7 @@ QPDFWriter::Members::generateID(bool encrypted) |
| 2125 | 2144 | } |
| 2126 | 2145 | |
| 2127 | 2146 | void |
| 2128 | -QPDFWriter::Members::initializeSpecialStreams() | |
| 2147 | +impl::Writer::initializeSpecialStreams() | |
| 2129 | 2148 | { |
| 2130 | 2149 | // Mark all page content streams in case we are filtering or normalizing. |
| 2131 | 2150 | int num = 0; |
| ... | ... | @@ -2150,9 +2169,9 @@ QPDFWriter::Members::initializeSpecialStreams() |
| 2150 | 2169 | } |
| 2151 | 2170 | |
| 2152 | 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 | 2175 | // Our object_to_object_stream map has to map ObjGen -> ObjGen since we may be generating object |
| 2157 | 2176 | // streams out of old objects that have generation numbers greater than zero. However in an |
| 2158 | 2177 | // existing PDF, all object stream objects and all objects in them must have generation 0 |
| ... | ... | @@ -2162,7 +2181,7 @@ QPDFWriter::Members::preserveObjectStreams() |
| 2162 | 2181 | // objects from being included. |
| 2163 | 2182 | auto end = xref.cend(); |
| 2164 | 2183 | obj.streams_empty = true; |
| 2165 | - if (preserve_unreferenced_objects) { | |
| 2184 | + if (cfg.preserve_unreferenced()) { | |
| 2166 | 2185 | for (auto iter = xref.cbegin(); iter != end; ++iter) { |
| 2167 | 2186 | if (iter->second.getType() == 2) { |
| 2168 | 2187 | // Pdf contains object streams. |
| ... | ... | @@ -2177,9 +2196,9 @@ QPDFWriter::Members::preserveObjectStreams() |
| 2177 | 2196 | if (iter->second.getType() == 2) { |
| 2178 | 2197 | // Pdf contains object streams. |
| 2179 | 2198 | obj.streams_empty = false; |
| 2180 | - auto eligible = getCompressibleObjSet(); | |
| 2199 | + auto eligible = objects.compressible_set(); | |
| 2181 | 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 | 2202 | // table may contain multiple generations of an object). |
| 2184 | 2203 | for (iter = xref.cbegin(); iter != end; ++iter) { |
| 2185 | 2204 | if (iter->second.getType() == 2) { |
| ... | ... | @@ -2198,7 +2217,7 @@ QPDFWriter::Members::preserveObjectStreams() |
| 2198 | 2217 | } |
| 2199 | 2218 | |
| 2200 | 2219 | void |
| 2201 | -QPDFWriter::Members::generateObjectStreams() | |
| 2220 | +impl::Writer::generateObjectStreams() | |
| 2202 | 2221 | { |
| 2203 | 2222 | // Basic strategy: make a list of objects that can go into an object stream. Then figure out |
| 2204 | 2223 | // how many object streams are needed so that we can distribute objects approximately evenly |
| ... | ... | @@ -2208,7 +2227,7 @@ QPDFWriter::Members::generateObjectStreams() |
| 2208 | 2227 | |
| 2209 | 2228 | // This code doesn't do anything with /Extends. |
| 2210 | 2229 | |
| 2211 | - std::vector<QPDFObjGen> eligible = getCompressibleObjGens(); | |
| 2230 | + auto eligible = objects.compressible_vector(); | |
| 2212 | 2231 | size_t n_object_streams = (eligible.size() + 99U) / 100U; |
| 2213 | 2232 | |
| 2214 | 2233 | initializeTables(2U * n_object_streams); |
| ... | ... | @@ -2237,7 +2256,7 @@ QPDFWriter::Members::generateObjectStreams() |
| 2237 | 2256 | } |
| 2238 | 2257 | |
| 2239 | 2258 | Dictionary |
| 2240 | -QPDFWriter::Members::trimmed_trailer() | |
| 2259 | +impl::Writer::trimmed_trailer() | |
| 2241 | 2260 | { |
| 2242 | 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 | 2283 | |
| 2265 | 2284 | // Make document extension level information direct as required by the spec. |
| 2266 | 2285 | void |
| 2267 | -QPDFWriter::Members::prepareFileForWrite() | |
| 2286 | +impl::Writer::prepareFileForWrite() | |
| 2268 | 2287 | { |
| 2269 | 2288 | qpdf.fixDanglingReferences(); |
| 2270 | 2289 | auto root = qpdf.getRoot(); |
| ... | ... | @@ -2287,15 +2306,15 @@ QPDFWriter::Members::prepareFileForWrite() |
| 2287 | 2306 | } |
| 2288 | 2307 | |
| 2289 | 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 | 2312 | obj.resize(size); |
| 2294 | 2313 | new_obj.resize(size); |
| 2295 | 2314 | } |
| 2296 | 2315 | |
| 2297 | 2316 | void |
| 2298 | -QPDFWriter::Members::doWriteSetup() | |
| 2317 | +impl::Writer::doWriteSetup() | |
| 2299 | 2318 | { |
| 2300 | 2319 | if (did_write_setup) { |
| 2301 | 2320 | return; |
| ... | ... | @@ -2304,63 +2323,42 @@ QPDFWriter::Members::doWriteSetup() |
| 2304 | 2323 | |
| 2305 | 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 | 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 | 2334 | if (encryption) { |
| 2330 | 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 | 2338 | // Encryption makes looking at contents pretty useless. If the user explicitly encrypted |
| 2334 | 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 | 2344 | copyEncryptionParameters(qpdf); |
| 2340 | 2345 | } |
| 2341 | 2346 | |
| 2342 | - if (!forced_pdf_version.empty()) { | |
| 2347 | + if (!cfg.forced_pdf_version().empty()) { | |
| 2343 | 2348 | int major = 0; |
| 2344 | 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 | 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 | 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 | 2362 | case qpdf_o_disable: |
| 2365 | 2363 | initializeTables(); |
| 2366 | 2364 | obj.streams_empty = true; |
| ... | ... | @@ -2374,12 +2372,10 @@ QPDFWriter::Members::doWriteSetup() |
| 2374 | 2372 | case qpdf_o_generate: |
| 2375 | 2373 | generateObjectStreams(); |
| 2376 | 2374 | break; |
| 2377 | - | |
| 2378 | - // no default so gcc will warn for missing case tag | |
| 2379 | 2375 | } |
| 2380 | 2376 | |
| 2381 | 2377 | if (!obj.streams_empty) { |
| 2382 | - if (linearized) { | |
| 2378 | + if (cfg.linearize()) { | |
| 2383 | 2379 | // Page dictionaries are not allowed to be compressed objects. |
| 2384 | 2380 | for (auto& page: pages) { |
| 2385 | 2381 | if (obj[page].object_stream > 0) { |
| ... | ... | @@ -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 | 2390 | // handle encrypted files with compressed document catalogs, so we disable them in that |
| 2395 | 2391 | // case as well. |
| 2396 | 2392 | if (obj[root_og].object_stream > 0) { |
| ... | ... | @@ -2413,16 +2409,16 @@ QPDFWriter::Members::doWriteSetup() |
| 2413 | 2409 | if (object_stream_to_objects.empty()) { |
| 2414 | 2410 | obj.streams_empty = true; |
| 2415 | 2411 | } else { |
| 2416 | - w.setMinimumPDFVersion("1.5"); | |
| 2412 | + setMinimumPDFVersion("1.5"); | |
| 2417 | 2413 | } |
| 2418 | 2414 | } |
| 2419 | 2415 | |
| 2420 | 2416 | setMinimumPDFVersion(qpdf.getPDFVersion(), qpdf.getExtensionLevel()); |
| 2421 | 2417 | final_pdf_version = min_pdf_version; |
| 2422 | 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 | 2429 | } |
| 2434 | 2430 | |
| 2435 | 2431 | void |
| 2436 | -QPDFWriter::Members::write() | |
| 2432 | +impl::Writer::write() | |
| 2437 | 2433 | { |
| 2438 | 2434 | doWriteSetup(); |
| 2439 | 2435 | |
| 2440 | 2436 | // Set up progress reporting. For linearized files, we write two passes. events_expected is an |
| 2441 | 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 | 2440 | prepareFileForWrite(); |
| 2445 | 2441 | |
| 2446 | - if (linearized) { | |
| 2442 | + if (cfg.linearize()) { | |
| 2447 | 2443 | writeLinearized(); |
| 2448 | 2444 | } else { |
| 2449 | 2445 | writeStandard(); |
| ... | ... | @@ -2474,7 +2470,7 @@ QPDFWriter::getWrittenXRefTable() |
| 2474 | 2470 | } |
| 2475 | 2471 | |
| 2476 | 2472 | std::map<QPDFObjGen, QPDFXRefEntry> |
| 2477 | -QPDFWriter::Members::getWrittenXRefTable() | |
| 2473 | +impl::Writer::getWrittenXRefTable() | |
| 2478 | 2474 | { |
| 2479 | 2475 | std::map<QPDFObjGen, QPDFXRefEntry> result; |
| 2480 | 2476 | |
| ... | ... | @@ -2488,7 +2484,7 @@ QPDFWriter::Members::getWrittenXRefTable() |
| 2488 | 2484 | } |
| 2489 | 2485 | |
| 2490 | 2486 | void |
| 2491 | -QPDFWriter::Members::enqueuePart(std::vector<QPDFObjectHandle>& part) | |
| 2487 | +impl::Writer::enqueuePart(std::vector<QPDFObjectHandle>& part) | |
| 2492 | 2488 | { |
| 2493 | 2489 | for (auto const& oh: part) { |
| 2494 | 2490 | enqueue(oh); |
| ... | ... | @@ -2496,7 +2492,7 @@ QPDFWriter::Members::enqueuePart(std::vector<QPDFObjectHandle>& part) |
| 2496 | 2492 | } |
| 2497 | 2493 | |
| 2498 | 2494 | void |
| 2499 | -QPDFWriter::Members::writeEncryptionDictionary() | |
| 2495 | +impl::Writer::writeEncryptionDictionary() | |
| 2500 | 2496 | { |
| 2501 | 2497 | encryption_dict_objid = openObject(encryption_dict_objid); |
| 2502 | 2498 | auto& enc = *encryption; |
| ... | ... | @@ -2505,10 +2501,10 @@ QPDFWriter::Members::writeEncryptionDictionary() |
| 2505 | 2501 | write("<<"); |
| 2506 | 2502 | if (V >= 4) { |
| 2507 | 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 | 2505 | // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of |
| 2510 | 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 | 2508 | if (!encryption->getEncryptMetadata()) { |
| 2513 | 2509 | write(" /EncryptMetadata false"); |
| 2514 | 2510 | } |
| ... | ... | @@ -2543,10 +2539,10 @@ QPDFWriter::getFinalVersion() |
| 2543 | 2539 | } |
| 2544 | 2540 | |
| 2545 | 2541 | void |
| 2546 | -QPDFWriter::Members::writeHeader() | |
| 2542 | +impl::Writer::writeHeader() | |
| 2547 | 2543 | { |
| 2548 | 2544 | write("%PDF-").write(final_pdf_version); |
| 2549 | - if (pclm) { | |
| 2545 | + if (cfg.pclm()) { | |
| 2550 | 2546 | // PCLm version |
| 2551 | 2547 | write("\n%PCLm 1.0\n"); |
| 2552 | 2548 | } else { |
| ... | ... | @@ -2563,13 +2559,13 @@ QPDFWriter::Members::writeHeader() |
| 2563 | 2559 | } |
| 2564 | 2560 | |
| 2565 | 2561 | void |
| 2566 | -QPDFWriter::Members::writeHintStream(int hint_id) | |
| 2562 | +impl::Writer::writeHintStream(int hint_id) | |
| 2567 | 2563 | { |
| 2568 | 2564 | std::string hint_buffer; |
| 2569 | 2565 | int S = 0; |
| 2570 | 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 | 2570 | openObject(hint_id); |
| 2575 | 2571 | setDataKey(hint_id); |
| ... | ... | @@ -2597,7 +2593,7 @@ QPDFWriter::Members::writeHintStream(int hint_id) |
| 2597 | 2593 | } |
| 2598 | 2594 | |
| 2599 | 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 | 2598 | // There are too many extra arguments to replace overloaded function with defaults in the header |
| 2603 | 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 | 2601 | } |
| 2606 | 2602 | |
| 2607 | 2603 | qpdf_offset_t |
| 2608 | -QPDFWriter::Members::writeXRefTable( | |
| 2604 | +impl::Writer::writeXRefTable( | |
| 2609 | 2605 | trailer_e which, |
| 2610 | 2606 | int first, |
| 2611 | 2607 | int last, |
| ... | ... | @@ -2640,7 +2636,7 @@ QPDFWriter::Members::writeXRefTable( |
| 2640 | 2636 | } |
| 2641 | 2637 | |
| 2642 | 2638 | qpdf_offset_t |
| 2643 | -QPDFWriter::Members::writeXRefStream( | |
| 2639 | +impl::Writer::writeXRefStream( | |
| 2644 | 2640 | int objid, int max_id, qpdf_offset_t max_offset, trailer_e which, int first, int last, int size) |
| 2645 | 2641 | { |
| 2646 | 2642 | // There are too many extra arguments to replace overloaded function with defaults in the header |
| ... | ... | @@ -2650,7 +2646,7 @@ QPDFWriter::Members::writeXRefStream( |
| 2650 | 2646 | } |
| 2651 | 2647 | |
| 2652 | 2648 | qpdf_offset_t |
| 2653 | -QPDFWriter::Members::writeXRefStream( | |
| 2649 | +impl::Writer::writeXRefStream( | |
| 2654 | 2650 | int xref_id, |
| 2655 | 2651 | int max_id, |
| 2656 | 2652 | qpdf_offset_t max_offset, |
| ... | ... | @@ -2681,7 +2677,7 @@ QPDFWriter::Members::writeXRefStream( |
| 2681 | 2677 | new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount()); |
| 2682 | 2678 | |
| 2683 | 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 | 2682 | auto pp_xref = pipeline_stack.activate(xref_data); |
| 2687 | 2683 | |
| ... | ... | @@ -2746,7 +2742,7 @@ QPDFWriter::Members::writeXRefStream( |
| 2746 | 2742 | } |
| 2747 | 2743 | |
| 2748 | 2744 | size_t |
| 2749 | -QPDFWriter::Members::calculateXrefStreamPadding(qpdf_offset_t xref_bytes) | |
| 2745 | +impl::Writer::calculateXrefStreamPadding(qpdf_offset_t xref_bytes) | |
| 2750 | 2746 | { |
| 2751 | 2747 | // This routine is called right after a linearization first pass xref stream has been written |
| 2752 | 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 | 2754 | } |
| 2759 | 2755 | |
| 2760 | 2756 | void |
| 2761 | -QPDFWriter::Members::writeLinearized() | |
| 2757 | +impl::Writer::writeLinearized() | |
| 2762 | 2758 | { |
| 2763 | 2759 | // Optimize file and enqueue objects in order |
| 2764 | 2760 | |
| ... | ... | @@ -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 | 2773 | std::vector<QPDFObjectHandle> part4; |
| 2778 | 2774 | std::vector<QPDFObjectHandle> part6; |
| 2779 | 2775 | std::vector<QPDFObjectHandle> part7; |
| 2780 | 2776 | std::vector<QPDFObjectHandle> part8; |
| 2781 | 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 | 2780 | // Object number sequence: |
| 2785 | 2781 | // |
| ... | ... | @@ -2871,7 +2867,7 @@ QPDFWriter::Members::writeLinearized() |
| 2871 | 2867 | enqueuePart(part8); |
| 2872 | 2868 | enqueuePart(part9); |
| 2873 | 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 | 2873 | qpdf_offset_t hint_length = 0; |
| ... | ... | @@ -2884,15 +2880,15 @@ QPDFWriter::Members::writeLinearized() |
| 2884 | 2880 | auto pp_md5 = pipeline_stack.popper(); |
| 2885 | 2881 | for (int pass: {1, 2}) { |
| 2886 | 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 | 2885 | pipeline_stack.activate( |
| 2890 | 2886 | pp_pass1, |
| 2891 | 2887 | std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file)); |
| 2892 | 2888 | } else { |
| 2893 | 2889 | pipeline_stack.activate(pp_pass1, true); |
| 2894 | 2890 | } |
| 2895 | - if (deterministic_id) { | |
| 2891 | + if (cfg.deterministic_id()) { | |
| 2896 | 2892 | pipeline_stack.activate_md5(pp_md5); |
| 2897 | 2893 | } |
| 2898 | 2894 | } |
| ... | ... | @@ -2927,7 +2923,7 @@ QPDFWriter::Members::writeLinearized() |
| 2927 | 2923 | |
| 2928 | 2924 | // If the user supplied any additional header text, write it here after the linearization |
| 2929 | 2925 | // parameter dictionary. |
| 2930 | - write(extra_header_text); | |
| 2926 | + write(cfg.extra_header_text()); | |
| 2931 | 2927 | |
| 2932 | 2928 | // Part 3: first page cross reference table and trailer. |
| 2933 | 2929 | |
| ... | ... | @@ -3062,7 +3058,7 @@ QPDFWriter::Members::writeLinearized() |
| 3062 | 3058 | write("startxref\n").write(first_xref_offset).write("\n%%EOF\n"); |
| 3063 | 3059 | |
| 3064 | 3060 | if (pass == 1) { |
| 3065 | - if (deterministic_id) { | |
| 3061 | + if (cfg.deterministic_id()) { | |
| 3066 | 3062 | QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1); |
| 3067 | 3063 | computeDeterministicIDData(); |
| 3068 | 3064 | pp_md5.pop(); |
| ... | ... | @@ -3105,9 +3101,9 @@ QPDFWriter::Members::writeLinearized() |
| 3105 | 3101 | } |
| 3106 | 3102 | |
| 3107 | 3103 | void |
| 3108 | -QPDFWriter::Members::enqueueObjectsStandard() | |
| 3104 | +impl::Writer::enqueueObjectsStandard() | |
| 3109 | 3105 | { |
| 3110 | - if (preserve_unreferenced_objects) { | |
| 3106 | + if (cfg.preserve_unreferenced()) { | |
| 3111 | 3107 | for (auto const& oh: qpdf.getAllObjects()) { |
| 3112 | 3108 | enqueue(oh); |
| 3113 | 3109 | } |
| ... | ... | @@ -3127,7 +3123,7 @@ QPDFWriter::Members::enqueueObjectsStandard() |
| 3127 | 3123 | } |
| 3128 | 3124 | |
| 3129 | 3125 | void |
| 3130 | -QPDFWriter::Members::enqueueObjectsPCLm() | |
| 3126 | +impl::Writer::enqueueObjectsPCLm() | |
| 3131 | 3127 | { |
| 3132 | 3128 | // Image transform stream content for page strip images. Each of this new stream has to come |
| 3133 | 3129 | // after every page image strip written in the pclm file. |
| ... | ... | @@ -3151,7 +3147,7 @@ QPDFWriter::Members::enqueueObjectsPCLm() |
| 3151 | 3147 | } |
| 3152 | 3148 | |
| 3153 | 3149 | void |
| 3154 | -QPDFWriter::Members::indicateProgress(bool decrement, bool finished) | |
| 3150 | +impl::Writer::indicateProgress(bool decrement, bool finished) | |
| 3155 | 3151 | { |
| 3156 | 3152 | if (decrement) { |
| 3157 | 3153 | --events_seen; |
| ... | ... | @@ -3185,19 +3181,19 @@ QPDFWriter::registerProgressReporter(std::shared_ptr<ProgressReporter> pr) |
| 3185 | 3181 | } |
| 3186 | 3182 | |
| 3187 | 3183 | void |
| 3188 | -QPDFWriter::Members::writeStandard() | |
| 3184 | +impl::Writer::writeStandard() | |
| 3189 | 3185 | { |
| 3190 | 3186 | auto pp_md5 = pipeline_stack.popper(); |
| 3191 | - if (deterministic_id) { | |
| 3187 | + if (cfg.deterministic_id()) { | |
| 3192 | 3188 | pipeline_stack.activate_md5(pp_md5); |
| 3193 | 3189 | } |
| 3194 | 3190 | |
| 3195 | 3191 | // Start writing |
| 3196 | 3192 | |
| 3197 | 3193 | writeHeader(); |
| 3198 | - write(extra_header_text); | |
| 3194 | + write(cfg.extra_header_text()); | |
| 3199 | 3195 | |
| 3200 | - if (pclm) { | |
| 3196 | + if (cfg.pclm()) { | |
| 3201 | 3197 | enqueueObjectsPCLm(); |
| 3202 | 3198 | } else { |
| 3203 | 3199 | enqueueObjectsStandard(); |
| ... | ... | @@ -3227,7 +3223,7 @@ QPDFWriter::Members::writeStandard() |
| 3227 | 3223 | } |
| 3228 | 3224 | write("startxref\n").write(xref_offset).write("\n%%EOF\n"); |
| 3229 | 3225 | |
| 3230 | - if (deterministic_id) { | |
| 3226 | + if (cfg.deterministic_id()) { | |
| 3231 | 3227 | QTC::TC( |
| 3232 | 3228 | "qpdf", |
| 3233 | 3229 | "QPDFWriter standard deterministic ID", | ... | ... |
libqpdf/QPDF_linearization.cc
libqpdf/QPDF_objects.cc
| ... | ... | @@ -1916,7 +1916,7 @@ QPDF::swapObjects(QPDFObjGen og1, QPDFObjGen og2) |
| 1916 | 1916 | } |
| 1917 | 1917 | |
| 1918 | 1918 | size_t |
| 1919 | -Objects::tableSize() | |
| 1919 | +Objects::table_size() | |
| 1920 | 1920 | { |
| 1921 | 1921 | // If obj_cache is dense, accommodate all object in tables,else accommodate only original |
| 1922 | 1922 | // objects. |
| ... | ... | @@ -1936,20 +1936,20 @@ Objects::tableSize() |
| 1936 | 1936 | } |
| 1937 | 1937 | |
| 1938 | 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 | 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 | 1950 | template <typename T> |
| 1951 | 1951 | std::vector<T> |
| 1952 | -Objects::getCompressibleObjGens() | |
| 1952 | +Objects::compressible() | |
| 1953 | 1953 | { |
| 1954 | 1954 | // Return a list of objects that are allowed to be in object streams. Walk through the objects |
| 1955 | 1955 | // by traversing the document from the root, including a traversal of the pages tree. This |
| ... | ... | @@ -1969,10 +1969,9 @@ Objects::getCompressibleObjGens() |
| 1969 | 1969 | std::vector<T> result; |
| 1970 | 1970 | if constexpr (std::is_same_v<T, QPDFObjGen>) { |
| 1971 | 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 | 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 | 1976 | while (!queue.empty()) { |
| 1978 | 1977 | auto obj = queue.back(); | ... | ... |
libqpdf/qpdf/ObjTable.hh
libqpdf/qpdf/QPDFJob_private.hh
| ... | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | |
| 6 | 6 | #include <qpdf/ClosedFileInputSource.hh> |
| 7 | 7 | #include <qpdf/QPDFLogger.hh> |
| 8 | +#include <qpdf/QPDFWriter_private.hh> | |
| 8 | 9 | #include <qpdf/QPDF_private.hh> |
| 9 | 10 | |
| 10 | 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 | 151 | |
| 151 | 152 | public: |
| 152 | 153 | Members(QPDFJob& job) : |
| 153 | - log(qcf.log()), | |
| 154 | + log(d_cfg.log()), | |
| 154 | 155 | inputs(job) |
| 155 | 156 | { |
| 156 | 157 | } |
| ... | ... | @@ -168,14 +169,14 @@ class QPDFJob::Members |
| 168 | 169 | static int constexpr DEFAULT_OI_MIN_AREA = 16384; |
| 169 | 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 | 174 | std::shared_ptr<QPDFLogger> log; |
| 173 | 175 | std::string message_prefix{"qpdf"}; |
| 174 | 176 | bool warnings{false}; |
| 175 | 177 | unsigned long encryption_status{0}; |
| 176 | 178 | bool verbose{false}; |
| 177 | 179 | std::string password; |
| 178 | - bool linearize{false}; | |
| 179 | 180 | bool decrypt{false}; |
| 180 | 181 | bool remove_restrictions{false}; |
| 181 | 182 | int split_pages{0}; |
| ... | ... | @@ -206,25 +207,9 @@ class QPDFJob::Members |
| 206 | 207 | bool force_R5{false}; |
| 207 | 208 | bool cleartext_metadata{false}; |
| 208 | 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 | 210 | int compression_level{-1}; |
| 216 | 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 | 212 | remove_unref_e remove_unreferenced_page_resources{re_auto}; |
| 226 | - bool newline_before_endstream{false}; | |
| 227 | - std::string linearize_pass1; | |
| 228 | 213 | bool coalesce_contents{false}; |
| 229 | 214 | bool flatten_annotations{false}; |
| 230 | 215 | int flatten_annotations_required{0}; |
| ... | ... | @@ -234,10 +219,7 @@ class QPDFJob::Members |
| 234 | 219 | std::string min_version; |
| 235 | 220 | std::string force_version; |
| 236 | 221 | bool show_npages{false}; |
| 237 | - bool deterministic_id{false}; | |
| 238 | - bool static_id{false}; | |
| 239 | 222 | bool static_aes_iv{false}; |
| 240 | - bool suppress_original_object_id{false}; | |
| 241 | 223 | bool show_encryption{false}; |
| 242 | 224 | bool show_encryption_key{false}; |
| 243 | 225 | bool check_linearization{false}; | ... | ... |
libqpdf/qpdf/QPDFObject_private.hh
| ... | ... | @@ -30,6 +30,11 @@ namespace qpdf |
| 30 | 30 | class Integer; |
| 31 | 31 | class Name; |
| 32 | 32 | class Stream; |
| 33 | + | |
| 34 | + namespace impl | |
| 35 | + { | |
| 36 | + class Writer; | |
| 37 | + } | |
| 33 | 38 | } // namespace qpdf |
| 34 | 39 | |
| 35 | 40 | class QPDF_Array final |
| ... | ... | @@ -256,7 +261,7 @@ class QPDF_String final |
| 256 | 261 | { |
| 257 | 262 | friend class QPDFObject; |
| 258 | 263 | friend class qpdf::BaseHandle; |
| 259 | - friend class QPDFWriter; | |
| 264 | + friend class qpdf::impl::Writer; | |
| 260 | 265 | |
| 261 | 266 | public: |
| 262 | 267 | static std::shared_ptr<QPDFObject> create_utf16(std::string const& utf8_val); | ... | ... |
libqpdf/qpdf/QPDFWriter_private.hh
| ... | ... | @@ -5,10 +5,332 @@ |
| 5 | 5 | |
| 6 | 6 | #include <qpdf/ObjTable.hh> |
| 7 | 7 | #include <qpdf/Pipeline_private.hh> |
| 8 | +#include <qpdf/QPDF.hh> | |
| 9 | +#include <qpdf/QPDFUsage.hh> | |
| 8 | 10 | |
| 9 | 11 | // This file is intended for inclusion by QPDFWriter, QPDF, QPDF_optimization and QPDF_linearization |
| 10 | 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 | 334 | struct QPDFWriter::Object |
| 13 | 335 | { |
| 14 | 336 | int renumber{0}; |
| ... | ... | @@ -24,7 +346,7 @@ struct QPDFWriter::NewObject |
| 24 | 346 | |
| 25 | 347 | class QPDFWriter::ObjTable: public ::ObjTable<QPDFWriter::Object> |
| 26 | 348 | { |
| 27 | - friend class QPDFWriter; | |
| 349 | + friend class qpdf::impl::Writer; | |
| 28 | 350 | |
| 29 | 351 | public: |
| 30 | 352 | bool | ... | ... |
libqpdf/qpdf/QPDF_private.hh
| ... | ... | @@ -22,6 +22,11 @@ namespace qpdf |
| 22 | 22 | class OffsetBuffer; |
| 23 | 23 | } // namespace is |
| 24 | 24 | |
| 25 | + namespace impl | |
| 26 | + { | |
| 27 | + using Doc = QPDF::Doc; | |
| 28 | + } | |
| 29 | + | |
| 25 | 30 | class Doc: public QPDF |
| 26 | 31 | { |
| 27 | 32 | public: |
| ... | ... | @@ -271,7 +276,6 @@ class QPDF::Doc |
| 271 | 276 | class Pages; |
| 272 | 277 | class ParseGuard; |
| 273 | 278 | class Resolver; |
| 274 | - class Writer; | |
| 275 | 279 | |
| 276 | 280 | // This is the common base-class for all document components. It is used by the other document |
| 277 | 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 | 290 | ~Common() = default; |
| 287 | 291 | |
| 288 | 292 | inline Common(QPDF& qpdf, QPDF::Members* m); |
| 293 | + inline Common(Doc& doc); | |
| 289 | 294 | |
| 290 | 295 | void stopOnError(std::string const& message); |
| 291 | 296 | void warn(QPDFExc const& e); |
| ... | ... | @@ -546,7 +551,7 @@ class QPDF::Doc::Linearization: Common |
| 546 | 551 | ~Linearization() = default; |
| 547 | 552 | |
| 548 | 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 | 572 | |
| 568 | 573 | // Get lists of all objects in order according to the part of a linearized file that they |
| 569 | 574 | // belong to. |
| 570 | - void getLinearizedParts( | |
| 575 | + void parts( | |
| 571 | 576 | QPDFWriter::ObjTable const& obj, |
| 572 | 577 | std::vector<QPDFObjectHandle>& part4, |
| 573 | 578 | std::vector<QPDFObjectHandle>& part6, |
| ... | ... | @@ -940,7 +945,7 @@ class QPDF::Doc::Objects: Common |
| 940 | 945 | ~Objects() = default; |
| 941 | 946 | |
| 942 | 947 | Objects(Doc& doc) : |
| 943 | - Common(doc.qpdf, doc.m), | |
| 948 | + Common(doc), | |
| 944 | 949 | foreign_(*this), |
| 945 | 950 | streams_(*this) |
| 946 | 951 | { |
| ... | ... | @@ -987,18 +992,19 @@ class QPDF::Doc::Objects: Common |
| 987 | 992 | QPDFObjectHandle makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj); |
| 988 | 993 | std::shared_ptr<QPDFObject> getObjectForParser(int id, int gen, bool parse_pdf); |
| 989 | 994 | std::shared_ptr<QPDFObject> getObjectForJSON(int id, int gen); |
| 990 | - size_t tableSize(); | |
| 995 | + size_t table_size(); | |
| 991 | 996 | |
| 992 | 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 | 1004 | // Get a list of objects that would be permitted in an object stream. |
| 996 | 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 | 1008 | void setTrailer(QPDFObjectHandle obj); |
| 1003 | 1009 | void reconstruct_xref(QPDFExc& e, bool found_startxref = true); |
| 1004 | 1010 | void read_xref(qpdf_offset_t offset, bool in_stream_recovery = false); |
| ... | ... | @@ -1060,7 +1066,7 @@ class QPDF::Doc::Pages: Common |
| 1060 | 1066 | ~Pages() = default; |
| 1061 | 1067 | |
| 1062 | 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& 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 | 1228 | inline QPDF::Doc::Linearization& |
| 1218 | 1229 | QPDF::Doc::linearization() |
| 1219 | 1230 | { |
| ... | ... | @@ -1245,7 +1256,7 @@ QPDF::doc() |
| 1245 | 1256 | } |
| 1246 | 1257 | |
| 1247 | 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 | 46 | QPDF ignoring XRefStm in trailer 0 |
| 47 | 47 | SF_FlateLzwDecode PNG filter 0 |
| 48 | 48 | QPDF xref /Index is array 1 |
| 49 | -QPDFWriter encrypt object stream 0 | |
| 50 | 49 | QPDF exclude indirect length 0 |
| 51 | 50 | QPDF exclude encryption dictionary 0 |
| 52 | 51 | QPDF_Stream pipeStreamData with null pipeline 0 | ... | ... |