Commit bd89aac36020f3a289c1624f2e00ec3d3d932a49

Authored by Jay Berkenbilt
1 parent 23b64f83

QPDFJob increment: move arg parsing into QPDFJob

Move ArgParser from qpdf.cc into QPDFJob.cc. It still works with
millions of public member variables, but now qpdf.cc is minimal and
just calls stable library functions.
include/qpdf/QPDFArgParser.hh
@@ -73,6 +73,11 @@ class QPDFArgParser @@ -73,6 +73,11 @@ class QPDFArgParser
73 QPDF_DLL 73 QPDF_DLL
74 void parseArgs(); 74 void parseArgs();
75 75
  76 + // Return the program name as the last path element of the program
  77 + // executable.
  78 + QPDF_DLL
  79 + std::string getProgname();
  80 +
76 // Methods for registering arguments. QPDFArgParser starts off 81 // Methods for registering arguments. QPDFArgParser starts off
77 // with the main option table selected. You can add handlers for 82 // with the main option table selected. You can add handlers for
78 // arguments in the current option table, and you can select which 83 // arguments in the current option table, and you can select which
include/qpdf/QPDFJob.hh
@@ -27,6 +27,7 @@ @@ -27,6 +27,7 @@
27 #include <qpdf/PointerHolder.hh> 27 #include <qpdf/PointerHolder.hh>
28 #include <qpdf/QPDF.hh> 28 #include <qpdf/QPDF.hh>
29 #include <qpdf/QPDFPageObjectHelper.hh> 29 #include <qpdf/QPDFPageObjectHelper.hh>
  30 +#include <qpdf/QPDFArgParser.hh>
30 31
31 #include <string> 32 #include <string>
32 #include <list> 33 #include <list>
@@ -44,6 +45,29 @@ class QPDFJob @@ -44,6 +45,29 @@ class QPDFJob
44 QPDF_DLL 45 QPDF_DLL
45 QPDFJob(); 46 QPDFJob();
46 47
  48 + // Initialize a QPDFJob object from argv. The progname_env
  49 + // argument is the name of an environment variable which, if set,
  50 + // overrides the name of the executable for purposes of generating
  51 + // the --completion options. See QPDFArgParser for details. If a
  52 + // null pointer is passed in, the default value of
  53 + // "QPDF_EXECUTABLE" is used. This is used by the QPDF cli, which
  54 + // just initializes a QPDFJob from argv, calls run(), and handles
  55 + // errors and exit status issues. You can perform much of the cli
  56 + // functionality programmatically in this way rather than using
  57 + // the regular API. This is exposed in the C API, which makes it
  58 + // easier to get certain high-level qpdf functionality from other
  59 + // languages. If there are any command-line errors, this method
  60 + // will throw QPDFArgParser::Usage which is derived from
  61 + // std::runtime_error. Other exceptions may be thrown in some
  62 + // cases. Note that argc, and argv should be UTF-8 encoded. If you
  63 + // are calling this from a Windows Unicode-aware main (wmain), see
  64 + // QUtil::call_main_from_wmain for information about converting
  65 + // arguments to UTF-8. This method will mutate arguments that are
  66 + // passed to it.
  67 + QPDF_DLL
  68 + void initializeFromArgv(int argc, char* argv[],
  69 + char const* progname_env = nullptr);
  70 +
47 // Set name that is used to prefix verbose messages, progress 71 // Set name that is used to prefix verbose messages, progress
48 // messages, and other things that the library writes to output 72 // messages, and other things that the library writes to output
49 // and error streams on the caller's behalf. Defaults to "qpdf". 73 // and error streams on the caller's behalf. Defaults to "qpdf".
@@ -74,6 +98,9 @@ class QPDFJob @@ -74,6 +98,9 @@ class QPDFJob
74 bool suppressWarnings(); 98 bool suppressWarnings();
75 99
76 QPDF_DLL 100 QPDF_DLL
  101 + bool warningsExitZero();
  102 +
  103 + QPDF_DLL
77 bool checkRequiresPassword(); 104 bool checkRequiresPassword();
78 105
79 QPDF_DLL 106 QPDF_DLL
@@ -180,6 +207,7 @@ class QPDFJob @@ -180,6 +207,7 @@ class QPDFJob
180 bool verbose; 207 bool verbose;
181 bool progress; 208 bool progress;
182 bool suppress_warnings; 209 bool suppress_warnings;
  210 + bool warnings_exit_zero;
183 bool copy_encryption; 211 bool copy_encryption;
184 char const* encryption_file; 212 char const* encryption_file;
185 char const* encryption_file_password; 213 char const* encryption_file_password;
@@ -367,6 +395,7 @@ class QPDFJob @@ -367,6 +395,7 @@ class QPDFJob
367 std::ostream* cout; 395 std::ostream* cout;
368 std::ostream* cerr; 396 std::ostream* cerr;
369 unsigned long encryption_status; 397 unsigned long encryption_status;
  398 + PointerHolder<QPDFArgParser> ap;
370 }; 399 };
371 PointerHolder<Members> m; 400 PointerHolder<Members> m;
372 }; 401 };
libqpdf/QPDFArgParser.cc
@@ -662,6 +662,12 @@ QPDFArgParser::parseArgs() @@ -662,6 +662,12 @@ QPDFArgParser::parseArgs()
662 } 662 }
663 } 663 }
664 664
  665 +std::string
  666 +QPDFArgParser::getProgname()
  667 +{
  668 + return this->m->whoami;
  669 +}
  670 +
665 void 671 void
666 QPDFArgParser::doFinalChecks() 672 QPDFArgParser::doFinalChecks()
667 { 673 {
libqpdf/QPDFJob.cc
@@ -326,6 +326,7 @@ QPDFJob::QPDFJob() : @@ -326,6 +326,7 @@ QPDFJob::QPDFJob() :
326 verbose(false), 326 verbose(false),
327 progress(false), 327 progress(false),
328 suppress_warnings(false), 328 suppress_warnings(false),
  329 + warnings_exit_zero(false),
329 copy_encryption(false), 330 copy_encryption(false),
330 encryption_file(0), 331 encryption_file(0),
331 encryption_file_password(0), 332 encryption_file_password(0),
@@ -529,6 +530,12 @@ QPDFJob::suppressWarnings() @@ -529,6 +530,12 @@ QPDFJob::suppressWarnings()
529 } 530 }
530 531
531 bool 532 bool
  533 +QPDFJob::warningsExitZero()
  534 +{
  535 + return this->warnings_exit_zero;
  536 +}
  537 +
  538 +bool
532 QPDFJob::checkRequiresPassword() 539 QPDFJob::checkRequiresPassword()
533 { 540 {
534 return this->check_requires_password; 541 return this->check_requires_password;
libqpdf/QPDFJob_argv.cc 0 โ†’ 100644
  1 +#include <qpdf/QPDFJob.hh>
  2 +
  3 +#include <iostream>
  4 +#include <string.h>
  5 +#include <stdlib.h>
  6 +#include <cstdio>
  7 +#include <ctype.h>
  8 +#include <memory>
  9 +
  10 +#include <qpdf/QUtil.hh>
  11 +#include <qpdf/QTC.hh>
  12 +#include <qpdf/QPDFCryptoProvider.hh>
  13 +#include <qpdf/QPDFArgParser.hh>
  14 +#include <qpdf/QPDFJob.hh>
  15 +#include <qpdf/QIntC.hh>
  16 +
  17 +namespace
  18 +{
  19 + class ArgParser
  20 + {
  21 + public:
  22 + ArgParser(QPDFArgParser& ap, QPDFJob& o);
  23 + void parseOptions();
  24 +
  25 + private:
  26 + static constexpr char const* O_PAGES = "pages";
  27 + static constexpr char const* O_ENCRYPT = "encryption";
  28 + static constexpr char const* O_ENCRYPT_40 = "40-bit encryption";
  29 + static constexpr char const* O_ENCRYPT_128 = "128-bit encryption";
  30 + static constexpr char const* O_ENCRYPT_256 = "256-bit encryption";
  31 + static constexpr char const* O_UNDER_OVERLAY = "underlay/overlay";
  32 + static constexpr char const* O_ATTACHMENT = "attachment";
  33 + static constexpr char const* O_COPY_ATTACHMENT = "copy attachment";
  34 +
  35 + void argHelp();
  36 + void argVersion();
  37 + void argCopyright();
  38 + void argJsonHelp();
  39 + void argShowCrypto();
  40 + void argPositional(char* arg);
  41 + void argPassword(char* parameter);
  42 + void argPasswordFile(char* parameter);
  43 + void argEmpty();
  44 + void argLinearize();
  45 + void argEncrypt();
  46 + void argDecrypt();
  47 + void argPasswordIsHexKey();
  48 + void argAllowInsecure();
  49 + void argAllowWeakCrypto();
  50 + void argPasswordMode(char* parameter);
  51 + void argSuppressPasswordRecovery();
  52 + void argCopyEncryption(char* parameter);
  53 + void argEncryptionFilePassword(char* parameter);
  54 + void argPages();
  55 + void argPagesPassword(char* parameter);
  56 + void argPagesPositional(char* parameter);
  57 + void argEndPages();
  58 + void argUnderlay();
  59 + void argOverlay();
  60 + void argRotate(char* parameter);
  61 + void argCollate(char* parameter);
  62 + void argFlattenRotation();
  63 + void argListAttachments();
  64 + void argShowAttachment(char* parameter);
  65 + void argRemoveAttachment(char* parameter);
  66 + void argAddAttachment();
  67 + void argCopyAttachments();
  68 + void argStreamData(char* parameter);
  69 + void argCompressStreams(char* parameter);
  70 + void argRecompressFlate();
  71 + void argCompressionLevel(char* parameter);
  72 + void argDecodeLevel(char* parameter);
  73 + void argNormalizeContent(char* parameter);
  74 + void argSuppressRecovery();
  75 + void argObjectStreams(char* parameter);
  76 + void argIgnoreXrefStreams();
  77 + void argQdf();
  78 + void argPreserveUnreferenced();
  79 + void argPreserveUnreferencedResources();
  80 + void argRemoveUnreferencedResources(char* parameter);
  81 + void argKeepFilesOpen(char* parameter);
  82 + void argKeepFilesOpenThreshold(char* parameter);
  83 + void argNewlineBeforeEndstream();
  84 + void argLinearizePass1(char* parameter);
  85 + void argCoalesceContents();
  86 + void argFlattenAnnotations(char* parameter);
  87 + void argGenerateAppearances();
  88 + void argMinVersion(char* parameter);
  89 + void argForceVersion(char* parameter);
  90 + void argSplitPages(char* parameter);
  91 + void argVerbose();
  92 + void argProgress();
  93 + void argNoWarn();
  94 + void argWarningExitZero();
  95 + void argDeterministicId();
  96 + void argStaticId();
  97 + void argStaticAesIv();
  98 + void argNoOriginalObjectIds();
  99 + void argShowEncryption();
  100 + void argShowEncryptionKey();
  101 + void argCheckLinearization();
  102 + void argShowLinearization();
  103 + void argShowXref();
  104 + void argShowObject(char* parameter);
  105 + void argRawStreamData();
  106 + void argFilteredStreamData();
  107 + void argShowNpages();
  108 + void argShowPages();
  109 + void argWithImages();
  110 + void argJson();
  111 + void argJsonKey(char* parameter);
  112 + void argJsonObject(char* parameter);
  113 + void argCheck();
  114 + void argOptimizeImages();
  115 + void argExternalizeInlineImages();
  116 + void argKeepInlineImages();
  117 + void argRemovePageLabels();
  118 + void argOiMinWidth(char* parameter);
  119 + void argOiMinHeight(char* parameter);
  120 + void argOiMinArea(char* parameter);
  121 + void argIiMinBytes(char* parameter);
  122 + void arg40Print(char* parameter);
  123 + void arg40Modify(char* parameter);
  124 + void arg40Extract(char* parameter);
  125 + void arg40Annotate(char* parameter);
  126 + void arg128Accessibility(char* parameter);
  127 + void arg128Extract(char* parameter);
  128 + void arg128Print(char* parameter);
  129 + void arg128Modify(char* parameter);
  130 + void arg128ClearTextMetadata();
  131 + void arg128Assemble(char* parameter);
  132 + void arg128Annotate(char* parameter);
  133 + void arg128Form(char* parameter);
  134 + void arg128ModOther(char* parameter);
  135 + void arg128UseAes(char* parameter);
  136 + void arg128ForceV4();
  137 + void arg256ForceR5();
  138 + void argEncryptPositional(char* arg);
  139 + void argEndEncrypt();
  140 + void argUOpositional(char* arg);
  141 + void argUOto(char* parameter);
  142 + void argUOfrom(char* parameter);
  143 + void argUOrepeat(char* parameter);
  144 + void argUOpassword(char* parameter);
  145 + void argEndUnderOverlay();
  146 + void argReplaceInput();
  147 + void argIsEncrypted();
  148 + void argRequiresPassword();
  149 + void argAApositional(char* arg);
  150 + void argAAKey(char* parameter);
  151 + void argAAFilename(char* parameter);
  152 + void argAACreationDate(char* parameter);
  153 + void argAAModDate(char* parameter);
  154 + void argAAMimeType(char* parameter);
  155 + void argAADescription(char* parameter);
  156 + void argAAReplace();
  157 + void argEndAddAttachment();
  158 + void argCApositional(char* arg);
  159 + void argCAprefix(char* parameter);
  160 + void argCApassword(char* parameter);
  161 + void argEndCopyAttachments();
  162 +
  163 + void usage(std::string const& message);
  164 + void initOptionTable();
  165 + void doFinalChecks();
  166 + void parseUnderOverlayOptions(QPDFJob::UnderOverlay*);
  167 + void parseRotationParameter(std::string const&);
  168 + std::vector<int> parseNumrange(char const* range, int max,
  169 + bool throw_error = false);
  170 +
  171 + QPDFArgParser ap;
  172 + QPDFJob& o;
  173 + std::vector<char*> accumulated_args; // points to member in ap
  174 + char* pages_password;
  175 + };
  176 +}
  177 +
  178 +ArgParser::ArgParser(QPDFArgParser& ap, QPDFJob& o) :
  179 + ap(ap),
  180 + o(o),
  181 + pages_password(nullptr)
  182 +{
  183 + initOptionTable();
  184 +}
  185 +
  186 +void
  187 +ArgParser::initOptionTable()
  188 +{
  189 + auto b = [this](void (ArgParser::*f)()) {
  190 + return QPDFArgParser::bindBare(f, this);
  191 + };
  192 + auto p = [this](void (ArgParser::*f)(char *)) {
  193 + return QPDFArgParser::bindParam(f, this);
  194 + };
  195 +
  196 + this->ap.addFinalCheck(b(&ArgParser::doFinalChecks));
  197 +
  198 + this->ap.selectHelpOptionTable();
  199 + this->ap.addBare("help", b(&ArgParser::argHelp));
  200 + this->ap.addBare("version", b(&ArgParser::argVersion));
  201 + this->ap.addBare("copyright", b(&ArgParser::argCopyright));
  202 + this->ap.addBare("json-help", b(&ArgParser::argJsonHelp));
  203 + this->ap.addBare("show-crypto", b(&ArgParser::argShowCrypto));
  204 +
  205 + this->ap.selectMainOptionTable();
  206 + char const* yn[] = {"y", "n", 0};
  207 + this->ap.addPositional(p(&ArgParser::argPositional));
  208 + this->ap.addRequiredParameter("password",
  209 + p(&ArgParser::argPassword), "password");
  210 + this->ap.addRequiredParameter("password-file",
  211 + p(&ArgParser::argPasswordFile), "password-file");
  212 + this->ap.addBare("empty", b(&ArgParser::argEmpty));
  213 + this->ap.addBare("linearize", b(&ArgParser::argLinearize));
  214 + this->ap.addBare("decrypt", b(&ArgParser::argDecrypt));
  215 + this->ap.addBare("password-is-hex-key", b(&ArgParser::argPasswordIsHexKey));
  216 + this->ap.addBare("suppress-password-recovery",
  217 + b(&ArgParser::argSuppressPasswordRecovery));
  218 + char const* password_mode_choices[] =
  219 + {"bytes", "hex-bytes", "unicode", "auto", 0};
  220 + this->ap.addRequiredChoices("password-mode",
  221 + p(&ArgParser::argPasswordMode), password_mode_choices);
  222 + this->ap.addRequiredParameter("copy-encryption",
  223 + p(&ArgParser::argCopyEncryption), "file");
  224 + this->ap.addRequiredParameter("encryption-file-password",
  225 + p(&ArgParser::argEncryptionFilePassword), "password");
  226 + this->ap.addRequiredParameter("rotate",
  227 + p(&ArgParser::argRotate), "[+|-]angle:page-range");
  228 + char const* stream_data_choices[] =
  229 + {"compress", "preserve", "uncompress", 0};
  230 + this->ap.addOptionalParameter("collate",p(&ArgParser::argCollate));
  231 + this->ap.addBare("flatten-rotation", b(&ArgParser::argFlattenRotation));
  232 + this->ap.addBare("list-attachments", b(&ArgParser::argListAttachments));
  233 + this->ap.addRequiredParameter("show-attachment",
  234 + p(&ArgParser::argShowAttachment), "attachment-key");
  235 + this->ap.addRequiredParameter("remove-attachment",
  236 + p(&ArgParser::argRemoveAttachment), "attachment-key");
  237 + this->ap.addBare("add-attachment", b(&ArgParser::argAddAttachment));
  238 + this->ap.addBare(
  239 + "copy-attachments-from", b(&ArgParser::argCopyAttachments));
  240 + this->ap.addRequiredChoices("stream-data",
  241 + p(&ArgParser::argStreamData), stream_data_choices);
  242 + this->ap.addRequiredChoices("compress-streams",
  243 + p(&ArgParser::argCompressStreams), yn);
  244 + this->ap.addBare("recompress-flate", b(&ArgParser::argRecompressFlate));
  245 + this->ap.addRequiredParameter("compression-level",
  246 + p(&ArgParser::argCompressionLevel), "level");
  247 + char const* decode_level_choices[] =
  248 + {"none", "generalized", "specialized", "all", 0};
  249 + this->ap.addRequiredChoices("decode-level",
  250 + p(&ArgParser::argDecodeLevel), decode_level_choices);
  251 + this->ap.addRequiredChoices("normalize-content",
  252 + p(&ArgParser::argNormalizeContent), yn);
  253 + this->ap.addBare("suppress-recovery", b(&ArgParser::argSuppressRecovery));
  254 + char const* object_streams_choices[] = {
  255 + "disable", "preserve", "generate", 0};
  256 + this->ap.addRequiredChoices("object-streams",
  257 + p(&ArgParser::argObjectStreams), object_streams_choices);
  258 + this->ap.addBare(
  259 + "ignore-xref-streams", b(&ArgParser::argIgnoreXrefStreams));
  260 + this->ap.addBare("qdf", b(&ArgParser::argQdf));
  261 + this->ap.addBare(
  262 + "preserve-unreferenced", b(&ArgParser::argPreserveUnreferenced));
  263 + this->ap.addBare(
  264 + "preserve-unreferenced-resources",
  265 + b(&ArgParser::argPreserveUnreferencedResources));
  266 + char const* remove_unref_choices[] = {
  267 + "auto", "yes", "no", 0};
  268 + this->ap.addRequiredChoices("remove-unreferenced-resources",
  269 + p(&ArgParser::argRemoveUnreferencedResources), remove_unref_choices);
  270 + this->ap.addRequiredChoices("keep-files-open",
  271 + p(&ArgParser::argKeepFilesOpen), yn);
  272 + this->ap.addRequiredParameter("keep-files-open-threshold",
  273 + p(&ArgParser::argKeepFilesOpenThreshold), "count");
  274 + this->ap.addBare("newline-before-endstream", b(&ArgParser::argNewlineBeforeEndstream));
  275 + this->ap.addRequiredParameter("linearize-pass1",
  276 + p(&ArgParser::argLinearizePass1), "filename");
  277 + this->ap.addBare("coalesce-contents", b(&ArgParser::argCoalesceContents));
  278 + char const* flatten_choices[] = {"all", "print", "screen", 0};
  279 + this->ap.addRequiredChoices("flatten-annotations",
  280 + p(&ArgParser::argFlattenAnnotations), flatten_choices);
  281 + this->ap.addBare("generate-appearances", b(&ArgParser::argGenerateAppearances));
  282 + this->ap.addRequiredParameter("min-version",
  283 + p(&ArgParser::argMinVersion), "version");
  284 + this->ap.addRequiredParameter("force-version",
  285 + p(&ArgParser::argForceVersion), "version");
  286 + this->ap.addOptionalParameter("split-pages",p(&ArgParser::argSplitPages));
  287 + this->ap.addBare("verbose", b(&ArgParser::argVerbose));
  288 + this->ap.addBare("progress", b(&ArgParser::argProgress));
  289 + this->ap.addBare("no-warn", b(&ArgParser::argNoWarn));
  290 + this->ap.addBare("warning-exit-0", b(&ArgParser::argWarningExitZero));
  291 + this->ap.addBare("deterministic-id", b(&ArgParser::argDeterministicId));
  292 + this->ap.addBare("static-id", b(&ArgParser::argStaticId));
  293 + this->ap.addBare("static-aes-iv", b(&ArgParser::argStaticAesIv));
  294 + this->ap.addBare("no-original-object-ids", b(&ArgParser::argNoOriginalObjectIds));
  295 + this->ap.addBare("show-encryption", b(&ArgParser::argShowEncryption));
  296 + this->ap.addBare("show-encryption-key", b(&ArgParser::argShowEncryptionKey));
  297 + this->ap.addBare("check-linearization", b(&ArgParser::argCheckLinearization));
  298 + this->ap.addBare("show-linearization", b(&ArgParser::argShowLinearization));
  299 + this->ap.addBare("show-xref", b(&ArgParser::argShowXref));
  300 + this->ap.addRequiredParameter("show-object",
  301 + p(&ArgParser::argShowObject), "trailer|obj[,gen]");
  302 + this->ap.addBare("raw-stream-data", b(&ArgParser::argRawStreamData));
  303 + this->ap.addBare("filtered-stream-data", b(&ArgParser::argFilteredStreamData));
  304 + this->ap.addBare("show-npages", b(&ArgParser::argShowNpages));
  305 + this->ap.addBare("show-pages", b(&ArgParser::argShowPages));
  306 + this->ap.addBare("with-images", b(&ArgParser::argWithImages));
  307 + this->ap.addBare("json", b(&ArgParser::argJson));
  308 + // QXXXQ
  309 + // The list of selectable top-level keys id duplicated in three
  310 + // places: json_schema, do_json, and initOptionTable.
  311 + char const* json_key_choices[] = {
  312 + "objects", "objectinfo", "pages", "pagelabels", "outlines",
  313 + "acroform", "encrypt", "attachments", 0};
  314 + this->ap.addRequiredChoices("json-key",
  315 + p(&ArgParser::argJsonKey), json_key_choices);
  316 + this->ap.addRequiredParameter("json-object",
  317 + p(&ArgParser::argJsonObject), "trailer|obj[,gen]");
  318 + this->ap.addBare("check", b(&ArgParser::argCheck));
  319 + this->ap.addBare("optimize-images", b(&ArgParser::argOptimizeImages));
  320 + this->ap.addBare("externalize-inline-images", b(&ArgParser::argExternalizeInlineImages));
  321 + this->ap.addBare("keep-inline-images", b(&ArgParser::argKeepInlineImages));
  322 + this->ap.addBare("remove-page-labels", b(&ArgParser::argRemovePageLabels));
  323 + this->ap.addRequiredParameter("oi-min-width",
  324 + p(&ArgParser::argOiMinWidth), "minimum-width");
  325 + this->ap.addRequiredParameter("oi-min-height",
  326 + p(&ArgParser::argOiMinHeight), "minimum-height");
  327 + this->ap.addRequiredParameter("oi-min-area",
  328 + p(&ArgParser::argOiMinArea), "minimum-area");
  329 + this->ap.addRequiredParameter("ii-min-bytes",
  330 + p(&ArgParser::argIiMinBytes), "minimum-bytes");
  331 + this->ap.addBare("overlay", b(&ArgParser::argOverlay));
  332 + this->ap.addBare("underlay", b(&ArgParser::argUnderlay));
  333 + this->ap.addBare("replace-input", b(&ArgParser::argReplaceInput));
  334 + this->ap.addBare("is-encrypted", b(&ArgParser::argIsEncrypted));
  335 + this->ap.addBare("requires-password", b(&ArgParser::argRequiresPassword));
  336 + this->ap.addBare("allow-weak-crypto", b(&ArgParser::argAllowWeakCrypto));
  337 +
  338 + this->ap.selectMainOptionTable();
  339 + this->ap.addBare("pages", b(&ArgParser::argPages));
  340 + this->ap.registerOptionTable(O_PAGES, b(&ArgParser::argEndPages));
  341 + this->ap.addRequiredParameter(
  342 + "password", p(&ArgParser::argPagesPassword), "password");
  343 + this->ap.addPositional(p(&ArgParser::argPagesPositional));
  344 +
  345 + this->ap.selectMainOptionTable();
  346 + this->ap.addBare("encrypt", b(&ArgParser::argEncrypt));
  347 + this->ap.registerOptionTable(O_ENCRYPT, b(&ArgParser::argEndEncrypt));
  348 + this->ap.addPositional(p(&ArgParser::argEncryptPositional));
  349 + this->ap.registerOptionTable(O_ENCRYPT_40, b(&ArgParser::argEndEncrypt));
  350 + this->ap.addRequiredChoices("extract",p(&ArgParser::arg40Extract), yn);
  351 + this->ap.addRequiredChoices("annotate",p(&ArgParser::arg40Annotate), yn);
  352 + this->ap.addRequiredChoices("print",p(&ArgParser::arg40Print), yn);
  353 + this->ap.addRequiredChoices("modify",p(&ArgParser::arg40Modify), yn);
  354 + this->ap.registerOptionTable(O_ENCRYPT_128, b(&ArgParser::argEndEncrypt));
  355 + this->ap.registerOptionTable(O_ENCRYPT_256, b(&ArgParser::argEndEncrypt));
  356 + for (char const* k: {O_ENCRYPT_128, O_ENCRYPT_256})
  357 + {
  358 + this->ap.selectOptionTable(k);
  359 + this->ap.addRequiredChoices("accessibility",
  360 + p(&ArgParser::arg128Accessibility), yn);
  361 + this->ap.addRequiredChoices("extract", p(&ArgParser::arg128Extract), yn);
  362 + char const* print128_choices[] = {"full", "low", "none", 0};
  363 + this->ap.addRequiredChoices("print",
  364 + p(&ArgParser::arg128Print), print128_choices);
  365 + this->ap.addRequiredChoices("assemble",p(&ArgParser::arg128Assemble), yn);
  366 + this->ap.addRequiredChoices("annotate",p(&ArgParser::arg128Annotate), yn);
  367 + this->ap.addRequiredChoices("form",p(&ArgParser::arg128Form), yn);
  368 + this->ap.addRequiredChoices("modify-other",p(&ArgParser::arg128ModOther), yn);
  369 + char const* modify128_choices[] =
  370 + {"all", "annotate", "form", "assembly", "none", 0};
  371 + this->ap.addRequiredChoices("modify",
  372 + p(&ArgParser::arg128Modify), modify128_choices);
  373 + this->ap.addBare("cleartext-metadata", b(&ArgParser::arg128ClearTextMetadata));
  374 + }
  375 +
  376 + this->ap.selectOptionTable(O_ENCRYPT_128);
  377 + this->ap.addRequiredChoices("use-aes",p(&ArgParser::arg128UseAes), yn);
  378 + this->ap.addBare("force-V4", b(&ArgParser::arg128ForceV4));
  379 +
  380 + this->ap.selectOptionTable(O_ENCRYPT_256);
  381 + this->ap.addBare("force-R5", b(&ArgParser::arg256ForceR5));
  382 + this->ap.addBare("allow-insecure", b(&ArgParser::argAllowInsecure));
  383 +
  384 + this->ap.registerOptionTable(O_UNDER_OVERLAY, b(&ArgParser::argEndUnderOverlay));
  385 + this->ap.addPositional(p(&ArgParser::argUOpositional));
  386 + this->ap.addRequiredParameter("to",
  387 + p(&ArgParser::argUOto), "page-range");
  388 + this->ap.addRequiredParameter("from",
  389 + p(&ArgParser::argUOfrom), "page-range");
  390 + this->ap.addRequiredParameter("repeat",
  391 + p(&ArgParser::argUOrepeat), "page-range");
  392 + this->ap.addRequiredParameter("password",
  393 + p(&ArgParser::argUOpassword), "password");
  394 +
  395 + this->ap.registerOptionTable(O_ATTACHMENT, b(&ArgParser::argEndAddAttachment));
  396 + this->ap.addPositional(p(&ArgParser::argAApositional));
  397 + this->ap.addRequiredParameter("key",
  398 + p(&ArgParser::argAAKey), "attachment-key");
  399 + this->ap.addRequiredParameter("filename",
  400 + p(&ArgParser::argAAFilename), "filename");
  401 + this->ap.addRequiredParameter("creationdate",
  402 + p(&ArgParser::argAACreationDate), "creation-date");
  403 + this->ap.addRequiredParameter("moddate",
  404 + p(&ArgParser::argAAModDate), "modification-date");
  405 + this->ap.addRequiredParameter("mimetype",
  406 + p(&ArgParser::argAAMimeType), "mime/type");
  407 + this->ap.addRequiredParameter("description",
  408 + p(&ArgParser::argAADescription), "description");
  409 + this->ap.addBare("replace", b(&ArgParser::argAAReplace));
  410 +
  411 + this->ap.registerOptionTable(O_COPY_ATTACHMENT, b(&ArgParser::argEndCopyAttachments));
  412 + this->ap.addPositional(p(&ArgParser::argCApositional));
  413 + this->ap.addRequiredParameter("prefix",
  414 + p(&ArgParser::argCAprefix), "prefix");
  415 + this->ap.addRequiredParameter("password",
  416 + p(&ArgParser::argCApassword), "password");
  417 +}
  418 +
  419 +void
  420 +ArgParser::argPositional(char* arg)
  421 +{
  422 + if (o.infilename == 0)
  423 + {
  424 + o.infilename = arg;
  425 + }
  426 + else if (o.outfilename == 0)
  427 + {
  428 + o.outfilename = arg;
  429 + }
  430 + else
  431 + {
  432 + usage(std::string("unknown argument ") + arg);
  433 + }
  434 +}
  435 +
  436 +void
  437 +ArgParser::argVersion()
  438 +{
  439 + auto whoami = this->ap.getProgname();
  440 + std::cout
  441 + << whoami << " version " << QPDF::QPDFVersion() << std::endl
  442 + << "Run " << whoami << " --copyright to see copyright and license information."
  443 + << std::endl;
  444 +}
  445 +
  446 +void
  447 +ArgParser::argCopyright()
  448 +{
  449 + // Make sure the output looks right on an 80-column display.
  450 + // 1 2 3 4 5 6 7 8
  451 + // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
  452 + std::cout
  453 + << this->ap.getProgname()
  454 + << " version " << QPDF::QPDFVersion() << std::endl
  455 + << std::endl
  456 + << "Copyright (c) 2005-2021 Jay Berkenbilt"
  457 + << std::endl
  458 + << "QPDF is licensed under the Apache License, Version 2.0 (the \"License\");"
  459 + << std::endl
  460 + << "you may not use this file except in compliance with the License."
  461 + << std::endl
  462 + << "You may obtain a copy of the License at"
  463 + << std::endl
  464 + << std::endl
  465 + << " http://www.apache.org/licenses/LICENSE-2.0"
  466 + << std::endl
  467 + << std::endl
  468 + << "Unless required by applicable law or agreed to in writing, software"
  469 + << std::endl
  470 + << "distributed under the License is distributed on an \"AS IS\" BASIS,"
  471 + << std::endl
  472 + << "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied."
  473 + << std::endl
  474 + << "See the License for the specific language governing permissions and"
  475 + << std::endl
  476 + << "limitations under the License."
  477 + << std::endl
  478 + << std::endl
  479 + << "Versions of qpdf prior to version 7 were released under the terms"
  480 + << std::endl
  481 + << "of version 2.0 of the Artistic License. At your option, you may"
  482 + << std::endl
  483 + << "continue to consider qpdf to be licensed under those terms. Please"
  484 + << std::endl
  485 + << "see the manual for additional information."
  486 + << std::endl;
  487 +}
  488 +
  489 +void
  490 +ArgParser::argHelp()
  491 +{
  492 + std::cout
  493 + // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
  494 + << "Usage: qpdf [options] {infile | --empty} [page_selection_options] outfile\n"
  495 + << "\n"
  496 + << "An option summary appears below. Please see the documentation for details.\n"
  497 + << "\n"
  498 + << "If @filename appears anywhere in the command-line, each line of filename\n"
  499 + << "will be interpreted as an argument. No interpolation is done. Line\n"
  500 + << "terminators are stripped, but leading and trailing whitespace is\n"
  501 + << "intentionally preserved. @- can be specified to read from standard input.\n"
  502 + << "\n"
  503 + << "The output file can be - to indicate writing to standard output, or it can\n"
  504 + << "be --replace-input to cause qpdf to replace the input file with the output.\n"
  505 + << "\n"
  506 + << "Note that when contradictory options are provided, whichever options are\n"
  507 + << "provided last take precedence.\n"
  508 + << "\n"
  509 + << "\n"
  510 + << "Basic Options\n"
  511 + << "-------------\n"
  512 + << "\n"
  513 + << "--version show version of qpdf\n"
  514 + << "--copyright show qpdf's copyright and license information\n"
  515 + << "--help show command-line argument help\n"
  516 + << "--show-crypto show supported crypto providers; default is first\n"
  517 + << "--completion-bash output a bash complete command you can eval\n"
  518 + << "--completion-zsh output a zsh complete command you can eval\n"
  519 + << "--password=password specify a password for accessing encrypted files\n"
  520 + << "--password-file=file get the password the first line \"file\"; use \"-\"\n"
  521 + << " to read the password from stdin (without prompt or\n"
  522 + << " disabling echo, so use with caution)\n"
  523 + << "--is-encrypted silently exit 0 if the file is encrypted or 2\n"
  524 + << " if not; useful for shell scripts\n"
  525 + << "--requires-password silently exit 0 if a password (other than as\n"
  526 + << " supplied) is required, 2 if the file is not\n"
  527 + << " encrypted, or 3 if the file is encrypted\n"
  528 + << " but requires no password or the supplied password\n"
  529 + << " is correct; useful for shell scripts\n"
  530 + << "--verbose provide additional informational output\n"
  531 + << "--progress give progress indicators while writing output\n"
  532 + << "--no-warn suppress warnings\n"
  533 + << "--warning-exit-0 exit with code 0 instead of 3 if there are warnings\n"
  534 + << "--linearize generated a linearized (web optimized) file\n"
  535 + << "--replace-input use in place of specifying an output file; qpdf will\n"
  536 + << " replace the input file with the output\n"
  537 + << "--copy-encryption=file copy encryption parameters from specified file\n"
  538 + << "--encryption-file-password=password\n"
  539 + << " password used to open the file from which encryption\n"
  540 + << " parameters are being copied\n"
  541 + << "--allow-weak-crypto allow creation of files using weak cryptographic\n"
  542 + << " algorithms\n"
  543 + << "--encrypt options -- generate an encrypted file\n"
  544 + << "--decrypt remove any encryption on the file\n"
  545 + << "--password-is-hex-key treat primary password option as a hex-encoded key\n"
  546 + << "--suppress-password-recovery\n"
  547 + << " do not attempt recovering from password string\n"
  548 + << " encoding errors\n"
  549 + << "--password-mode=mode control qpdf's encoding of passwords\n"
  550 + << "--pages options -- select specific pages from one or more files\n"
  551 + << "--collate=n causes files specified in --pages to be collated\n"
  552 + << " in groups of n pages (default 1) rather than\n"
  553 + << " concatenated\n"
  554 + << "--flatten-rotation move page rotation from /Rotate key to content\n"
  555 + << "--rotate=[+|-]angle[:page-range]\n"
  556 + << " rotate each specified page 0, 90, 180, or 270\n"
  557 + << " degrees; rotate all pages if no page range is given\n"
  558 + << "--split-pages=[n] write each output page to a separate file\n"
  559 + << "--overlay options -- overlay pages from another file\n"
  560 + << "--underlay options -- underlay pages from another file\n"
  561 + << "\n"
  562 + << "Note that you can use the @filename or @- syntax for any argument at any\n"
  563 + << "point in the command. This provides a good way to specify a password without\n"
  564 + << "having to explicitly put it on the command line. @filename or @- must be a\n"
  565 + << "word by itself. Syntax such as --arg=@filename doesn't work.\n"
  566 + << "\n"
  567 + << "If none of --copy-encryption, --encrypt or --decrypt are given, qpdf will\n"
  568 + << "preserve any encryption data associated with a file.\n"
  569 + << "\n"
  570 + << "Note that when copying encryption parameters from another file, all\n"
  571 + << "parameters will be copied, including both user and owner passwords, even\n"
  572 + << "if the user password is used to open the other file. This works even if\n"
  573 + << "the owner password is not known.\n"
  574 + << "\n"
  575 + << "The --password-is-hex-key option overrides the normal computation of\n"
  576 + << "encryption keys. It only applies to the password used to open the main\n"
  577 + << "file. This option is not ordinarily useful but can be helpful for forensic\n"
  578 + << "or investigatory purposes. See manual for further discussion.\n"
  579 + << "\n"
  580 + << "The --rotate flag can be used to specify pages to rotate pages either\n"
  581 + << "0, 90, 180, or 270 degrees. The page range is specified in the same\n"
  582 + << "format as with the --pages option, described below. Repeat the option\n"
  583 + << "to rotate multiple groups of pages. If the angle is preceded by + or -,\n"
  584 + << "it is added to or subtracted from the original rotation. Otherwise, the\n"
  585 + << "rotation angle is set explicitly to the given value. You almost always\n"
  586 + << "want to use + or - unless you are certain about the internals of the PDF\n"
  587 + << "you are working with.\n"
  588 + << "\n"
  589 + << "If --split-pages is specified, each page is written to a separate output\n"
  590 + << "file. File names are generated as follows:\n"
  591 + << "* If the string %d appears in the output file name, it is replaced with a\n"
  592 + << " zero-padded page range starting from 1\n"
  593 + << "* Otherwise, if the output file name ends in .pdf (case insensitive), a\n"
  594 + << " zero-padded page range, preceded by a dash, is inserted before the file\n"
  595 + << " extension\n"
  596 + << "* Otherwise, the file name is appended with a zero-padded page range\n"
  597 + << " preceded by a dash.\n"
  598 + << "Page ranges are single page numbers for single-page groups or first-last\n"
  599 + << "for multipage groups.\n"
  600 + << "\n"
  601 + << "\n"
  602 + << "Encryption Options\n"
  603 + << "------------------\n"
  604 + << "\n"
  605 + << " --encrypt user-password owner-password key-length flags --\n"
  606 + << "\n"
  607 + << "Note that -- terminates parsing of encryption flags.\n"
  608 + << "\n"
  609 + << "Either or both of the user password and the owner password may be\n"
  610 + << "empty strings.\n"
  611 + << "\n"
  612 + << "key-length may be 40, 128, or 256\n"
  613 + << "\n"
  614 + << "Additional flags are dependent upon key length.\n"
  615 + << "\n"
  616 + << " If 40:\n"
  617 + << "\n"
  618 + << " --print=[yn] allow printing\n"
  619 + << " --modify=[yn] allow document modification\n"
  620 + << " --extract=[yn] allow text/graphic extraction\n"
  621 + << " --annotate=[yn] allow comments and form fill-in and signing\n"
  622 + << "\n"
  623 + << " If 128:\n"
  624 + << "\n"
  625 + << " --accessibility=[yn] allow accessibility to visually impaired\n"
  626 + << " --extract=[yn] allow other text/graphic extraction\n"
  627 + << " --print=print-opt control printing access\n"
  628 + << " --assemble=[yn] allow document assembly\n"
  629 + << " --annotate=[yn] allow commenting/filling form fields\n"
  630 + << " --form=[yn] allow filling form fields\n"
  631 + << " --modify-other=[yn] allow other modifications\n"
  632 + << " --modify=modify-opt control modify access (old way)\n"
  633 + << " --cleartext-metadata prevents encryption of metadata\n"
  634 + << " --use-aes=[yn] indicates whether to use AES encryption\n"
  635 + << " --force-V4 forces use of V=4 encryption handler\n"
  636 + << "\n"
  637 + << " If 256, options are the same as 128 with these exceptions:\n"
  638 + << " --force-V4 this option is not available with 256-bit keys\n"
  639 + << " --use-aes this option is always on with 256-bit keys\n"
  640 + << " --force-R5 forces use of deprecated R=5 encryption\n"
  641 + << " --allow-insecure allow the owner password to be empty when the\n"
  642 + << " user password is not empty\n"
  643 + << "\n"
  644 + << " print-opt may be:\n"
  645 + << "\n"
  646 + << " full allow full printing\n"
  647 + << " low allow only low-resolution printing\n"
  648 + << " none disallow printing\n"
  649 + << "\n"
  650 + << " modify-opt may be:\n"
  651 + << "\n"
  652 + << " all allow full document modification\n"
  653 + << " annotate allow comment authoring and form operations\n"
  654 + << " form allow form field fill-in and signing\n"
  655 + << " assembly allow document assembly only\n"
  656 + << " none allow no modifications\n"
  657 + << "\n"
  658 + << "The default for each permission option is to be fully permissive. Please\n"
  659 + << "refer to the manual for more details on the modify options.\n"
  660 + << "\n"
  661 + << "Specifying cleartext-metadata forces the PDF version to at least 1.5.\n"
  662 + << "Specifying use of AES forces the PDF version to at least 1.6. These\n"
  663 + << "options are both off by default.\n"
  664 + << "\n"
  665 + << "The --force-V4 flag forces the V=4 encryption handler introduced in PDF 1.5\n"
  666 + << "to be used even if not otherwise needed. This option is primarily useful\n"
  667 + << "for testing qpdf and has no other practical use.\n"
  668 + << "\n"
  669 + << "A warning will be issued if you attempt to encrypt a file with a format that\n"
  670 + << "uses a weak cryptographic algorithm such as RC4. To suppress the warning,\n"
  671 + << "specify the option --allow-weak-crypto. This option is outside of encryption\n"
  672 + << "options (e.g. --allow-week-crypto --encrypt u o 128 --)\n"
  673 + << "\n"
  674 + << "\n"
  675 + << "Password Modes\n"
  676 + << "--------------\n"
  677 + << "\n"
  678 + << "The --password-mode controls how qpdf interprets passwords supplied\n"
  679 + << "on the command-line. qpdf's default behavior is correct in almost all\n"
  680 + << "cases, but you can fine-tune with this option.\n"
  681 + << "\n"
  682 + << " bytes: use the password literally as supplied\n"
  683 + << " hex-bytes: interpret the password as a hex-encoded byte string\n"
  684 + << " unicode: interpret the password as a UTF-8 encoded string\n"
  685 + << " auto: attempt to infer the encoding and adjust as needed\n"
  686 + << "\n"
  687 + << "This is a complex topic. See the manual for a complete discussion.\n"
  688 + << "\n"
  689 + << "\n"
  690 + << "Page Selection Options\n"
  691 + << "----------------------\n"
  692 + << "\n"
  693 + << "These options allow pages to be selected from one or more PDF files.\n"
  694 + << "Whatever file is given as the primary input file is used as the\n"
  695 + << "starting point, but its pages are replaced with pages as specified.\n"
  696 + << "\n"
  697 + << "--keep-files-open=[yn]\n"
  698 + << "--keep-files-open-threshold=count\n"
  699 + << "--pages file [ --password=password ] [ page-range ] ... --\n"
  700 + << "\n"
  701 + << "For each file that pages should be taken from, specify the file, a\n"
  702 + << "password needed to open the file (if any), and a page range. The\n"
  703 + << "password needs to be given only once per file. If any of the input\n"
  704 + << "files are the same as the primary input file or the file used to copy\n"
  705 + << "encryption parameters (if specified), you do not need to repeat the\n"
  706 + << "password here. The same file can be repeated multiple times. The\n"
  707 + << "filename \".\" may be used to refer to the current input file. All\n"
  708 + << "non-page data (info, outlines, page numbers, etc. are taken from the\n"
  709 + << "primary input file. To discard this, use --empty as the primary\n"
  710 + << "input.\n"
  711 + << "\n"
  712 + << "By default, when more than 200 distinct files are specified, qpdf will\n"
  713 + << "close each file when not being referenced. With 200 files or fewer, all\n"
  714 + << "files will be kept open at the same time. This behavior can be overridden\n"
  715 + << "by specifying --keep-files-open=[yn]. Closing and opening files can have\n"
  716 + << "very high overhead on certain file systems, especially networked file\n"
  717 + << "systems. The threshold of 200 can be modified with\n"
  718 + << "--keep-files-open-threshold\n"
  719 + << "\n"
  720 + << "The page range is a set of numbers separated by commas, ranges of\n"
  721 + << "numbers separated dashes, or combinations of those. The character\n"
  722 + << "\"z\" represents the last page. A number preceded by an \"r\" indicates\n"
  723 + << "to count from the end, so \"r3-r1\" would be the last three pages of the\n"
  724 + << "document. Pages can appear in any order. Ranges can appear with a\n"
  725 + << "high number followed by a low number, which causes the pages to appear in\n"
  726 + << "reverse. Numbers may be repeated. A page range may be appended with :odd\n"
  727 + << "to indicate odd pages in the selected range or :even to indicate even\n"
  728 + << "pages.\n"
  729 + << "\n"
  730 + << "If the page range is omitted, the range of 1-z is assumed. qpdf decides\n"
  731 + << "that the page range is omitted if the range argument is either -- or a\n"
  732 + << "valid file name and not a valid range.\n"
  733 + << "\n"
  734 + << "The usual behavior of --pages is to add all pages from the first file,\n"
  735 + << "then all pages from the second file, and so on. If the --collate option\n"
  736 + << "is specified, then pages are collated instead. In other words, qpdf takes\n"
  737 + << "the first page from the first file, the first page from the second file,\n"
  738 + << "and so on until it runs out of files; then it takes the second page from\n"
  739 + << "each file, etc. When a file runs out of pages, it is skipped until all\n"
  740 + << "specified pages are taken from all files.\n"
  741 + << "\n"
  742 + << "See the manual for examples and a discussion of additional subtleties.\n"
  743 + << "\n"
  744 + << "\n"
  745 + << "Overlay and Underlay Options\n"
  746 + << "----------------------------\n"
  747 + << "\n"
  748 + << "These options allow pages from another file to be overlaid or underlaid\n"
  749 + << "on the primary output. Overlaid pages are drawn on top of the destination\n"
  750 + << "page and may obscure the page. Underlaid pages are drawn below the\n"
  751 + << "destination page.\n"
  752 + << "\n"
  753 + << "{--overlay | --underlay } file\n"
  754 + " [ --password=password ]\n"
  755 + " [ --to=page-range ]\n"
  756 + " [ --from=[page-range] ]\n"
  757 + " [ --repeat=page-range ]\n"
  758 + " --\n"
  759 + << "\n"
  760 + << "For overlay and underlay, a file and optional password are specified, along\n"
  761 + << "with a series of optional page ranges. The default behavior is that each\n"
  762 + << "page of the overlay or underlay file is imposed on the corresponding page\n"
  763 + << "of the primary output until it runs out of pages, and any extra pages are\n"
  764 + << "ignored. The page range options all take page ranges in the same form as\n"
  765 + << "the --pages option. They have the following meanings:\n"
  766 + << "\n"
  767 + << " --to: the pages in the primary output to which overlay/underlay is\n"
  768 + << " applied\n"
  769 + << " --from: the pages from the overlay/underlay file that are used\n"
  770 + << " --repeat: pages from the overlay/underlay that are repeated after\n"
  771 + << " any \"from\" pages have been exhausted\n"
  772 + << "\n"
  773 + << "\n"
  774 + << "Embedded Files/Attachments Options\n"
  775 + << "----------------------------------\n"
  776 + << "\n"
  777 + << "These options can be used to work with embedded files, also known as\n"
  778 + << "attachments.\n"
  779 + << "\n"
  780 + << "--list-attachments show key and stream number for embedded files;\n"
  781 + << " combine with --verbose for more detailed information\n"
  782 + << "--show-attachment=key write the contents of the specified attachment to\n"
  783 + << " standard output as binary data\n"
  784 + << "--add-attachment file options --\n"
  785 + << " add or replace an attachment\n"
  786 + << "--remove-attachment=key remove the specified attachment; repeatable\n"
  787 + << "--copy-attachments-from file options --\n"
  788 + << " copy attachments from another file\n"
  789 + << "\n"
  790 + << "The \"key\" option is the unique name under which the attachment is registered\n"
  791 + << "within the PDF file. You can get this using the --list-attachments option. This\n"
  792 + << "is usually the same as the filename, but it doesn't have to be.\n"
  793 + << "\n"
  794 + << "Options for adding attachments:\n"
  795 + << "\n"
  796 + << " file path to the file to attach\n"
  797 + << " --key=key the name of this in the embedded files table;\n"
  798 + << " defaults to the last path element of file\n"
  799 + << " --filename=name the file name of the attachment; this is what is\n"
  800 + << " usually displayed to the user; defaults to the\n"
  801 + << " last path element of file\n"
  802 + << " --creationdate=date creation date in PDF format; defaults to the\n"
  803 + << " current time\n"
  804 + << " --moddate=date modification date in PDF format; defaults to the\n"
  805 + << " current time\n"
  806 + << " --mimetype=type/subtype mime type of attachment (e.g. application/pdf)\n"
  807 + << " --description=\"text\" attachment description\n"
  808 + << " --replace replace any existing attachment with the same key\n"
  809 + << "\n"
  810 + << "Options for copying attachments:\n"
  811 + << "\n"
  812 + << " file file whose attachments should be copied\n"
  813 + << " --password=password password to open the other file, if needed\n"
  814 + << " --prefix=prefix a prefix to insert in front of each key;\n"
  815 + << " required if needed to ensure each attachment\n"
  816 + << " has a unique key\n"
  817 + << "\n"
  818 + << "Date format: D:yyyymmddhhmmss<z> where <z> is either Z for UTC or a timezone\n"
  819 + << "offset in the form -hh'mm' or +hh'mm'.\n"
  820 + << "Examples: D:20210207161528-05'00', D:20210207211528Z\n"
  821 + << "\n"
  822 + << "\n"
  823 + << "Advanced Parsing Options\n"
  824 + << "------------------------\n"
  825 + << "\n"
  826 + << "These options control aspects of how qpdf reads PDF files. Mostly these are\n"
  827 + << "of use to people who are working with damaged files. There is little reason\n"
  828 + << "to use these options unless you are trying to solve specific problems.\n"
  829 + << "\n"
  830 + << "--suppress-recovery prevents qpdf from attempting to recover damaged files\n"
  831 + << "--ignore-xref-streams tells qpdf to ignore any cross-reference streams\n"
  832 + << "\n"
  833 + << "\n"
  834 + << "Advanced Transformation Options\n"
  835 + << "-------------------------------\n"
  836 + << "\n"
  837 + << "These transformation options control fine points of how qpdf creates\n"
  838 + << "the output file. Mostly these are of use only to people who are very\n"
  839 + << "familiar with the PDF file format or who are PDF developers.\n"
  840 + << "\n"
  841 + << "--stream-data=option controls transformation of stream data (below)\n"
  842 + << "--compress-streams=[yn] controls whether to compress streams on output\n"
  843 + << "--decode-level=option controls how to filter streams from the input\n"
  844 + << "--recompress-flate recompress streams already compressed with Flate\n"
  845 + << "--compression-level=n set zlib compression level; most effective with\n"
  846 + << " --recompress-flate --object-streams=generate\n"
  847 + << "--normalize-content=[yn] enables or disables normalization of content streams\n"
  848 + << "--object-streams=mode controls handing of object streams\n"
  849 + << "--preserve-unreferenced preserve unreferenced objects\n"
  850 + << "--remove-unreferenced-resources={auto,yes,no}\n"
  851 + << " whether to remove unreferenced page resources\n"
  852 + << "--preserve-unreferenced-resources\n"
  853 + << " synonym for --remove-unreferenced-resources=no\n"
  854 + << "--newline-before-endstream always put a newline before endstream\n"
  855 + << "--coalesce-contents force all pages' content to be a single stream\n"
  856 + << "--flatten-annotations=option\n"
  857 + << " incorporate rendering of annotations into page\n"
  858 + << " contents including those for interactive form\n"
  859 + << " fields; may also want --generate-appearances\n"
  860 + << "--generate-appearances generate appearance streams for form fields\n"
  861 + << "--optimize-images compress images with DCT (JPEG) when advantageous\n"
  862 + << "--oi-min-width=w do not optimize images whose width is below w;\n"
  863 + << " default is 128. Use 0 to mean no minimum\n"
  864 + << "--oi-min-height=h do not optimize images whose height is below h\n"
  865 + << " default is 128. Use 0 to mean no minimum\n"
  866 + << "--oi-min-area=a do not optimize images whose pixel count is below a\n"
  867 + << " default is 16,384. Use 0 to mean no minimum\n"
  868 + << "--externalize-inline-images convert inline images to regular images; by\n"
  869 + << " default, images of at least 1,024 bytes are\n"
  870 + << " externalized\n"
  871 + << "--ii-min-bytes=bytes specify minimum size of inline images to be\n"
  872 + << " converted to regular images\n"
  873 + << "--keep-inline-images exclude inline images from image optimization\n"
  874 + << "--remove-page-labels remove any page labels present in the output file\n"
  875 + << "--qdf turns on \"QDF mode\" (below)\n"
  876 + << "--linearize-pass1=file write intermediate pass of linearized file\n"
  877 + << " for debugging\n"
  878 + << "--min-version=version sets the minimum PDF version of the output file\n"
  879 + << "--force-version=version forces this to be the PDF version of the output file\n"
  880 + << "\n"
  881 + << "Options for --flatten-annotations are all, print, or screen. If the option\n"
  882 + << "is print, only annotations marked as print are included. If the option is\n"
  883 + << "screen, options marked as \"no view\" are excluded. Otherwise, annotations\n"
  884 + << "are flattened regardless of the presence of print or NoView flags. It is\n"
  885 + << "common for PDF files to have a flag set that appearance streams need to be\n"
  886 + << "regenerated. This happens when someone changes a form value with software\n"
  887 + << "that does not know how to render the new value. qpdf will not flatten form\n"
  888 + << "fields in files like this. If you get this warning, you have two choices:\n"
  889 + << "either use qpdf's --generate-appearances flag to tell qpdf to go ahead and\n"
  890 + << "regenerate appearances, or use some other tool to generate the appearances.\n"
  891 + << "qpdf does a pretty good job with most forms when only ASCII and \"Windows\n"
  892 + << "ANSI\" characters are used in form field values, but if your form fields\n"
  893 + << "contain other characters, rich text, or are other than left justified, you\n"
  894 + << "will get better results first saving with other software.\n"
  895 + << "\n"
  896 + << "Version numbers may be expressed as major.minor.extension-level, so 1.7.3\n"
  897 + << "means PDF version 1.7 at extension level 3.\n"
  898 + << "\n"
  899 + << "Values for stream data options:\n"
  900 + << "\n"
  901 + << " compress recompress stream data when possible (default)\n"
  902 + << " preserve leave all stream data as is\n"
  903 + << " uncompress uncompress stream data when possible\n"
  904 + << "\n"
  905 + << "Values for object stream mode:\n"
  906 + << "\n"
  907 + << " preserve preserve original object streams (default)\n"
  908 + << " disable don't write any object streams\n"
  909 + << " generate use object streams wherever possible\n"
  910 + << "\n"
  911 + << "When --compress-streams=n is specified, this overrides the default behavior\n"
  912 + << "of qpdf, which is to attempt compress uncompressed streams. Setting\n"
  913 + << "stream data mode to uncompress or preserve has the same effect.\n"
  914 + << "\n"
  915 + << "The --decode-level parameter may be set to one of the following values:\n"
  916 + << " none do not decode streams\n"
  917 + << " generalized decode streams compressed with generalized filters\n"
  918 + << " including LZW, Flate, and the ASCII encoding filters.\n"
  919 + << " specialized additionally decode streams with non-lossy specialized\n"
  920 + << " filters including RunLength\n"
  921 + << " all additionally decode streams with lossy filters\n"
  922 + << " including DCT (JPEG)\n"
  923 + << "\n"
  924 + << "In qdf mode, by default, content normalization is turned on, and the\n"
  925 + << "stream data mode is set to uncompress. QDF mode does not support\n"
  926 + << "linearized files. The --linearize flag disables qdf mode.\n"
  927 + << "\n"
  928 + << "Setting the minimum PDF version of the output file may raise the version\n"
  929 + << "but will never lower it. Forcing the PDF version of the output file may\n"
  930 + << "set the PDF version to a lower value than actually allowed by the file's\n"
  931 + << "contents. You should only do this if you have no other possible way to\n"
  932 + << "open the file or if you know that the file definitely doesn't include\n"
  933 + << "features not supported later versions.\n"
  934 + << "\n"
  935 + << "Testing, Inspection, and Debugging Options\n"
  936 + << "------------------------------------------\n"
  937 + << "\n"
  938 + << "These options can be useful for digging into PDF files or for use in\n"
  939 + << "automated test suites for software that uses the qpdf library.\n"
  940 + << "\n"
  941 + << "--deterministic-id generate deterministic /ID\n"
  942 + << "--static-id generate static /ID: FOR TESTING ONLY!\n"
  943 + << "--static-aes-iv use a static initialization vector for AES-CBC\n"
  944 + << " This is option is not secure! FOR TESTING ONLY!\n"
  945 + << "--no-original-object-ids suppress original object ID comments in qdf mode\n"
  946 + << "--show-encryption quickly show encryption parameters\n"
  947 + << "--show-encryption-key when showing encryption, reveal the actual key\n"
  948 + << "--check-linearization check file integrity and linearization status\n"
  949 + << "--show-linearization check and show all linearization data\n"
  950 + << "--show-xref show the contents of the cross-reference table\n"
  951 + << "--show-object=trailer|obj[,gen]\n"
  952 + << " show the contents of the given object\n"
  953 + << " --raw-stream-data show raw stream data instead of object contents\n"
  954 + << " --filtered-stream-data show filtered stream data instead of object contents\n"
  955 + << "--show-npages print the number of pages in the file\n"
  956 + << "--show-pages shows the object/generation number for each page\n"
  957 + << " --with-images also shows the object IDs for images on each page\n"
  958 + << "--check check file structure + encryption, linearization\n"
  959 + << "--json generate a json representation of the file\n"
  960 + << "--json-help describe the format of the json representation\n"
  961 + << "--json-key=key repeatable; prune json structure to include only\n"
  962 + << " specified keys. If absent, all keys are shown\n"
  963 + << "--json-object=trailer|[obj,gen]\n"
  964 + << " repeatable; include only specified objects in the\n"
  965 + << " \"objects\" section of the json. If absent, all\n"
  966 + << " objects are shown\n"
  967 + << "\n"
  968 + << "The json representation generated by qpdf is designed to facilitate\n"
  969 + << "processing of qpdf from other programming languages that have a hard\n"
  970 + << "time calling C++ APIs. Run qpdf --json-help for details on the format.\n"
  971 + << "The manual has more in-depth information about the json representation\n"
  972 + << "and certain compatibility guarantees that qpdf provides.\n"
  973 + << "\n"
  974 + << "The --raw-stream-data and --filtered-stream-data options are ignored\n"
  975 + << "unless --show-object is given. Either of these options will cause the\n"
  976 + << "stream data to be written to standard output.\n"
  977 + << "\n"
  978 + << "If --filtered-stream-data is given and --normalize-content=y is also\n"
  979 + << "given, qpdf will attempt to normalize the stream data as if it is a\n"
  980 + << "page content stream. This attempt will be made even if it is not a\n"
  981 + << "page content stream, in which case it will produce unusable results.\n"
  982 + << "\n"
  983 + << "Ordinarily, qpdf exits with a status of 0 on success or a status of 2\n"
  984 + << "if any errors occurred. If there were warnings but not errors, qpdf\n"
  985 + << "exits with a status of 3. If warnings would have been issued but --no-warn\n"
  986 + << "was given, an exit status of 3 is still used. If you want qpdf to exit\n"
  987 + << "with status 0 when there are warnings, use the --warning-exit-0 flag.\n"
  988 + << "When --no-warn and --warning-exit-0 are used together, the effect is for\n"
  989 + << "qpdf to completely ignore warnings. qpdf does not use exit status 1,\n"
  990 + << "since that is used by the shell if it can't execute qpdf.\n";
  991 +}
  992 +
  993 +void
  994 +ArgParser::argJsonHelp()
  995 +{
  996 + // Make sure the output looks right on an 80-column display.
  997 + // 1 2 3 4 5 6 7 8
  998 + // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
  999 + std::cout
  1000 + << "The json block below contains the same structure with the same keys as the"
  1001 + << std::endl
  1002 + << "json generated by qpdf. In the block below, the values are descriptions of"
  1003 + << std::endl
  1004 + << "the meanings of those entries. The specific contract guaranteed by qpdf in"
  1005 + << std::endl
  1006 + << "its json representation is explained in more detail in the manual. You can"
  1007 + << std::endl
  1008 + << "specify a subset of top-level keys when you invoke qpdf, but the \"version\""
  1009 + << std::endl
  1010 + << "and \"parameters\" keys will always be present. Note that the \"encrypt\""
  1011 + << std::endl
  1012 + << "key's values will be populated for non-encrypted files. Some values will"
  1013 + << std::endl
  1014 + << "be null, and others will have values that apply to unencrypted files."
  1015 + << std::endl
  1016 + << QPDFJob::json_schema().unparse()
  1017 + << std::endl;
  1018 +}
  1019 +
  1020 +void
  1021 +ArgParser::argShowCrypto()
  1022 +{
  1023 + auto crypto = QPDFCryptoProvider::getRegisteredImpls();
  1024 + std::string default_crypto = QPDFCryptoProvider::getDefaultProvider();
  1025 + std::cout << default_crypto << std::endl;
  1026 + for (auto const& iter: crypto)
  1027 + {
  1028 + if (iter != default_crypto)
  1029 + {
  1030 + std::cout << iter << std::endl;
  1031 + }
  1032 + }
  1033 +}
  1034 +
  1035 +void
  1036 +ArgParser::argPassword(char* parameter)
  1037 +{
  1038 + o.password = parameter;
  1039 +}
  1040 +
  1041 +void
  1042 +ArgParser::argPasswordFile(char* parameter)
  1043 +{
  1044 + std::list<std::string> lines;
  1045 + if (strcmp(parameter, "-") == 0)
  1046 + {
  1047 + QTC::TC("qpdf", "qpdf password stdin");
  1048 + lines = QUtil::read_lines_from_file(std::cin);
  1049 + }
  1050 + else
  1051 + {
  1052 + QTC::TC("qpdf", "qpdf password file");
  1053 + lines = QUtil::read_lines_from_file(parameter);
  1054 + }
  1055 + if (lines.size() >= 1)
  1056 + {
  1057 + // Make sure the memory for this stays in scope.
  1058 + o.password_alloc = std::shared_ptr<char>(
  1059 + QUtil::copy_string(lines.front().c_str()),
  1060 + std::default_delete<char[]>());
  1061 + o.password = o.password_alloc.get();
  1062 +
  1063 + if (lines.size() > 1)
  1064 + {
  1065 + std::cerr << this->ap.getProgname()
  1066 + << ": WARNING: all but the first line of"
  1067 + << " the password file are ignored" << std::endl;
  1068 + }
  1069 + }
  1070 +}
  1071 +
  1072 +void
  1073 +ArgParser::argEmpty()
  1074 +{
  1075 + o.infilename = "";
  1076 +}
  1077 +
  1078 +void
  1079 +ArgParser::argLinearize()
  1080 +{
  1081 + o.linearize = true;
  1082 +}
  1083 +
  1084 +void
  1085 +ArgParser::argEncrypt()
  1086 +{
  1087 + this->accumulated_args.clear();
  1088 + if (this->ap.isCompleting() && this->ap.argsLeft() == 0)
  1089 + {
  1090 + this->ap.insertCompletion("user-password");
  1091 + }
  1092 + this->ap.selectOptionTable(O_ENCRYPT);
  1093 +}
  1094 +
  1095 +void
  1096 +ArgParser::argEncryptPositional(char* arg)
  1097 +{
  1098 + this->accumulated_args.push_back(arg);
  1099 + size_t n_args = this->accumulated_args.size();
  1100 + if (n_args < 3)
  1101 + {
  1102 + if (this->ap.isCompleting() && (this->ap.argsLeft() == 0))
  1103 + {
  1104 + if (n_args == 1)
  1105 + {
  1106 + this->ap.insertCompletion("owner-password");
  1107 + }
  1108 + else if (n_args == 2)
  1109 + {
  1110 + this->ap.insertCompletion("40");
  1111 + this->ap.insertCompletion("128");
  1112 + this->ap.insertCompletion("256");
  1113 + }
  1114 + }
  1115 + return;
  1116 + }
  1117 + o.user_password = this->accumulated_args.at(0);
  1118 + o.owner_password = this->accumulated_args.at(1);
  1119 + std::string len_str = this->accumulated_args.at(2);
  1120 + if (len_str == "40")
  1121 + {
  1122 + o.keylen = 40;
  1123 + this->ap.selectOptionTable(O_ENCRYPT_40);
  1124 + }
  1125 + else if (len_str == "128")
  1126 + {
  1127 + o.keylen = 128;
  1128 + this->ap.selectOptionTable(O_ENCRYPT_128);
  1129 + }
  1130 + else if (len_str == "256")
  1131 + {
  1132 + o.keylen = 256;
  1133 + o.use_aes = true;
  1134 + this->ap.selectOptionTable(O_ENCRYPT_256);
  1135 + }
  1136 + else
  1137 + {
  1138 + usage("encryption key length must be 40, 128, or 256");
  1139 + }
  1140 +}
  1141 +
  1142 +void
  1143 +ArgParser::argDecrypt()
  1144 +{
  1145 + o.decrypt = true;
  1146 + o.encrypt = false;
  1147 + o.copy_encryption = false;
  1148 +}
  1149 +
  1150 +void
  1151 +ArgParser::argPasswordIsHexKey()
  1152 +{
  1153 + o.password_is_hex_key = true;
  1154 +}
  1155 +
  1156 +void
  1157 +ArgParser::argSuppressPasswordRecovery()
  1158 +{
  1159 + o.suppress_password_recovery = true;
  1160 +}
  1161 +
  1162 +void
  1163 +ArgParser::argPasswordMode(char* parameter)
  1164 +{
  1165 + if (strcmp(parameter, "bytes") == 0)
  1166 + {
  1167 + o.password_mode = QPDFJob::pm_bytes;
  1168 + }
  1169 + else if (strcmp(parameter, "hex-bytes") == 0)
  1170 + {
  1171 + o.password_mode = QPDFJob::pm_hex_bytes;
  1172 + }
  1173 + else if (strcmp(parameter, "unicode") == 0)
  1174 + {
  1175 + o.password_mode = QPDFJob::pm_unicode;
  1176 + }
  1177 + else if (strcmp(parameter, "auto") == 0)
  1178 + {
  1179 + o.password_mode = QPDFJob::pm_auto;
  1180 + }
  1181 + else
  1182 + {
  1183 + usage("invalid password-mode option");
  1184 + }
  1185 +}
  1186 +
  1187 +void
  1188 +ArgParser::argAllowInsecure()
  1189 +{
  1190 + o.allow_insecure = true;
  1191 +}
  1192 +
  1193 +void
  1194 +ArgParser::argAllowWeakCrypto()
  1195 +{
  1196 + o.allow_weak_crypto = true;
  1197 +}
  1198 +
  1199 +void
  1200 +ArgParser::argCopyEncryption(char* parameter)
  1201 +{
  1202 + o.encryption_file = parameter;
  1203 + o.copy_encryption = true;
  1204 + o.encrypt = false;
  1205 + o.decrypt = false;
  1206 +}
  1207 +
  1208 +void
  1209 +ArgParser::argEncryptionFilePassword(char* parameter)
  1210 +{
  1211 + o.encryption_file_password = parameter;
  1212 +}
  1213 +
  1214 +void
  1215 +ArgParser::argCollate(char* parameter)
  1216 +{
  1217 + auto n = ((parameter == 0) ? 1 :
  1218 + QUtil::string_to_uint(parameter));
  1219 + o.collate = QIntC::to_size(n);
  1220 +}
  1221 +
  1222 +void
  1223 +ArgParser::argPages()
  1224 +{
  1225 + if (! o.page_specs.empty())
  1226 + {
  1227 + usage("the --pages may only be specified one time");
  1228 + }
  1229 + this->accumulated_args.clear();
  1230 + this->ap.selectOptionTable(O_PAGES);
  1231 +}
  1232 +
  1233 +void
  1234 +ArgParser::argPagesPassword(char* parameter)
  1235 +{
  1236 + if (this->pages_password != nullptr)
  1237 + {
  1238 + QTC::TC("qpdf", "qpdf duplicated pages password");
  1239 + usage("--password already specified for this file");
  1240 + }
  1241 + if (this->accumulated_args.size() != 1)
  1242 + {
  1243 + QTC::TC("qpdf", "qpdf misplaced pages password");
  1244 + usage("in --pages, --password must immediately follow a file name");
  1245 + }
  1246 + this->pages_password = parameter;
  1247 +}
  1248 +
  1249 +void
  1250 +ArgParser::argPagesPositional(char* arg)
  1251 +{
  1252 + if (arg == nullptr)
  1253 + {
  1254 + if (this->accumulated_args.empty())
  1255 + {
  1256 + return;
  1257 + }
  1258 + }
  1259 + else
  1260 + {
  1261 + this->accumulated_args.push_back(arg);
  1262 + }
  1263 +
  1264 + char const* file = this->accumulated_args.at(0);
  1265 + char const* range = nullptr;
  1266 +
  1267 + size_t n_args = this->accumulated_args.size();
  1268 + if (n_args >= 2)
  1269 + {
  1270 + range = this->accumulated_args.at(1);
  1271 + }
  1272 +
  1273 + // See if the user omitted the range entirely, in which case we
  1274 + // assume "1-z".
  1275 + char* next_file = nullptr;
  1276 + if (range == nullptr)
  1277 + {
  1278 + if (arg == nullptr)
  1279 + {
  1280 + // The filename or password was the last argument
  1281 + QTC::TC("qpdf", "qpdf pages range omitted at end",
  1282 + this->pages_password == nullptr ? 0 : 1);
  1283 + }
  1284 + else
  1285 + {
  1286 + // We need to accumulate some more arguments
  1287 + return;
  1288 + }
  1289 + }
  1290 + else
  1291 + {
  1292 + try
  1293 + {
  1294 + parseNumrange(range, 0, true);
  1295 + }
  1296 + catch (std::runtime_error& e1)
  1297 + {
  1298 + // The range is invalid. Let's see if it's a file.
  1299 + if (strcmp(range, ".") == 0)
  1300 + {
  1301 + // "." means the input file.
  1302 + QTC::TC("qpdf", "qpdf pages range omitted with .");
  1303 + }
  1304 + else if (QUtil::file_can_be_opened(range))
  1305 + {
  1306 + QTC::TC("qpdf", "qpdf pages range omitted in middle");
  1307 + // Yup, it's a file.
  1308 + }
  1309 + else
  1310 + {
  1311 + // Give the range error
  1312 + usage(e1.what());
  1313 + }
  1314 + next_file = const_cast<char*>(range);
  1315 + range = nullptr;
  1316 + }
  1317 + }
  1318 + if (range == nullptr)
  1319 + {
  1320 + range = "1-z";
  1321 + }
  1322 + o.page_specs.push_back(QPDFJob::PageSpec(file, this->pages_password, range));
  1323 + this->accumulated_args.clear();
  1324 + this->pages_password = nullptr;
  1325 + if (next_file != nullptr)
  1326 + {
  1327 + this->accumulated_args.push_back(next_file);
  1328 + }
  1329 +}
  1330 +
  1331 +void
  1332 +ArgParser::argEndPages()
  1333 +{
  1334 + argPagesPositional(nullptr);
  1335 + if (o.page_specs.empty())
  1336 + {
  1337 + usage("--pages: no page specifications given");
  1338 + }
  1339 +}
  1340 +
  1341 +void
  1342 +ArgParser::argUnderlay()
  1343 +{
  1344 + parseUnderOverlayOptions(&o.underlay);
  1345 +}
  1346 +
  1347 +void
  1348 +ArgParser::argOverlay()
  1349 +{
  1350 + parseUnderOverlayOptions(&o.overlay);
  1351 +}
  1352 +
  1353 +void
  1354 +ArgParser::argRotate(char* parameter)
  1355 +{
  1356 + parseRotationParameter(parameter);
  1357 +}
  1358 +
  1359 +void
  1360 +ArgParser::argFlattenRotation()
  1361 +{
  1362 + o.flatten_rotation = true;
  1363 +}
  1364 +
  1365 +void
  1366 +ArgParser::argListAttachments()
  1367 +{
  1368 + o.list_attachments = true;
  1369 + o.require_outfile = false;
  1370 +}
  1371 +
  1372 +void
  1373 +ArgParser::argShowAttachment(char* parameter)
  1374 +{
  1375 + o.attachment_to_show = parameter;
  1376 + o.require_outfile = false;
  1377 +}
  1378 +
  1379 +void
  1380 +ArgParser::argRemoveAttachment(char* parameter)
  1381 +{
  1382 + o.attachments_to_remove.push_back(parameter);
  1383 +}
  1384 +
  1385 +void
  1386 +ArgParser::argAddAttachment()
  1387 +{
  1388 + o.attachments_to_add.push_back(QPDFJob::AddAttachment());
  1389 + this->ap.selectOptionTable(O_ATTACHMENT);
  1390 +}
  1391 +
  1392 +void
  1393 +ArgParser::argCopyAttachments()
  1394 +{
  1395 + o.attachments_to_copy.push_back(QPDFJob::CopyAttachmentFrom());
  1396 + this->ap.selectOptionTable(O_COPY_ATTACHMENT);
  1397 +}
  1398 +
  1399 +void
  1400 +ArgParser::argStreamData(char* parameter)
  1401 +{
  1402 + o.stream_data_set = true;
  1403 + if (strcmp(parameter, "compress") == 0)
  1404 + {
  1405 + o.stream_data_mode = qpdf_s_compress;
  1406 + }
  1407 + else if (strcmp(parameter, "preserve") == 0)
  1408 + {
  1409 + o.stream_data_mode = qpdf_s_preserve;
  1410 + }
  1411 + else if (strcmp(parameter, "uncompress") == 0)
  1412 + {
  1413 + o.stream_data_mode = qpdf_s_uncompress;
  1414 + }
  1415 + else
  1416 + {
  1417 + // If this happens, it means streamDataChoices in
  1418 + // ArgParser::initOptionTable is wrong.
  1419 + usage("invalid stream-data option");
  1420 + }
  1421 +}
  1422 +
  1423 +void
  1424 +ArgParser::argCompressStreams(char* parameter)
  1425 +{
  1426 + o.compress_streams_set = true;
  1427 + o.compress_streams = (strcmp(parameter, "y") == 0);
  1428 +}
  1429 +
  1430 +void
  1431 +ArgParser::argRecompressFlate()
  1432 +{
  1433 + o.recompress_flate_set = true;
  1434 + o.recompress_flate = true;
  1435 +}
  1436 +
  1437 +void
  1438 +ArgParser::argCompressionLevel(char* parameter)
  1439 +{
  1440 + o.compression_level = QUtil::string_to_int(parameter);
  1441 +}
  1442 +
  1443 +void
  1444 +ArgParser::argDecodeLevel(char* parameter)
  1445 +{
  1446 + o.decode_level_set = true;
  1447 + if (strcmp(parameter, "none") == 0)
  1448 + {
  1449 + o.decode_level = qpdf_dl_none;
  1450 + }
  1451 + else if (strcmp(parameter, "generalized") == 0)
  1452 + {
  1453 + o.decode_level = qpdf_dl_generalized;
  1454 + }
  1455 + else if (strcmp(parameter, "specialized") == 0)
  1456 + {
  1457 + o.decode_level = qpdf_dl_specialized;
  1458 + }
  1459 + else if (strcmp(parameter, "all") == 0)
  1460 + {
  1461 + o.decode_level = qpdf_dl_all;
  1462 + }
  1463 + else
  1464 + {
  1465 + // If this happens, it means decodeLevelChoices in
  1466 + // ArgParser::initOptionTable is wrong.
  1467 + usage("invalid option");
  1468 + }
  1469 +}
  1470 +
  1471 +void
  1472 +ArgParser::argNormalizeContent(char* parameter)
  1473 +{
  1474 + o.normalize_set = true;
  1475 + o.normalize = (strcmp(parameter, "y") == 0);
  1476 +}
  1477 +
  1478 +void
  1479 +ArgParser::argSuppressRecovery()
  1480 +{
  1481 + o.suppress_recovery = true;
  1482 +}
  1483 +
  1484 +void
  1485 +ArgParser::argObjectStreams(char* parameter)
  1486 +{
  1487 + o.object_stream_set = true;
  1488 + if (strcmp(parameter, "disable") == 0)
  1489 + {
  1490 + o.object_stream_mode = qpdf_o_disable;
  1491 + }
  1492 + else if (strcmp(parameter, "preserve") == 0)
  1493 + {
  1494 + o.object_stream_mode = qpdf_o_preserve;
  1495 + }
  1496 + else if (strcmp(parameter, "generate") == 0)
  1497 + {
  1498 + o.object_stream_mode = qpdf_o_generate;
  1499 + }
  1500 + else
  1501 + {
  1502 + // If this happens, it means objectStreamsChoices in
  1503 + // ArgParser::initOptionTable is wrong.
  1504 + usage("invalid object stream mode");
  1505 + }
  1506 +}
  1507 +
  1508 +void
  1509 +ArgParser::argIgnoreXrefStreams()
  1510 +{
  1511 + o.ignore_xref_streams = true;
  1512 +}
  1513 +
  1514 +void
  1515 +ArgParser::argQdf()
  1516 +{
  1517 + o.qdf_mode = true;
  1518 +}
  1519 +
  1520 +void
  1521 +ArgParser::argPreserveUnreferenced()
  1522 +{
  1523 + o.preserve_unreferenced_objects = true;
  1524 +}
  1525 +
  1526 +void
  1527 +ArgParser::argPreserveUnreferencedResources()
  1528 +{
  1529 + o.remove_unreferenced_page_resources = QPDFJob::re_no;
  1530 +}
  1531 +
  1532 +void
  1533 +ArgParser::argRemoveUnreferencedResources(char* parameter)
  1534 +{
  1535 + if (strcmp(parameter, "auto") == 0)
  1536 + {
  1537 + o.remove_unreferenced_page_resources = QPDFJob::re_auto;
  1538 + }
  1539 + else if (strcmp(parameter, "yes") == 0)
  1540 + {
  1541 + o.remove_unreferenced_page_resources = QPDFJob::re_yes;
  1542 + }
  1543 + else if (strcmp(parameter, "no") == 0)
  1544 + {
  1545 + o.remove_unreferenced_page_resources = QPDFJob::re_no;
  1546 + }
  1547 + else
  1548 + {
  1549 + // If this happens, it means remove_unref_choices in
  1550 + // ArgParser::initOptionTable is wrong.
  1551 + usage("invalid value for --remove-unreferenced-page-resources");
  1552 + }
  1553 +}
  1554 +
  1555 +void
  1556 +ArgParser::argKeepFilesOpen(char* parameter)
  1557 +{
  1558 + o.keep_files_open_set = true;
  1559 + o.keep_files_open = (strcmp(parameter, "y") == 0);
  1560 +}
  1561 +
  1562 +void
  1563 +ArgParser::argKeepFilesOpenThreshold(char* parameter)
  1564 +{
  1565 + o.keep_files_open_threshold = QUtil::string_to_uint(parameter);
  1566 +}
  1567 +
  1568 +void
  1569 +ArgParser::argNewlineBeforeEndstream()
  1570 +{
  1571 + o.newline_before_endstream = true;
  1572 +}
  1573 +
  1574 +void
  1575 +ArgParser::argLinearizePass1(char* parameter)
  1576 +{
  1577 + o.linearize_pass1 = parameter;
  1578 +}
  1579 +
  1580 +void
  1581 +ArgParser::argCoalesceContents()
  1582 +{
  1583 + o.coalesce_contents = true;
  1584 +}
  1585 +
  1586 +void
  1587 +ArgParser::argFlattenAnnotations(char* parameter)
  1588 +{
  1589 + o.flatten_annotations = true;
  1590 + if (strcmp(parameter, "screen") == 0)
  1591 + {
  1592 + o.flatten_annotations_forbidden |= an_no_view;
  1593 + }
  1594 + else if (strcmp(parameter, "print") == 0)
  1595 + {
  1596 + o.flatten_annotations_required |= an_print;
  1597 + }
  1598 +}
  1599 +
  1600 +void
  1601 +ArgParser::argGenerateAppearances()
  1602 +{
  1603 + o.generate_appearances = true;
  1604 +}
  1605 +
  1606 +void
  1607 +ArgParser::argMinVersion(char* parameter)
  1608 +{
  1609 + o.min_version = parameter;
  1610 +}
  1611 +
  1612 +void
  1613 +ArgParser::argForceVersion(char* parameter)
  1614 +{
  1615 + o.force_version = parameter;
  1616 +}
  1617 +
  1618 +void
  1619 +ArgParser::argSplitPages(char* parameter)
  1620 +{
  1621 + int n = ((parameter == 0) ? 1 :
  1622 + QUtil::string_to_int(parameter));
  1623 + o.split_pages = n;
  1624 +}
  1625 +
  1626 +void
  1627 +ArgParser::argVerbose()
  1628 +{
  1629 + o.verbose = true;
  1630 +}
  1631 +
  1632 +void
  1633 +ArgParser::argProgress()
  1634 +{
  1635 + o.progress = true;
  1636 +}
  1637 +
  1638 +void
  1639 +ArgParser::argNoWarn()
  1640 +{
  1641 + o.suppress_warnings = true;
  1642 +}
  1643 +
  1644 +void
  1645 +ArgParser::argWarningExitZero()
  1646 +{
  1647 + o.warnings_exit_zero = true;
  1648 +}
  1649 +
  1650 +void
  1651 +ArgParser::argDeterministicId()
  1652 +{
  1653 + o.deterministic_id = true;
  1654 +}
  1655 +
  1656 +void
  1657 +ArgParser::argStaticId()
  1658 +{
  1659 + o.static_id = true;
  1660 +}
  1661 +
  1662 +void
  1663 +ArgParser::argStaticAesIv()
  1664 +{
  1665 + o.static_aes_iv = true;
  1666 +}
  1667 +
  1668 +void
  1669 +ArgParser::argNoOriginalObjectIds()
  1670 +{
  1671 + o.suppress_original_object_id = true;
  1672 +}
  1673 +
  1674 +void
  1675 +ArgParser::argShowEncryption()
  1676 +{
  1677 + o.show_encryption = true;
  1678 + o.require_outfile = false;
  1679 +}
  1680 +
  1681 +void
  1682 +ArgParser::argShowEncryptionKey()
  1683 +{
  1684 + o.show_encryption_key = true;
  1685 +}
  1686 +
  1687 +void
  1688 +ArgParser::argCheckLinearization()
  1689 +{
  1690 + o.check_linearization = true;
  1691 + o.require_outfile = false;
  1692 +}
  1693 +
  1694 +void
  1695 +ArgParser::argShowLinearization()
  1696 +{
  1697 + o.show_linearization = true;
  1698 + o.require_outfile = false;
  1699 +}
  1700 +
  1701 +void
  1702 +ArgParser::argShowXref()
  1703 +{
  1704 + o.show_xref = true;
  1705 + o.require_outfile = false;
  1706 +}
  1707 +
  1708 +void
  1709 +ArgParser::argShowObject(char* parameter)
  1710 +{
  1711 + QPDFJob::parse_object_id(parameter, o.show_trailer, o.show_obj, o.show_gen);
  1712 + o.require_outfile = false;
  1713 +}
  1714 +
  1715 +void
  1716 +ArgParser::argRawStreamData()
  1717 +{
  1718 + o.show_raw_stream_data = true;
  1719 +}
  1720 +
  1721 +void
  1722 +ArgParser::argFilteredStreamData()
  1723 +{
  1724 + o.show_filtered_stream_data = true;
  1725 +}
  1726 +
  1727 +void
  1728 +ArgParser::argShowNpages()
  1729 +{
  1730 + o.show_npages = true;
  1731 + o.require_outfile = false;
  1732 +}
  1733 +
  1734 +void
  1735 +ArgParser::argShowPages()
  1736 +{
  1737 + o.show_pages = true;
  1738 + o.require_outfile = false;
  1739 +}
  1740 +
  1741 +void
  1742 +ArgParser::argWithImages()
  1743 +{
  1744 + o.show_page_images = true;
  1745 +}
  1746 +
  1747 +void
  1748 +ArgParser::argJson()
  1749 +{
  1750 + o.json = true;
  1751 + o.require_outfile = false;
  1752 +}
  1753 +
  1754 +void
  1755 +ArgParser::argJsonKey(char* parameter)
  1756 +{
  1757 + o.json_keys.insert(parameter);
  1758 +}
  1759 +
  1760 +void
  1761 +ArgParser::argJsonObject(char* parameter)
  1762 +{
  1763 + o.json_objects.insert(parameter);
  1764 +}
  1765 +
  1766 +void
  1767 +ArgParser::argCheck()
  1768 +{
  1769 + o.check = true;
  1770 + o.require_outfile = false;
  1771 +}
  1772 +
  1773 +void
  1774 +ArgParser::argOptimizeImages()
  1775 +{
  1776 + o.optimize_images = true;
  1777 +}
  1778 +
  1779 +void
  1780 +ArgParser::argExternalizeInlineImages()
  1781 +{
  1782 + o.externalize_inline_images = true;
  1783 +}
  1784 +
  1785 +void
  1786 +ArgParser::argKeepInlineImages()
  1787 +{
  1788 + o.keep_inline_images = true;
  1789 +}
  1790 +
  1791 +void
  1792 +ArgParser::argRemovePageLabels()
  1793 +{
  1794 + o.remove_page_labels = true;
  1795 +}
  1796 +
  1797 +void
  1798 +ArgParser::argOiMinWidth(char* parameter)
  1799 +{
  1800 + o.oi_min_width = QUtil::string_to_uint(parameter);
  1801 +}
  1802 +
  1803 +void
  1804 +ArgParser::argOiMinHeight(char* parameter)
  1805 +{
  1806 + o.oi_min_height = QUtil::string_to_uint(parameter);
  1807 +}
  1808 +
  1809 +void
  1810 +ArgParser::argOiMinArea(char* parameter)
  1811 +{
  1812 + o.oi_min_area = QUtil::string_to_uint(parameter);
  1813 +}
  1814 +
  1815 +void
  1816 +ArgParser::argIiMinBytes(char* parameter)
  1817 +{
  1818 + o.ii_min_bytes = QUtil::string_to_uint(parameter);
  1819 +}
  1820 +
  1821 +void
  1822 +ArgParser::arg40Print(char* parameter)
  1823 +{
  1824 + o.r2_print = (strcmp(parameter, "y") == 0);
  1825 +}
  1826 +
  1827 +void
  1828 +ArgParser::arg40Modify(char* parameter)
  1829 +{
  1830 + o.r2_modify = (strcmp(parameter, "y") == 0);
  1831 +}
  1832 +
  1833 +void
  1834 +ArgParser::arg40Extract(char* parameter)
  1835 +{
  1836 + o.r2_extract = (strcmp(parameter, "y") == 0);
  1837 +}
  1838 +
  1839 +void
  1840 +ArgParser::arg40Annotate(char* parameter)
  1841 +{
  1842 + o.r2_annotate = (strcmp(parameter, "y") == 0);
  1843 +}
  1844 +
  1845 +void
  1846 +ArgParser::arg128Accessibility(char* parameter)
  1847 +{
  1848 + o.r3_accessibility = (strcmp(parameter, "y") == 0);
  1849 +}
  1850 +
  1851 +void
  1852 +ArgParser::arg128Extract(char* parameter)
  1853 +{
  1854 + o.r3_extract = (strcmp(parameter, "y") == 0);
  1855 +}
  1856 +
  1857 +void
  1858 +ArgParser::arg128Print(char* parameter)
  1859 +{
  1860 + if (strcmp(parameter, "full") == 0)
  1861 + {
  1862 + o.r3_print = qpdf_r3p_full;
  1863 + }
  1864 + else if (strcmp(parameter, "low") == 0)
  1865 + {
  1866 + o.r3_print = qpdf_r3p_low;
  1867 + }
  1868 + else if (strcmp(parameter, "none") == 0)
  1869 + {
  1870 + o.r3_print = qpdf_r3p_none;
  1871 + }
  1872 + else
  1873 + {
  1874 + usage("invalid print option");
  1875 + }
  1876 +}
  1877 +
  1878 +void
  1879 +ArgParser::arg128Modify(char* parameter)
  1880 +{
  1881 + if (strcmp(parameter, "all") == 0)
  1882 + {
  1883 + o.r3_assemble = true;
  1884 + o.r3_annotate_and_form = true;
  1885 + o.r3_form_filling = true;
  1886 + o.r3_modify_other = true;
  1887 + }
  1888 + else if (strcmp(parameter, "annotate") == 0)
  1889 + {
  1890 + o.r3_assemble = true;
  1891 + o.r3_annotate_and_form = true;
  1892 + o.r3_form_filling = true;
  1893 + o.r3_modify_other = false;
  1894 + }
  1895 + else if (strcmp(parameter, "form") == 0)
  1896 + {
  1897 + o.r3_assemble = true;
  1898 + o.r3_annotate_and_form = false;
  1899 + o.r3_form_filling = true;
  1900 + o.r3_modify_other = false;
  1901 + }
  1902 + else if (strcmp(parameter, "assembly") == 0)
  1903 + {
  1904 + o.r3_assemble = true;
  1905 + o.r3_annotate_and_form = false;
  1906 + o.r3_form_filling = false;
  1907 + o.r3_modify_other = false;
  1908 + }
  1909 + else if (strcmp(parameter, "none") == 0)
  1910 + {
  1911 + o.r3_assemble = false;
  1912 + o.r3_annotate_and_form = false;
  1913 + o.r3_form_filling = false;
  1914 + o.r3_modify_other = false;
  1915 + }
  1916 + else
  1917 + {
  1918 + usage("invalid modify option");
  1919 + }
  1920 +}
  1921 +
  1922 +void
  1923 +ArgParser::arg128ClearTextMetadata()
  1924 +{
  1925 + o.cleartext_metadata = true;
  1926 +}
  1927 +
  1928 +void
  1929 +ArgParser::arg128Assemble(char* parameter)
  1930 +{
  1931 + o.r3_assemble = (strcmp(parameter, "y") == 0);
  1932 +}
  1933 +
  1934 +void
  1935 +ArgParser::arg128Annotate(char* parameter)
  1936 +{
  1937 + o.r3_annotate_and_form = (strcmp(parameter, "y") == 0);
  1938 +}
  1939 +
  1940 +void
  1941 +ArgParser::arg128Form(char* parameter)
  1942 +{
  1943 + o.r3_form_filling = (strcmp(parameter, "y") == 0);
  1944 +}
  1945 +
  1946 +void
  1947 +ArgParser::arg128ModOther(char* parameter)
  1948 +{
  1949 + o.r3_modify_other = (strcmp(parameter, "y") == 0);
  1950 +}
  1951 +
  1952 +void
  1953 +ArgParser::arg128UseAes(char* parameter)
  1954 +{
  1955 + o.use_aes = (strcmp(parameter, "y") == 0);
  1956 +}
  1957 +
  1958 +void
  1959 +ArgParser::arg128ForceV4()
  1960 +{
  1961 + o.force_V4 = true;
  1962 +}
  1963 +
  1964 +void
  1965 +ArgParser::arg256ForceR5()
  1966 +{
  1967 + o.force_R5 = true;
  1968 +}
  1969 +
  1970 +void
  1971 +ArgParser::argEndEncrypt()
  1972 +{
  1973 + o.encrypt = true;
  1974 + o.decrypt = false;
  1975 + o.copy_encryption = false;
  1976 +}
  1977 +
  1978 +void
  1979 +ArgParser::argUOpositional(char* arg)
  1980 +{
  1981 + if (o.under_overlay->filename)
  1982 + {
  1983 + usage(o.under_overlay->which + " file already specified");
  1984 + }
  1985 + else
  1986 + {
  1987 + o.under_overlay->filename = arg;
  1988 + }
  1989 +}
  1990 +
  1991 +void
  1992 +ArgParser::argUOto(char* parameter)
  1993 +{
  1994 + parseNumrange(parameter, 0);
  1995 + o.under_overlay->to_nr = parameter;
  1996 +}
  1997 +
  1998 +void
  1999 +ArgParser::argUOfrom(char* parameter)
  2000 +{
  2001 + if (strlen(parameter))
  2002 + {
  2003 + parseNumrange(parameter, 0);
  2004 + }
  2005 + o.under_overlay->from_nr = parameter;
  2006 +}
  2007 +
  2008 +void
  2009 +ArgParser::argUOrepeat(char* parameter)
  2010 +{
  2011 + if (strlen(parameter))
  2012 + {
  2013 + parseNumrange(parameter, 0);
  2014 + }
  2015 + o.under_overlay->repeat_nr = parameter;
  2016 +}
  2017 +
  2018 +void
  2019 +ArgParser::argUOpassword(char* parameter)
  2020 +{
  2021 + o.under_overlay->password = parameter;
  2022 +}
  2023 +
  2024 +void
  2025 +ArgParser::argEndUnderOverlay()
  2026 +{
  2027 + if (0 == o.under_overlay->filename)
  2028 + {
  2029 + usage(o.under_overlay->which + " file not specified");
  2030 + }
  2031 + o.under_overlay = 0;
  2032 +}
  2033 +
  2034 +void
  2035 +ArgParser::argReplaceInput()
  2036 +{
  2037 + o.replace_input = true;
  2038 +}
  2039 +
  2040 +void
  2041 +ArgParser::argIsEncrypted()
  2042 +{
  2043 + o.check_is_encrypted = true;
  2044 + o.require_outfile = false;
  2045 +}
  2046 +
  2047 +void
  2048 +ArgParser::argRequiresPassword()
  2049 +{
  2050 + o.check_requires_password = true;
  2051 + o.require_outfile = false;
  2052 +}
  2053 +
  2054 +void
  2055 +ArgParser::argAApositional(char* arg)
  2056 +{
  2057 + o.attachments_to_add.back().path = arg;
  2058 +}
  2059 +
  2060 +void
  2061 +ArgParser::argAAKey(char* parameter)
  2062 +{
  2063 + o.attachments_to_add.back().key = parameter;
  2064 +}
  2065 +
  2066 +void
  2067 +ArgParser::argAAFilename(char* parameter)
  2068 +{
  2069 + o.attachments_to_add.back().filename = parameter;
  2070 +}
  2071 +
  2072 +void
  2073 +ArgParser::argAACreationDate(char* parameter)
  2074 +{
  2075 + if (! QUtil::pdf_time_to_qpdf_time(parameter))
  2076 + {
  2077 + usage(std::string(parameter) + " is not a valid PDF timestamp");
  2078 + }
  2079 + o.attachments_to_add.back().creationdate = parameter;
  2080 +}
  2081 +
  2082 +void
  2083 +ArgParser::argAAModDate(char* parameter)
  2084 +{
  2085 + if (! QUtil::pdf_time_to_qpdf_time(parameter))
  2086 + {
  2087 + usage(std::string(parameter) + " is not a valid PDF timestamp");
  2088 + }
  2089 + o.attachments_to_add.back().moddate = parameter;
  2090 +}
  2091 +
  2092 +void
  2093 +ArgParser::argAAMimeType(char* parameter)
  2094 +{
  2095 + if (strchr(parameter, '/') == nullptr)
  2096 + {
  2097 + usage("mime type should be specified as type/subtype");
  2098 + }
  2099 + o.attachments_to_add.back().mimetype = parameter;
  2100 +}
  2101 +
  2102 +void
  2103 +ArgParser::argAADescription(char* parameter)
  2104 +{
  2105 + o.attachments_to_add.back().description = parameter;
  2106 +}
  2107 +
  2108 +void
  2109 +ArgParser::argAAReplace()
  2110 +{
  2111 + o.attachments_to_add.back().replace = true;
  2112 +}
  2113 +
  2114 +void
  2115 +ArgParser::argEndAddAttachment()
  2116 +{
  2117 + static std::string now = QUtil::qpdf_time_to_pdf_time(
  2118 + QUtil::get_current_qpdf_time());
  2119 + auto& cur = o.attachments_to_add.back();
  2120 + if (cur.path.empty())
  2121 + {
  2122 + usage("add attachment: no path specified");
  2123 + }
  2124 + std::string last_element = QUtil::path_basename(cur.path);
  2125 + if (last_element.empty())
  2126 + {
  2127 + usage("path for --add-attachment may not be empty");
  2128 + }
  2129 + if (cur.filename.empty())
  2130 + {
  2131 + cur.filename = last_element;
  2132 + }
  2133 + if (cur.key.empty())
  2134 + {
  2135 + cur.key = last_element;
  2136 + }
  2137 + if (cur.creationdate.empty())
  2138 + {
  2139 + cur.creationdate = now;
  2140 + }
  2141 + if (cur.moddate.empty())
  2142 + {
  2143 + cur.moddate = now;
  2144 + }
  2145 +}
  2146 +
  2147 +void
  2148 +ArgParser::argCApositional(char* arg)
  2149 +{
  2150 + o.attachments_to_copy.back().path = arg;
  2151 +}
  2152 +
  2153 +void
  2154 +ArgParser::argCAprefix(char* parameter)
  2155 +{
  2156 + o.attachments_to_copy.back().prefix = parameter;
  2157 +}
  2158 +
  2159 +void
  2160 +ArgParser::argCApassword(char* parameter)
  2161 +{
  2162 + o.attachments_to_copy.back().password = parameter;
  2163 +}
  2164 +
  2165 +void
  2166 +ArgParser::argEndCopyAttachments()
  2167 +{
  2168 + if (o.attachments_to_copy.back().path.empty())
  2169 + {
  2170 + usage("copy attachments: no path specified");
  2171 + }
  2172 +}
  2173 +
  2174 +void
  2175 +ArgParser::usage(std::string const& message)
  2176 +{
  2177 + if (this->ap.isCompleting())
  2178 + {
  2179 + // This will cause bash to fall back to regular file completion.
  2180 + exit(0);
  2181 + }
  2182 + else
  2183 + {
  2184 + throw QPDFArgParser::Usage(message);
  2185 + }
  2186 +}
  2187 +
  2188 +std::vector<int>
  2189 +ArgParser::parseNumrange(char const* range, int max, bool throw_error)
  2190 +{
  2191 + try
  2192 + {
  2193 + return QUtil::parse_numrange(range, max);
  2194 + }
  2195 + catch (std::runtime_error& e)
  2196 + {
  2197 + if (throw_error)
  2198 + {
  2199 + throw(e);
  2200 + }
  2201 + else
  2202 + {
  2203 + usage(e.what());
  2204 + }
  2205 + }
  2206 + return std::vector<int>();
  2207 +}
  2208 +
  2209 +void
  2210 +ArgParser::parseUnderOverlayOptions(QPDFJob::UnderOverlay* uo)
  2211 +{
  2212 + o.under_overlay = uo;
  2213 + this->ap.selectOptionTable(O_UNDER_OVERLAY);
  2214 +}
  2215 +
  2216 +void
  2217 +ArgParser::parseRotationParameter(std::string const& parameter)
  2218 +{
  2219 + std::string angle_str;
  2220 + std::string range;
  2221 + size_t colon = parameter.find(':');
  2222 + int relative = 0;
  2223 + if (colon != std::string::npos)
  2224 + {
  2225 + if (colon > 0)
  2226 + {
  2227 + angle_str = parameter.substr(0, colon);
  2228 + }
  2229 + if (colon + 1 < parameter.length())
  2230 + {
  2231 + range = parameter.substr(colon + 1);
  2232 + }
  2233 + }
  2234 + else
  2235 + {
  2236 + angle_str = parameter;
  2237 + }
  2238 + if (angle_str.length() > 0)
  2239 + {
  2240 + char first = angle_str.at(0);
  2241 + if ((first == '+') || (first == '-'))
  2242 + {
  2243 + relative = ((first == '+') ? 1 : -1);
  2244 + angle_str = angle_str.substr(1);
  2245 + }
  2246 + else if (! QUtil::is_digit(angle_str.at(0)))
  2247 + {
  2248 + angle_str = "";
  2249 + }
  2250 + }
  2251 + if (range.empty())
  2252 + {
  2253 + range = "1-z";
  2254 + }
  2255 + bool range_valid = false;
  2256 + try
  2257 + {
  2258 + parseNumrange(range.c_str(), 0, true);
  2259 + range_valid = true;
  2260 + }
  2261 + catch (std::runtime_error const&)
  2262 + {
  2263 + // ignore
  2264 + }
  2265 + if (range_valid &&
  2266 + ((angle_str == "0") ||(angle_str == "90") ||
  2267 + (angle_str == "180") || (angle_str == "270")))
  2268 + {
  2269 + int angle = QUtil::string_to_int(angle_str.c_str());
  2270 + if (relative == -1)
  2271 + {
  2272 + angle = -angle;
  2273 + }
  2274 + o.rotations[range] = QPDFJob::RotationSpec(angle, (relative != 0));
  2275 + }
  2276 + else
  2277 + {
  2278 + usage("invalid parameter to rotate: " + parameter);
  2279 + }
  2280 +}
  2281 +
  2282 +void
  2283 +ArgParser::parseOptions()
  2284 +{
  2285 + try
  2286 + {
  2287 + this->ap.parseArgs();
  2288 + }
  2289 + catch (QPDFArgParser::Usage& e)
  2290 + {
  2291 + usage(e.what());
  2292 + }
  2293 +}
  2294 +
  2295 +void
  2296 +ArgParser::doFinalChecks()
  2297 +{
  2298 + if (o.replace_input)
  2299 + {
  2300 + if (o.outfilename)
  2301 + {
  2302 + usage("--replace-input may not be used when"
  2303 + " an output file is specified");
  2304 + }
  2305 + else if (o.split_pages)
  2306 + {
  2307 + usage("--split-pages may not be used with --replace-input");
  2308 + }
  2309 + }
  2310 + if (o.infilename == 0)
  2311 + {
  2312 + usage("an input file name is required");
  2313 + }
  2314 + else if (o.require_outfile && (o.outfilename == 0) && (! o.replace_input))
  2315 + {
  2316 + usage("an output file name is required; use - for standard output");
  2317 + }
  2318 + else if ((! o.require_outfile) &&
  2319 + ((o.outfilename != 0) || o.replace_input))
  2320 + {
  2321 + usage("no output file may be given for this option");
  2322 + }
  2323 + if (o.optimize_images && (! o.keep_inline_images))
  2324 + {
  2325 + // QXXXQ this is not a check and doesn't belong here
  2326 + o.externalize_inline_images = true;
  2327 + }
  2328 + if (o.check_requires_password && o.check_is_encrypted)
  2329 + {
  2330 + usage("--requires-password and --is-encrypted may not be given"
  2331 + " together");
  2332 + }
  2333 +
  2334 + if (o.encrypt && (! o.allow_insecure) &&
  2335 + (o.owner_password.empty() &&
  2336 + (! o.user_password.empty()) &&
  2337 + (o.keylen == 256)))
  2338 + {
  2339 + // Note that empty owner passwords for R < 5 are copied from
  2340 + // the user password, so this lack of security is not an issue
  2341 + // for those files. Also we are consider only the ability to
  2342 + // open the file without a password to be insecure. We are not
  2343 + // concerned about whether the viewer enforces security
  2344 + // settings when the user and owner password match.
  2345 + usage("A PDF with a non-empty user password and an empty owner"
  2346 + " password encrypted with a 256-bit key is insecure as it"
  2347 + " can be opened without a password. If you really want to"
  2348 + " do this, you must also give the --allow-insecure option"
  2349 + " before the -- that follows --encrypt.");
  2350 + }
  2351 +
  2352 + if (o.require_outfile && o.outfilename &&
  2353 + (strcmp(o.outfilename, "-") == 0))
  2354 + {
  2355 + if (o.split_pages)
  2356 + {
  2357 + usage("--split-pages may not be used when"
  2358 + " writing to standard output");
  2359 + }
  2360 + if (o.verbose) // QXXXQ
  2361 + {
  2362 + usage("--verbose may not be used when"
  2363 + " writing to standard output");
  2364 + }
  2365 + if (o.progress)
  2366 + {
  2367 + usage("--progress may not be used when"
  2368 + " writing to standard output");
  2369 + }
  2370 + }
  2371 +
  2372 + if ((! o.split_pages) && QUtil::same_file(o.infilename, o.outfilename))
  2373 + {
  2374 + QTC::TC("qpdf", "qpdf same file error");
  2375 + usage("input file and output file are the same;"
  2376 + " use --replace-input to intentionally overwrite the input file");
  2377 + }
  2378 +}
  2379 +
  2380 +void
  2381 +QPDFJob::initializeFromArgv(int argc, char* argv[], char const* progname_env)
  2382 +{
  2383 + if (progname_env == nullptr)
  2384 + {
  2385 + progname_env = "QPDF_EXECUTABLE";
  2386 + }
  2387 + // QPDFArgParser must stay in scope for the life of the QPDFJob
  2388 + // object since it holds dynamic memory used for argv, which is
  2389 + // pointed to by other member variables.
  2390 + this->m->ap = new QPDFArgParser(argc, argv, progname_env);
  2391 + setMessagePrefix(this->m->ap->getProgname());
  2392 + ArgParser ap(*this->m->ap, *this);
  2393 + ap.parseOptions();
  2394 +}
libqpdf/build.mk
@@ -65,6 +65,7 @@ SRCS_libqpdf = \ @@ -65,6 +65,7 @@ SRCS_libqpdf = \
65 libqpdf/QPDFFileSpecObjectHelper.cc \ 65 libqpdf/QPDFFileSpecObjectHelper.cc \
66 libqpdf/QPDFFormFieldObjectHelper.cc \ 66 libqpdf/QPDFFormFieldObjectHelper.cc \
67 libqpdf/QPDFJob.cc \ 67 libqpdf/QPDFJob.cc \
  68 + libqpdf/QPDFJob_argv.cc \
68 libqpdf/QPDFMatrix.cc \ 69 libqpdf/QPDFMatrix.cc \
69 libqpdf/QPDFNameTreeObjectHelper.cc \ 70 libqpdf/QPDFNameTreeObjectHelper.cc \
70 libqpdf/QPDFNumberTreeObjectHelper.cc \ 71 libqpdf/QPDFNumberTreeObjectHelper.cc \
qpdf/qpdf.cc
1 -// QXXXQ update headers  
2 -  
3 -#include <iostream>  
4 -#include <string.h>  
5 -#include <stdlib.h>  
6 -//#include <fcntl.h>  
7 -#include <cstdio>  
8 -#include <ctype.h>  
9 -#include <memory>  
10 -  
11 -#include <qpdf/QUtil.hh>  
12 -#include <qpdf/QTC.hh>  
13 -#include <qpdf/QPDFCryptoProvider.hh>  
14 -#include <qpdf/QPDFArgParser.hh>  
15 -#include <qpdf/QPDFJob.hh>  
16 -#include <qpdf/QIntC.hh>  
17 -  
18 -static int constexpr EXIT_ERROR = 2;  
19 -static int EXIT_WARNING = 3; // may be changed to 0 at runtime  
20 -  
21 -// For is-encrypted and requires-password  
22 -static int constexpr EXIT_IS_NOT_ENCRYPTED = 2;  
23 -static int constexpr EXIT_CORRECT_PASSWORD = 3;  
24 -  
25 -static char const* whoami = 0;  
26 -  
27 -class ArgParser  
28 -{  
29 - public:  
30 - ArgParser(int argc, char* argv[], QPDFJob& o);  
31 - void parseOptions();  
32 -  
33 - private:  
34 - static constexpr char const* O_PAGES = "pages";  
35 - static constexpr char const* O_ENCRYPT = "encryption";  
36 - static constexpr char const* O_ENCRYPT_40 = "40-bit encryption";  
37 - static constexpr char const* O_ENCRYPT_128 = "128-bit encryption";  
38 - static constexpr char const* O_ENCRYPT_256 = "256-bit encryption";  
39 - static constexpr char const* O_UNDER_OVERLAY = "underlay/overlay";  
40 - static constexpr char const* O_ATTACHMENT = "attachment";  
41 - static constexpr char const* O_COPY_ATTACHMENT = "copy attachment";  
42 -  
43 - void argHelp();  
44 - void argVersion();  
45 - void argCopyright();  
46 - void argJsonHelp();  
47 - void argShowCrypto();  
48 - void argPositional(char* arg);  
49 - void argPassword(char* parameter);  
50 - void argPasswordFile(char* parameter);  
51 - void argEmpty();  
52 - void argLinearize();  
53 - void argEncrypt();  
54 - void argDecrypt();  
55 - void argPasswordIsHexKey();  
56 - void argAllowInsecure();  
57 - void argAllowWeakCrypto();  
58 - void argPasswordMode(char* parameter);  
59 - void argSuppressPasswordRecovery();  
60 - void argCopyEncryption(char* parameter);  
61 - void argEncryptionFilePassword(char* parameter);  
62 - void argPages();  
63 - void argPagesPassword(char* parameter);  
64 - void argPagesPositional(char* parameter);  
65 - void argEndPages();  
66 - void argUnderlay();  
67 - void argOverlay();  
68 - void argRotate(char* parameter);  
69 - void argCollate(char* parameter);  
70 - void argFlattenRotation();  
71 - void argListAttachments();  
72 - void argShowAttachment(char* parameter);  
73 - void argRemoveAttachment(char* parameter);  
74 - void argAddAttachment();  
75 - void argCopyAttachments();  
76 - void argStreamData(char* parameter);  
77 - void argCompressStreams(char* parameter);  
78 - void argRecompressFlate();  
79 - void argCompressionLevel(char* parameter);  
80 - void argDecodeLevel(char* parameter);  
81 - void argNormalizeContent(char* parameter);  
82 - void argSuppressRecovery();  
83 - void argObjectStreams(char* parameter);  
84 - void argIgnoreXrefStreams();  
85 - void argQdf();  
86 - void argPreserveUnreferenced();  
87 - void argPreserveUnreferencedResources();  
88 - void argRemoveUnreferencedResources(char* parameter);  
89 - void argKeepFilesOpen(char* parameter);  
90 - void argKeepFilesOpenThreshold(char* parameter);  
91 - void argNewlineBeforeEndstream();  
92 - void argLinearizePass1(char* parameter);  
93 - void argCoalesceContents();  
94 - void argFlattenAnnotations(char* parameter);  
95 - void argGenerateAppearances();  
96 - void argMinVersion(char* parameter);  
97 - void argForceVersion(char* parameter);  
98 - void argSplitPages(char* parameter);  
99 - void argVerbose();  
100 - void argProgress();  
101 - void argNoWarn();  
102 - void argWarningExitZero();  
103 - void argDeterministicId();  
104 - void argStaticId();  
105 - void argStaticAesIv();  
106 - void argNoOriginalObjectIds();  
107 - void argShowEncryption();  
108 - void argShowEncryptionKey();  
109 - void argCheckLinearization();  
110 - void argShowLinearization();  
111 - void argShowXref();  
112 - void argShowObject(char* parameter);  
113 - void argRawStreamData();  
114 - void argFilteredStreamData();  
115 - void argShowNpages();  
116 - void argShowPages();  
117 - void argWithImages();  
118 - void argJson();  
119 - void argJsonKey(char* parameter);  
120 - void argJsonObject(char* parameter);  
121 - void argCheck();  
122 - void argOptimizeImages();  
123 - void argExternalizeInlineImages();  
124 - void argKeepInlineImages();  
125 - void argRemovePageLabels();  
126 - void argOiMinWidth(char* parameter);  
127 - void argOiMinHeight(char* parameter);  
128 - void argOiMinArea(char* parameter);  
129 - void argIiMinBytes(char* parameter);  
130 - void arg40Print(char* parameter);  
131 - void arg40Modify(char* parameter);  
132 - void arg40Extract(char* parameter);  
133 - void arg40Annotate(char* parameter);  
134 - void arg128Accessibility(char* parameter);  
135 - void arg128Extract(char* parameter);  
136 - void arg128Print(char* parameter);  
137 - void arg128Modify(char* parameter);  
138 - void arg128ClearTextMetadata();  
139 - void arg128Assemble(char* parameter);  
140 - void arg128Annotate(char* parameter);  
141 - void arg128Form(char* parameter);  
142 - void arg128ModOther(char* parameter);  
143 - void arg128UseAes(char* parameter);  
144 - void arg128ForceV4();  
145 - void arg256ForceR5();  
146 - void argEncryptPositional(char* arg);  
147 - void argEndEncrypt();  
148 - void argUOpositional(char* arg);  
149 - void argUOto(char* parameter);  
150 - void argUOfrom(char* parameter);  
151 - void argUOrepeat(char* parameter);  
152 - void argUOpassword(char* parameter);  
153 - void argEndUnderOverlay();  
154 - void argReplaceInput();  
155 - void argIsEncrypted();  
156 - void argRequiresPassword();  
157 - void argAApositional(char* arg);  
158 - void argAAKey(char* parameter);  
159 - void argAAFilename(char* parameter);  
160 - void argAACreationDate(char* parameter);  
161 - void argAAModDate(char* parameter);  
162 - void argAAMimeType(char* parameter);  
163 - void argAADescription(char* parameter);  
164 - void argAAReplace();  
165 - void argEndAddAttachment();  
166 - void argCApositional(char* arg);  
167 - void argCAprefix(char* parameter);  
168 - void argCApassword(char* parameter);  
169 - void argEndCopyAttachments();  
170 -  
171 - void usage(std::string const& message);  
172 - void initOptionTable();  
173 - void doFinalChecks();  
174 - void parseUnderOverlayOptions(QPDFJob::UnderOverlay*);  
175 - void parseRotationParameter(std::string const&);  
176 - std::vector<int> parseNumrange(char const* range, int max,  
177 - bool throw_error = false);  
178 -  
179 - QPDFArgParser ap;  
180 - QPDFJob& o;  
181 - std::vector<char*> accumulated_args;  
182 - char* pages_password;  
183 -};  
184 -  
185 -ArgParser::ArgParser(int argc, char* argv[], QPDFJob& o) :  
186 - ap(argc, argv, "QPDF_EXECUTABLE"),  
187 - o(o),  
188 - pages_password(nullptr)  
189 -{  
190 - initOptionTable();  
191 -}  
192 -  
193 -void  
194 -ArgParser::initOptionTable()  
195 -{  
196 - auto b = [this](void (ArgParser::*f)()) {  
197 - return QPDFArgParser::bindBare(f, this);  
198 - };  
199 - auto p = [this](void (ArgParser::*f)(char *)) {  
200 - return QPDFArgParser::bindParam(f, this);  
201 - };  
202 -  
203 - this->ap.addFinalCheck(b(&ArgParser::doFinalChecks));  
204 -  
205 - this->ap.selectHelpOptionTable();  
206 - this->ap.addBare("help", b(&ArgParser::argHelp));  
207 - this->ap.addBare("version", b(&ArgParser::argVersion));  
208 - this->ap.addBare("copyright", b(&ArgParser::argCopyright));  
209 - this->ap.addBare("json-help", b(&ArgParser::argJsonHelp));  
210 - this->ap.addBare("show-crypto", b(&ArgParser::argShowCrypto));  
211 -  
212 - this->ap.selectMainOptionTable();  
213 - char const* yn[] = {"y", "n", 0};  
214 - this->ap.addPositional(p(&ArgParser::argPositional));  
215 - this->ap.addRequiredParameter("password",  
216 - p(&ArgParser::argPassword), "password");  
217 - this->ap.addRequiredParameter("password-file",  
218 - p(&ArgParser::argPasswordFile), "password-file");  
219 - this->ap.addBare("empty", b(&ArgParser::argEmpty));  
220 - this->ap.addBare("linearize", b(&ArgParser::argLinearize));  
221 - this->ap.addBare("decrypt", b(&ArgParser::argDecrypt));  
222 - this->ap.addBare("password-is-hex-key", b(&ArgParser::argPasswordIsHexKey));  
223 - this->ap.addBare("suppress-password-recovery",  
224 - b(&ArgParser::argSuppressPasswordRecovery));  
225 - char const* password_mode_choices[] =  
226 - {"bytes", "hex-bytes", "unicode", "auto", 0};  
227 - this->ap.addRequiredChoices("password-mode",  
228 - p(&ArgParser::argPasswordMode), password_mode_choices);  
229 - this->ap.addRequiredParameter("copy-encryption",  
230 - p(&ArgParser::argCopyEncryption), "file");  
231 - this->ap.addRequiredParameter("encryption-file-password",  
232 - p(&ArgParser::argEncryptionFilePassword), "password");  
233 - this->ap.addRequiredParameter("rotate",  
234 - p(&ArgParser::argRotate), "[+|-]angle:page-range");  
235 - char const* stream_data_choices[] =  
236 - {"compress", "preserve", "uncompress", 0};  
237 - this->ap.addOptionalParameter("collate",p(&ArgParser::argCollate));  
238 - this->ap.addBare("flatten-rotation", b(&ArgParser::argFlattenRotation));  
239 - this->ap.addBare("list-attachments", b(&ArgParser::argListAttachments));  
240 - this->ap.addRequiredParameter("show-attachment",  
241 - p(&ArgParser::argShowAttachment), "attachment-key");  
242 - this->ap.addRequiredParameter("remove-attachment",  
243 - p(&ArgParser::argRemoveAttachment), "attachment-key");  
244 - this->ap.addBare("add-attachment", b(&ArgParser::argAddAttachment));  
245 - this->ap.addBare(  
246 - "copy-attachments-from", b(&ArgParser::argCopyAttachments));  
247 - this->ap.addRequiredChoices("stream-data",  
248 - p(&ArgParser::argStreamData), stream_data_choices);  
249 - this->ap.addRequiredChoices("compress-streams",  
250 - p(&ArgParser::argCompressStreams), yn);  
251 - this->ap.addBare("recompress-flate", b(&ArgParser::argRecompressFlate));  
252 - this->ap.addRequiredParameter("compression-level",  
253 - p(&ArgParser::argCompressionLevel), "level");  
254 - char const* decode_level_choices[] =  
255 - {"none", "generalized", "specialized", "all", 0};  
256 - this->ap.addRequiredChoices("decode-level",  
257 - p(&ArgParser::argDecodeLevel), decode_level_choices);  
258 - this->ap.addRequiredChoices("normalize-content",  
259 - p(&ArgParser::argNormalizeContent), yn);  
260 - this->ap.addBare("suppress-recovery", b(&ArgParser::argSuppressRecovery));  
261 - char const* object_streams_choices[] = {  
262 - "disable", "preserve", "generate", 0};  
263 - this->ap.addRequiredChoices("object-streams",  
264 - p(&ArgParser::argObjectStreams), object_streams_choices);  
265 - this->ap.addBare(  
266 - "ignore-xref-streams", b(&ArgParser::argIgnoreXrefStreams));  
267 - this->ap.addBare("qdf", b(&ArgParser::argQdf));  
268 - this->ap.addBare(  
269 - "preserve-unreferenced", b(&ArgParser::argPreserveUnreferenced));  
270 - this->ap.addBare(  
271 - "preserve-unreferenced-resources",  
272 - b(&ArgParser::argPreserveUnreferencedResources));  
273 - char const* remove_unref_choices[] = {  
274 - "auto", "yes", "no", 0};  
275 - this->ap.addRequiredChoices("remove-unreferenced-resources",  
276 - p(&ArgParser::argRemoveUnreferencedResources), remove_unref_choices);  
277 - this->ap.addRequiredChoices("keep-files-open",  
278 - p(&ArgParser::argKeepFilesOpen), yn);  
279 - this->ap.addRequiredParameter("keep-files-open-threshold",  
280 - p(&ArgParser::argKeepFilesOpenThreshold), "count");  
281 - this->ap.addBare("newline-before-endstream", b(&ArgParser::argNewlineBeforeEndstream));  
282 - this->ap.addRequiredParameter("linearize-pass1",  
283 - p(&ArgParser::argLinearizePass1), "filename");  
284 - this->ap.addBare("coalesce-contents", b(&ArgParser::argCoalesceContents));  
285 - char const* flatten_choices[] = {"all", "print", "screen", 0};  
286 - this->ap.addRequiredChoices("flatten-annotations",  
287 - p(&ArgParser::argFlattenAnnotations), flatten_choices);  
288 - this->ap.addBare("generate-appearances", b(&ArgParser::argGenerateAppearances));  
289 - this->ap.addRequiredParameter("min-version",  
290 - p(&ArgParser::argMinVersion), "version");  
291 - this->ap.addRequiredParameter("force-version",  
292 - p(&ArgParser::argForceVersion), "version");  
293 - this->ap.addOptionalParameter("split-pages",p(&ArgParser::argSplitPages));  
294 - this->ap.addBare("verbose", b(&ArgParser::argVerbose));  
295 - this->ap.addBare("progress", b(&ArgParser::argProgress));  
296 - this->ap.addBare("no-warn", b(&ArgParser::argNoWarn));  
297 - this->ap.addBare("warning-exit-0", b(&ArgParser::argWarningExitZero));  
298 - this->ap.addBare("deterministic-id", b(&ArgParser::argDeterministicId));  
299 - this->ap.addBare("static-id", b(&ArgParser::argStaticId));  
300 - this->ap.addBare("static-aes-iv", b(&ArgParser::argStaticAesIv));  
301 - this->ap.addBare("no-original-object-ids", b(&ArgParser::argNoOriginalObjectIds));  
302 - this->ap.addBare("show-encryption", b(&ArgParser::argShowEncryption));  
303 - this->ap.addBare("show-encryption-key", b(&ArgParser::argShowEncryptionKey));  
304 - this->ap.addBare("check-linearization", b(&ArgParser::argCheckLinearization));  
305 - this->ap.addBare("show-linearization", b(&ArgParser::argShowLinearization));  
306 - this->ap.addBare("show-xref", b(&ArgParser::argShowXref));  
307 - this->ap.addRequiredParameter("show-object",  
308 - p(&ArgParser::argShowObject), "trailer|obj[,gen]");  
309 - this->ap.addBare("raw-stream-data", b(&ArgParser::argRawStreamData));  
310 - this->ap.addBare("filtered-stream-data", b(&ArgParser::argFilteredStreamData));  
311 - this->ap.addBare("show-npages", b(&ArgParser::argShowNpages));  
312 - this->ap.addBare("show-pages", b(&ArgParser::argShowPages));  
313 - this->ap.addBare("with-images", b(&ArgParser::argWithImages));  
314 - this->ap.addBare("json", b(&ArgParser::argJson));  
315 - // QXXXQ  
316 - // The list of selectable top-level keys id duplicated in three  
317 - // places: json_schema, do_json, and initOptionTable.  
318 - char const* json_key_choices[] = {  
319 - "objects", "objectinfo", "pages", "pagelabels", "outlines",  
320 - "acroform", "encrypt", "attachments", 0};  
321 - this->ap.addRequiredChoices("json-key",  
322 - p(&ArgParser::argJsonKey), json_key_choices);  
323 - this->ap.addRequiredParameter("json-object",  
324 - p(&ArgParser::argJsonObject), "trailer|obj[,gen]");  
325 - this->ap.addBare("check", b(&ArgParser::argCheck));  
326 - this->ap.addBare("optimize-images", b(&ArgParser::argOptimizeImages));  
327 - this->ap.addBare("externalize-inline-images", b(&ArgParser::argExternalizeInlineImages));  
328 - this->ap.addBare("keep-inline-images", b(&ArgParser::argKeepInlineImages));  
329 - this->ap.addBare("remove-page-labels", b(&ArgParser::argRemovePageLabels));  
330 - this->ap.addRequiredParameter("oi-min-width",  
331 - p(&ArgParser::argOiMinWidth), "minimum-width");  
332 - this->ap.addRequiredParameter("oi-min-height",  
333 - p(&ArgParser::argOiMinHeight), "minimum-height");  
334 - this->ap.addRequiredParameter("oi-min-area",  
335 - p(&ArgParser::argOiMinArea), "minimum-area");  
336 - this->ap.addRequiredParameter("ii-min-bytes",  
337 - p(&ArgParser::argIiMinBytes), "minimum-bytes");  
338 - this->ap.addBare("overlay", b(&ArgParser::argOverlay));  
339 - this->ap.addBare("underlay", b(&ArgParser::argUnderlay));  
340 - this->ap.addBare("replace-input", b(&ArgParser::argReplaceInput));  
341 - this->ap.addBare("is-encrypted", b(&ArgParser::argIsEncrypted));  
342 - this->ap.addBare("requires-password", b(&ArgParser::argRequiresPassword));  
343 - this->ap.addBare("allow-weak-crypto", b(&ArgParser::argAllowWeakCrypto));  
344 -  
345 - this->ap.selectMainOptionTable();  
346 - this->ap.addBare("pages", b(&ArgParser::argPages));  
347 - this->ap.registerOptionTable(O_PAGES, b(&ArgParser::argEndPages));  
348 - this->ap.addRequiredParameter(  
349 - "password", p(&ArgParser::argPagesPassword), "password");  
350 - this->ap.addPositional(p(&ArgParser::argPagesPositional));  
351 -  
352 - this->ap.selectMainOptionTable();  
353 - this->ap.addBare("encrypt", b(&ArgParser::argEncrypt));  
354 - this->ap.registerOptionTable(O_ENCRYPT, b(&ArgParser::argEndEncrypt));  
355 - this->ap.addPositional(p(&ArgParser::argEncryptPositional));  
356 - this->ap.registerOptionTable(O_ENCRYPT_40, b(&ArgParser::argEndEncrypt));  
357 - this->ap.addRequiredChoices("extract",p(&ArgParser::arg40Extract), yn);  
358 - this->ap.addRequiredChoices("annotate",p(&ArgParser::arg40Annotate), yn);  
359 - this->ap.addRequiredChoices("print",p(&ArgParser::arg40Print), yn);  
360 - this->ap.addRequiredChoices("modify",p(&ArgParser::arg40Modify), yn);  
361 - this->ap.registerOptionTable(O_ENCRYPT_128, b(&ArgParser::argEndEncrypt));  
362 - this->ap.registerOptionTable(O_ENCRYPT_256, b(&ArgParser::argEndEncrypt));  
363 - for (char const* k: {O_ENCRYPT_128, O_ENCRYPT_256})  
364 - {  
365 - this->ap.selectOptionTable(k);  
366 - this->ap.addRequiredChoices("accessibility",  
367 - p(&ArgParser::arg128Accessibility), yn);  
368 - this->ap.addRequiredChoices("extract", p(&ArgParser::arg128Extract), yn);  
369 - char const* print128_choices[] = {"full", "low", "none", 0};  
370 - this->ap.addRequiredChoices("print",  
371 - p(&ArgParser::arg128Print), print128_choices);  
372 - this->ap.addRequiredChoices("assemble",p(&ArgParser::arg128Assemble), yn);  
373 - this->ap.addRequiredChoices("annotate",p(&ArgParser::arg128Annotate), yn);  
374 - this->ap.addRequiredChoices("form",p(&ArgParser::arg128Form), yn);  
375 - this->ap.addRequiredChoices("modify-other",p(&ArgParser::arg128ModOther), yn);  
376 - char const* modify128_choices[] =  
377 - {"all", "annotate", "form", "assembly", "none", 0};  
378 - this->ap.addRequiredChoices("modify",  
379 - p(&ArgParser::arg128Modify), modify128_choices);  
380 - this->ap.addBare("cleartext-metadata", b(&ArgParser::arg128ClearTextMetadata));  
381 - }  
382 -  
383 - this->ap.selectOptionTable(O_ENCRYPT_128);  
384 - this->ap.addRequiredChoices("use-aes",p(&ArgParser::arg128UseAes), yn);  
385 - this->ap.addBare("force-V4", b(&ArgParser::arg128ForceV4));  
386 -  
387 - this->ap.selectOptionTable(O_ENCRYPT_256);  
388 - this->ap.addBare("force-R5", b(&ArgParser::arg256ForceR5));  
389 - this->ap.addBare("allow-insecure", b(&ArgParser::argAllowInsecure));  
390 -  
391 - this->ap.registerOptionTable(O_UNDER_OVERLAY, b(&ArgParser::argEndUnderOverlay));  
392 - this->ap.addPositional(p(&ArgParser::argUOpositional));  
393 - this->ap.addRequiredParameter("to",  
394 - p(&ArgParser::argUOto), "page-range");  
395 - this->ap.addRequiredParameter("from",  
396 - p(&ArgParser::argUOfrom), "page-range");  
397 - this->ap.addRequiredParameter("repeat",  
398 - p(&ArgParser::argUOrepeat), "page-range");  
399 - this->ap.addRequiredParameter("password",  
400 - p(&ArgParser::argUOpassword), "password");  
401 -  
402 - this->ap.registerOptionTable(O_ATTACHMENT, b(&ArgParser::argEndAddAttachment));  
403 - this->ap.addPositional(p(&ArgParser::argAApositional));  
404 - this->ap.addRequiredParameter("key",  
405 - p(&ArgParser::argAAKey), "attachment-key");  
406 - this->ap.addRequiredParameter("filename",  
407 - p(&ArgParser::argAAFilename), "filename");  
408 - this->ap.addRequiredParameter("creationdate",  
409 - p(&ArgParser::argAACreationDate), "creation-date");  
410 - this->ap.addRequiredParameter("moddate",  
411 - p(&ArgParser::argAAModDate), "modification-date");  
412 - this->ap.addRequiredParameter("mimetype",  
413 - p(&ArgParser::argAAMimeType), "mime/type");  
414 - this->ap.addRequiredParameter("description",  
415 - p(&ArgParser::argAADescription), "description");  
416 - this->ap.addBare("replace", b(&ArgParser::argAAReplace));  
417 -  
418 - this->ap.registerOptionTable(O_COPY_ATTACHMENT, b(&ArgParser::argEndCopyAttachments));  
419 - this->ap.addPositional(p(&ArgParser::argCApositional));  
420 - this->ap.addRequiredParameter("prefix",  
421 - p(&ArgParser::argCAprefix), "prefix");  
422 - this->ap.addRequiredParameter("password",  
423 - p(&ArgParser::argCApassword), "password");  
424 -}  
425 -  
426 -void  
427 -ArgParser::argPositional(char* arg)  
428 -{  
429 - if (o.infilename == 0)  
430 - {  
431 - o.infilename = arg;  
432 - }  
433 - else if (o.outfilename == 0)  
434 - {  
435 - o.outfilename = arg;  
436 - }  
437 - else  
438 - {  
439 - usage(std::string("unknown argument ") + arg);  
440 - }  
441 -}  
442 -  
443 -void  
444 -ArgParser::argVersion()  
445 -{  
446 - std::cout  
447 - << whoami << " version " << QPDF::QPDFVersion() << std::endl  
448 - << "Run " << whoami << " --copyright to see copyright and license information."  
449 - << std::endl;  
450 -}  
451 -  
452 -void  
453 -ArgParser::argCopyright()  
454 -{  
455 - // Make sure the output looks right on an 80-column display.  
456 - // 1 2 3 4 5 6 7 8  
457 - // 12345678901234567890123456789012345678901234567890123456789012345678901234567890  
458 - std::cout  
459 - << whoami << " version " << QPDF::QPDFVersion() << std::endl  
460 - << std::endl  
461 - << "Copyright (c) 2005-2021 Jay Berkenbilt"  
462 - << std::endl  
463 - << "QPDF is licensed under the Apache License, Version 2.0 (the \"License\");"  
464 - << std::endl  
465 - << "you may not use this file except in compliance with the License."  
466 - << std::endl  
467 - << "You may obtain a copy of the License at"  
468 - << std::endl  
469 - << std::endl  
470 - << " http://www.apache.org/licenses/LICENSE-2.0"  
471 - << std::endl  
472 - << std::endl  
473 - << "Unless required by applicable law or agreed to in writing, software"  
474 - << std::endl  
475 - << "distributed under the License is distributed on an \"AS IS\" BASIS,"  
476 - << std::endl  
477 - << "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied."  
478 - << std::endl  
479 - << "See the License for the specific language governing permissions and"  
480 - << std::endl  
481 - << "limitations under the License."  
482 - << std::endl  
483 - << std::endl  
484 - << "Versions of qpdf prior to version 7 were released under the terms"  
485 - << std::endl  
486 - << "of version 2.0 of the Artistic License. At your option, you may"  
487 - << std::endl  
488 - << "continue to consider qpdf to be licensed under those terms. Please"  
489 - << std::endl  
490 - << "see the manual for additional information."  
491 - << std::endl;  
492 -}  
493 -  
494 -void  
495 -ArgParser::argHelp()  
496 -{  
497 - std::cout  
498 - // 12345678901234567890123456789012345678901234567890123456789012345678901234567890  
499 - << "Usage: qpdf [options] {infile | --empty} [page_selection_options] outfile\n"  
500 - << "\n"  
501 - << "An option summary appears below. Please see the documentation for details.\n"  
502 - << "\n"  
503 - << "If @filename appears anywhere in the command-line, each line of filename\n"  
504 - << "will be interpreted as an argument. No interpolation is done. Line\n"  
505 - << "terminators are stripped, but leading and trailing whitespace is\n"  
506 - << "intentionally preserved. @- can be specified to read from standard input.\n"  
507 - << "\n"  
508 - << "The output file can be - to indicate writing to standard output, or it can\n"  
509 - << "be --replace-input to cause qpdf to replace the input file with the output.\n"  
510 - << "\n"  
511 - << "Note that when contradictory options are provided, whichever options are\n"  
512 - << "provided last take precedence.\n"  
513 - << "\n"  
514 - << "\n"  
515 - << "Basic Options\n"  
516 - << "-------------\n"  
517 - << "\n"  
518 - << "--version show version of qpdf\n"  
519 - << "--copyright show qpdf's copyright and license information\n"  
520 - << "--help show command-line argument help\n"  
521 - << "--show-crypto show supported crypto providers; default is first\n"  
522 - << "--completion-bash output a bash complete command you can eval\n"  
523 - << "--completion-zsh output a zsh complete command you can eval\n"  
524 - << "--password=password specify a password for accessing encrypted files\n"  
525 - << "--password-file=file get the password the first line \"file\"; use \"-\"\n"  
526 - << " to read the password from stdin (without prompt or\n"  
527 - << " disabling echo, so use with caution)\n"  
528 - << "--is-encrypted silently exit 0 if the file is encrypted or 2\n"  
529 - << " if not; useful for shell scripts\n"  
530 - << "--requires-password silently exit 0 if a password (other than as\n"  
531 - << " supplied) is required, 2 if the file is not\n"  
532 - << " encrypted, or 3 if the file is encrypted\n"  
533 - << " but requires no password or the supplied password\n"  
534 - << " is correct; useful for shell scripts\n"  
535 - << "--verbose provide additional informational output\n"  
536 - << "--progress give progress indicators while writing output\n"  
537 - << "--no-warn suppress warnings\n"  
538 - << "--warning-exit-0 exit with code 0 instead of 3 if there are warnings\n"  
539 - << "--linearize generated a linearized (web optimized) file\n"  
540 - << "--replace-input use in place of specifying an output file; qpdf will\n"  
541 - << " replace the input file with the output\n"  
542 - << "--copy-encryption=file copy encryption parameters from specified file\n"  
543 - << "--encryption-file-password=password\n"  
544 - << " password used to open the file from which encryption\n"  
545 - << " parameters are being copied\n"  
546 - << "--allow-weak-crypto allow creation of files using weak cryptographic\n"  
547 - << " algorithms\n"  
548 - << "--encrypt options -- generate an encrypted file\n"  
549 - << "--decrypt remove any encryption on the file\n"  
550 - << "--password-is-hex-key treat primary password option as a hex-encoded key\n"  
551 - << "--suppress-password-recovery\n"  
552 - << " do not attempt recovering from password string\n"  
553 - << " encoding errors\n"  
554 - << "--password-mode=mode control qpdf's encoding of passwords\n"  
555 - << "--pages options -- select specific pages from one or more files\n"  
556 - << "--collate=n causes files specified in --pages to be collated\n"  
557 - << " in groups of n pages (default 1) rather than\n"  
558 - << " concatenated\n"  
559 - << "--flatten-rotation move page rotation from /Rotate key to content\n"  
560 - << "--rotate=[+|-]angle[:page-range]\n"  
561 - << " rotate each specified page 0, 90, 180, or 270\n"  
562 - << " degrees; rotate all pages if no page range is given\n"  
563 - << "--split-pages=[n] write each output page to a separate file\n"  
564 - << "--overlay options -- overlay pages from another file\n"  
565 - << "--underlay options -- underlay pages from another file\n"  
566 - << "\n"  
567 - << "Note that you can use the @filename or @- syntax for any argument at any\n"  
568 - << "point in the command. This provides a good way to specify a password without\n"  
569 - << "having to explicitly put it on the command line. @filename or @- must be a\n"  
570 - << "word by itself. Syntax such as --arg=@filename doesn't work.\n"  
571 - << "\n"  
572 - << "If none of --copy-encryption, --encrypt or --decrypt are given, qpdf will\n"  
573 - << "preserve any encryption data associated with a file.\n"  
574 - << "\n"  
575 - << "Note that when copying encryption parameters from another file, all\n"  
576 - << "parameters will be copied, including both user and owner passwords, even\n"  
577 - << "if the user password is used to open the other file. This works even if\n"  
578 - << "the owner password is not known.\n"  
579 - << "\n"  
580 - << "The --password-is-hex-key option overrides the normal computation of\n"  
581 - << "encryption keys. It only applies to the password used to open the main\n"  
582 - << "file. This option is not ordinarily useful but can be helpful for forensic\n"  
583 - << "or investigatory purposes. See manual for further discussion.\n"  
584 - << "\n"  
585 - << "The --rotate flag can be used to specify pages to rotate pages either\n"  
586 - << "0, 90, 180, or 270 degrees. The page range is specified in the same\n"  
587 - << "format as with the --pages option, described below. Repeat the option\n"  
588 - << "to rotate multiple groups of pages. If the angle is preceded by + or -,\n"  
589 - << "it is added to or subtracted from the original rotation. Otherwise, the\n"  
590 - << "rotation angle is set explicitly to the given value. You almost always\n"  
591 - << "want to use + or - unless you are certain about the internals of the PDF\n"  
592 - << "you are working with.\n"  
593 - << "\n"  
594 - << "If --split-pages is specified, each page is written to a separate output\n"  
595 - << "file. File names are generated as follows:\n"  
596 - << "* If the string %d appears in the output file name, it is replaced with a\n"  
597 - << " zero-padded page range starting from 1\n"  
598 - << "* Otherwise, if the output file name ends in .pdf (case insensitive), a\n"  
599 - << " zero-padded page range, preceded by a dash, is inserted before the file\n"  
600 - << " extension\n"  
601 - << "* Otherwise, the file name is appended with a zero-padded page range\n"  
602 - << " preceded by a dash.\n"  
603 - << "Page ranges are single page numbers for single-page groups or first-last\n"  
604 - << "for multipage groups.\n"  
605 - << "\n"  
606 - << "\n"  
607 - << "Encryption Options\n"  
608 - << "------------------\n"  
609 - << "\n"  
610 - << " --encrypt user-password owner-password key-length flags --\n"  
611 - << "\n"  
612 - << "Note that -- terminates parsing of encryption flags.\n"  
613 - << "\n"  
614 - << "Either or both of the user password and the owner password may be\n"  
615 - << "empty strings.\n"  
616 - << "\n"  
617 - << "key-length may be 40, 128, or 256\n"  
618 - << "\n"  
619 - << "Additional flags are dependent upon key length.\n"  
620 - << "\n"  
621 - << " If 40:\n"  
622 - << "\n"  
623 - << " --print=[yn] allow printing\n"  
624 - << " --modify=[yn] allow document modification\n"  
625 - << " --extract=[yn] allow text/graphic extraction\n"  
626 - << " --annotate=[yn] allow comments and form fill-in and signing\n"  
627 - << "\n"  
628 - << " If 128:\n"  
629 - << "\n"  
630 - << " --accessibility=[yn] allow accessibility to visually impaired\n"  
631 - << " --extract=[yn] allow other text/graphic extraction\n"  
632 - << " --print=print-opt control printing access\n"  
633 - << " --assemble=[yn] allow document assembly\n"  
634 - << " --annotate=[yn] allow commenting/filling form fields\n"  
635 - << " --form=[yn] allow filling form fields\n"  
636 - << " --modify-other=[yn] allow other modifications\n"  
637 - << " --modify=modify-opt control modify access (old way)\n"  
638 - << " --cleartext-metadata prevents encryption of metadata\n"  
639 - << " --use-aes=[yn] indicates whether to use AES encryption\n"  
640 - << " --force-V4 forces use of V=4 encryption handler\n"  
641 - << "\n"  
642 - << " If 256, options are the same as 128 with these exceptions:\n"  
643 - << " --force-V4 this option is not available with 256-bit keys\n"  
644 - << " --use-aes this option is always on with 256-bit keys\n"  
645 - << " --force-R5 forces use of deprecated R=5 encryption\n"  
646 - << " --allow-insecure allow the owner password to be empty when the\n"  
647 - << " user password is not empty\n"  
648 - << "\n"  
649 - << " print-opt may be:\n"  
650 - << "\n"  
651 - << " full allow full printing\n"  
652 - << " low allow only low-resolution printing\n"  
653 - << " none disallow printing\n"  
654 - << "\n"  
655 - << " modify-opt may be:\n"  
656 - << "\n"  
657 - << " all allow full document modification\n"  
658 - << " annotate allow comment authoring and form operations\n"  
659 - << " form allow form field fill-in and signing\n"  
660 - << " assembly allow document assembly only\n"  
661 - << " none allow no modifications\n"  
662 - << "\n"  
663 - << "The default for each permission option is to be fully permissive. Please\n"  
664 - << "refer to the manual for more details on the modify options.\n"  
665 - << "\n"  
666 - << "Specifying cleartext-metadata forces the PDF version to at least 1.5.\n"  
667 - << "Specifying use of AES forces the PDF version to at least 1.6. These\n"  
668 - << "options are both off by default.\n"  
669 - << "\n"  
670 - << "The --force-V4 flag forces the V=4 encryption handler introduced in PDF 1.5\n"  
671 - << "to be used even if not otherwise needed. This option is primarily useful\n"  
672 - << "for testing qpdf and has no other practical use.\n"  
673 - << "\n"  
674 - << "A warning will be issued if you attempt to encrypt a file with a format that\n"  
675 - << "uses a weak cryptographic algorithm such as RC4. To suppress the warning,\n"  
676 - << "specify the option --allow-weak-crypto. This option is outside of encryption\n"  
677 - << "options (e.g. --allow-week-crypto --encrypt u o 128 --)\n"  
678 - << "\n"  
679 - << "\n"  
680 - << "Password Modes\n"  
681 - << "--------------\n"  
682 - << "\n"  
683 - << "The --password-mode controls how qpdf interprets passwords supplied\n"  
684 - << "on the command-line. qpdf's default behavior is correct in almost all\n"  
685 - << "cases, but you can fine-tune with this option.\n"  
686 - << "\n"  
687 - << " bytes: use the password literally as supplied\n"  
688 - << " hex-bytes: interpret the password as a hex-encoded byte string\n"  
689 - << " unicode: interpret the password as a UTF-8 encoded string\n"  
690 - << " auto: attempt to infer the encoding and adjust as needed\n"  
691 - << "\n"  
692 - << "This is a complex topic. See the manual for a complete discussion.\n"  
693 - << "\n"  
694 - << "\n"  
695 - << "Page Selection Options\n"  
696 - << "----------------------\n"  
697 - << "\n"  
698 - << "These options allow pages to be selected from one or more PDF files.\n"  
699 - << "Whatever file is given as the primary input file is used as the\n"  
700 - << "starting point, but its pages are replaced with pages as specified.\n"  
701 - << "\n"  
702 - << "--keep-files-open=[yn]\n"  
703 - << "--keep-files-open-threshold=count\n"  
704 - << "--pages file [ --password=password ] [ page-range ] ... --\n"  
705 - << "\n"  
706 - << "For each file that pages should be taken from, specify the file, a\n"  
707 - << "password needed to open the file (if any), and a page range. The\n"  
708 - << "password needs to be given only once per file. If any of the input\n"  
709 - << "files are the same as the primary input file or the file used to copy\n"  
710 - << "encryption parameters (if specified), you do not need to repeat the\n"  
711 - << "password here. The same file can be repeated multiple times. The\n"  
712 - << "filename \".\" may be used to refer to the current input file. All\n"  
713 - << "non-page data (info, outlines, page numbers, etc. are taken from the\n"  
714 - << "primary input file. To discard this, use --empty as the primary\n"  
715 - << "input.\n"  
716 - << "\n"  
717 - << "By default, when more than 200 distinct files are specified, qpdf will\n"  
718 - << "close each file when not being referenced. With 200 files or fewer, all\n"  
719 - << "files will be kept open at the same time. This behavior can be overridden\n"  
720 - << "by specifying --keep-files-open=[yn]. Closing and opening files can have\n"  
721 - << "very high overhead on certain file systems, especially networked file\n"  
722 - << "systems. The threshold of 200 can be modified with\n"  
723 - << "--keep-files-open-threshold\n"  
724 - << "\n"  
725 - << "The page range is a set of numbers separated by commas, ranges of\n"  
726 - << "numbers separated dashes, or combinations of those. The character\n"  
727 - << "\"z\" represents the last page. A number preceded by an \"r\" indicates\n"  
728 - << "to count from the end, so \"r3-r1\" would be the last three pages of the\n"  
729 - << "document. Pages can appear in any order. Ranges can appear with a\n"  
730 - << "high number followed by a low number, which causes the pages to appear in\n"  
731 - << "reverse. Numbers may be repeated. A page range may be appended with :odd\n"  
732 - << "to indicate odd pages in the selected range or :even to indicate even\n"  
733 - << "pages.\n"  
734 - << "\n"  
735 - << "If the page range is omitted, the range of 1-z is assumed. qpdf decides\n"  
736 - << "that the page range is omitted if the range argument is either -- or a\n"  
737 - << "valid file name and not a valid range.\n"  
738 - << "\n"  
739 - << "The usual behavior of --pages is to add all pages from the first file,\n"  
740 - << "then all pages from the second file, and so on. If the --collate option\n"  
741 - << "is specified, then pages are collated instead. In other words, qpdf takes\n"  
742 - << "the first page from the first file, the first page from the second file,\n"  
743 - << "and so on until it runs out of files; then it takes the second page from\n"  
744 - << "each file, etc. When a file runs out of pages, it is skipped until all\n"  
745 - << "specified pages are taken from all files.\n"  
746 - << "\n"  
747 - << "See the manual for examples and a discussion of additional subtleties.\n"  
748 - << "\n"  
749 - << "\n"  
750 - << "Overlay and Underlay Options\n"  
751 - << "----------------------------\n"  
752 - << "\n"  
753 - << "These options allow pages from another file to be overlaid or underlaid\n"  
754 - << "on the primary output. Overlaid pages are drawn on top of the destination\n"  
755 - << "page and may obscure the page. Underlaid pages are drawn below the\n"  
756 - << "destination page.\n"  
757 - << "\n"  
758 - << "{--overlay | --underlay } file\n"  
759 - " [ --password=password ]\n"  
760 - " [ --to=page-range ]\n"  
761 - " [ --from=[page-range] ]\n"  
762 - " [ --repeat=page-range ]\n"  
763 - " --\n"  
764 - << "\n"  
765 - << "For overlay and underlay, a file and optional password are specified, along\n"  
766 - << "with a series of optional page ranges. The default behavior is that each\n"  
767 - << "page of the overlay or underlay file is imposed on the corresponding page\n"  
768 - << "of the primary output until it runs out of pages, and any extra pages are\n"  
769 - << "ignored. The page range options all take page ranges in the same form as\n"  
770 - << "the --pages option. They have the following meanings:\n"  
771 - << "\n"  
772 - << " --to: the pages in the primary output to which overlay/underlay is\n"  
773 - << " applied\n"  
774 - << " --from: the pages from the overlay/underlay file that are used\n"  
775 - << " --repeat: pages from the overlay/underlay that are repeated after\n"  
776 - << " any \"from\" pages have been exhausted\n"  
777 - << "\n"  
778 - << "\n"  
779 - << "Embedded Files/Attachments Options\n"  
780 - << "----------------------------------\n"  
781 - << "\n"  
782 - << "These options can be used to work with embedded files, also known as\n"  
783 - << "attachments.\n"  
784 - << "\n"  
785 - << "--list-attachments show key and stream number for embedded files;\n"  
786 - << " combine with --verbose for more detailed information\n"  
787 - << "--show-attachment=key write the contents of the specified attachment to\n"  
788 - << " standard output as binary data\n"  
789 - << "--add-attachment file options --\n"  
790 - << " add or replace an attachment\n"  
791 - << "--remove-attachment=key remove the specified attachment; repeatable\n"  
792 - << "--copy-attachments-from file options --\n"  
793 - << " copy attachments from another file\n"  
794 - << "\n"  
795 - << "The \"key\" option is the unique name under which the attachment is registered\n"  
796 - << "within the PDF file. You can get this using the --list-attachments option. This\n"  
797 - << "is usually the same as the filename, but it doesn't have to be.\n"  
798 - << "\n"  
799 - << "Options for adding attachments:\n"  
800 - << "\n"  
801 - << " file path to the file to attach\n"  
802 - << " --key=key the name of this in the embedded files table;\n"  
803 - << " defaults to the last path element of file\n"  
804 - << " --filename=name the file name of the attachment; this is what is\n"  
805 - << " usually displayed to the user; defaults to the\n"  
806 - << " last path element of file\n"  
807 - << " --creationdate=date creation date in PDF format; defaults to the\n"  
808 - << " current time\n"  
809 - << " --moddate=date modification date in PDF format; defaults to the\n"  
810 - << " current time\n"  
811 - << " --mimetype=type/subtype mime type of attachment (e.g. application/pdf)\n"  
812 - << " --description=\"text\" attachment description\n"  
813 - << " --replace replace any existing attachment with the same key\n"  
814 - << "\n"  
815 - << "Options for copying attachments:\n"  
816 - << "\n"  
817 - << " file file whose attachments should be copied\n"  
818 - << " --password=password password to open the other file, if needed\n"  
819 - << " --prefix=prefix a prefix to insert in front of each key;\n"  
820 - << " required if needed to ensure each attachment\n"  
821 - << " has a unique key\n"  
822 - << "\n"  
823 - << "Date format: D:yyyymmddhhmmss<z> where <z> is either Z for UTC or a timezone\n"  
824 - << "offset in the form -hh'mm' or +hh'mm'.\n"  
825 - << "Examples: D:20210207161528-05'00', D:20210207211528Z\n"  
826 - << "\n"  
827 - << "\n"  
828 - << "Advanced Parsing Options\n"  
829 - << "------------------------\n"  
830 - << "\n"  
831 - << "These options control aspects of how qpdf reads PDF files. Mostly these are\n"  
832 - << "of use to people who are working with damaged files. There is little reason\n"  
833 - << "to use these options unless you are trying to solve specific problems.\n"  
834 - << "\n"  
835 - << "--suppress-recovery prevents qpdf from attempting to recover damaged files\n"  
836 - << "--ignore-xref-streams tells qpdf to ignore any cross-reference streams\n"  
837 - << "\n"  
838 - << "\n"  
839 - << "Advanced Transformation Options\n"  
840 - << "-------------------------------\n"  
841 - << "\n"  
842 - << "These transformation options control fine points of how qpdf creates\n"  
843 - << "the output file. Mostly these are of use only to people who are very\n"  
844 - << "familiar with the PDF file format or who are PDF developers.\n"  
845 - << "\n"  
846 - << "--stream-data=option controls transformation of stream data (below)\n"  
847 - << "--compress-streams=[yn] controls whether to compress streams on output\n"  
848 - << "--decode-level=option controls how to filter streams from the input\n"  
849 - << "--recompress-flate recompress streams already compressed with Flate\n"  
850 - << "--compression-level=n set zlib compression level; most effective with\n"  
851 - << " --recompress-flate --object-streams=generate\n"  
852 - << "--normalize-content=[yn] enables or disables normalization of content streams\n"  
853 - << "--object-streams=mode controls handing of object streams\n"  
854 - << "--preserve-unreferenced preserve unreferenced objects\n"  
855 - << "--remove-unreferenced-resources={auto,yes,no}\n"  
856 - << " whether to remove unreferenced page resources\n"  
857 - << "--preserve-unreferenced-resources\n"  
858 - << " synonym for --remove-unreferenced-resources=no\n"  
859 - << "--newline-before-endstream always put a newline before endstream\n"  
860 - << "--coalesce-contents force all pages' content to be a single stream\n"  
861 - << "--flatten-annotations=option\n"  
862 - << " incorporate rendering of annotations into page\n"  
863 - << " contents including those for interactive form\n"  
864 - << " fields; may also want --generate-appearances\n"  
865 - << "--generate-appearances generate appearance streams for form fields\n"  
866 - << "--optimize-images compress images with DCT (JPEG) when advantageous\n"  
867 - << "--oi-min-width=w do not optimize images whose width is below w;\n"  
868 - << " default is 128. Use 0 to mean no minimum\n"  
869 - << "--oi-min-height=h do not optimize images whose height is below h\n"  
870 - << " default is 128. Use 0 to mean no minimum\n"  
871 - << "--oi-min-area=a do not optimize images whose pixel count is below a\n"  
872 - << " default is 16,384. Use 0 to mean no minimum\n"  
873 - << "--externalize-inline-images convert inline images to regular images; by\n"  
874 - << " default, images of at least 1,024 bytes are\n"  
875 - << " externalized\n"  
876 - << "--ii-min-bytes=bytes specify minimum size of inline images to be\n"  
877 - << " converted to regular images\n"  
878 - << "--keep-inline-images exclude inline images from image optimization\n"  
879 - << "--remove-page-labels remove any page labels present in the output file\n"  
880 - << "--qdf turns on \"QDF mode\" (below)\n"  
881 - << "--linearize-pass1=file write intermediate pass of linearized file\n"  
882 - << " for debugging\n"  
883 - << "--min-version=version sets the minimum PDF version of the output file\n"  
884 - << "--force-version=version forces this to be the PDF version of the output file\n"  
885 - << "\n"  
886 - << "Options for --flatten-annotations are all, print, or screen. If the option\n"  
887 - << "is print, only annotations marked as print are included. If the option is\n"  
888 - << "screen, options marked as \"no view\" are excluded. Otherwise, annotations\n"  
889 - << "are flattened regardless of the presence of print or NoView flags. It is\n"  
890 - << "common for PDF files to have a flag set that appearance streams need to be\n"  
891 - << "regenerated. This happens when someone changes a form value with software\n"  
892 - << "that does not know how to render the new value. qpdf will not flatten form\n"  
893 - << "fields in files like this. If you get this warning, you have two choices:\n"  
894 - << "either use qpdf's --generate-appearances flag to tell qpdf to go ahead and\n"  
895 - << "regenerate appearances, or use some other tool to generate the appearances.\n"  
896 - << "qpdf does a pretty good job with most forms when only ASCII and \"Windows\n"  
897 - << "ANSI\" characters are used in form field values, but if your form fields\n"  
898 - << "contain other characters, rich text, or are other than left justified, you\n"  
899 - << "will get better results first saving with other software.\n"  
900 - << "\n"  
901 - << "Version numbers may be expressed as major.minor.extension-level, so 1.7.3\n"  
902 - << "means PDF version 1.7 at extension level 3.\n"  
903 - << "\n"  
904 - << "Values for stream data options:\n"  
905 - << "\n"  
906 - << " compress recompress stream data when possible (default)\n"  
907 - << " preserve leave all stream data as is\n"  
908 - << " uncompress uncompress stream data when possible\n"  
909 - << "\n"  
910 - << "Values for object stream mode:\n"  
911 - << "\n"  
912 - << " preserve preserve original object streams (default)\n"  
913 - << " disable don't write any object streams\n"  
914 - << " generate use object streams wherever possible\n"  
915 - << "\n"  
916 - << "When --compress-streams=n is specified, this overrides the default behavior\n"  
917 - << "of qpdf, which is to attempt compress uncompressed streams. Setting\n"  
918 - << "stream data mode to uncompress or preserve has the same effect.\n"  
919 - << "\n"  
920 - << "The --decode-level parameter may be set to one of the following values:\n"  
921 - << " none do not decode streams\n"  
922 - << " generalized decode streams compressed with generalized filters\n"  
923 - << " including LZW, Flate, and the ASCII encoding filters.\n"  
924 - << " specialized additionally decode streams with non-lossy specialized\n"  
925 - << " filters including RunLength\n"  
926 - << " all additionally decode streams with lossy filters\n"  
927 - << " including DCT (JPEG)\n"  
928 - << "\n"  
929 - << "In qdf mode, by default, content normalization is turned on, and the\n"  
930 - << "stream data mode is set to uncompress. QDF mode does not support\n"  
931 - << "linearized files. The --linearize flag disables qdf mode.\n"  
932 - << "\n"  
933 - << "Setting the minimum PDF version of the output file may raise the version\n"  
934 - << "but will never lower it. Forcing the PDF version of the output file may\n"  
935 - << "set the PDF version to a lower value than actually allowed by the file's\n"  
936 - << "contents. You should only do this if you have no other possible way to\n"  
937 - << "open the file or if you know that the file definitely doesn't include\n"  
938 - << "features not supported later versions.\n"  
939 - << "\n"  
940 - << "Testing, Inspection, and Debugging Options\n"  
941 - << "------------------------------------------\n"  
942 - << "\n"  
943 - << "These options can be useful for digging into PDF files or for use in\n"  
944 - << "automated test suites for software that uses the qpdf library.\n"  
945 - << "\n"  
946 - << "--deterministic-id generate deterministic /ID\n"  
947 - << "--static-id generate static /ID: FOR TESTING ONLY!\n"  
948 - << "--static-aes-iv use a static initialization vector for AES-CBC\n"  
949 - << " This is option is not secure! FOR TESTING ONLY!\n"  
950 - << "--no-original-object-ids suppress original object ID comments in qdf mode\n"  
951 - << "--show-encryption quickly show encryption parameters\n"  
952 - << "--show-encryption-key when showing encryption, reveal the actual key\n"  
953 - << "--check-linearization check file integrity and linearization status\n"  
954 - << "--show-linearization check and show all linearization data\n"  
955 - << "--show-xref show the contents of the cross-reference table\n"  
956 - << "--show-object=trailer|obj[,gen]\n"  
957 - << " show the contents of the given object\n"  
958 - << " --raw-stream-data show raw stream data instead of object contents\n"  
959 - << " --filtered-stream-data show filtered stream data instead of object contents\n"  
960 - << "--show-npages print the number of pages in the file\n"  
961 - << "--show-pages shows the object/generation number for each page\n"  
962 - << " --with-images also shows the object IDs for images on each page\n"  
963 - << "--check check file structure + encryption, linearization\n"  
964 - << "--json generate a json representation of the file\n"  
965 - << "--json-help describe the format of the json representation\n"  
966 - << "--json-key=key repeatable; prune json structure to include only\n"  
967 - << " specified keys. If absent, all keys are shown\n"  
968 - << "--json-object=trailer|[obj,gen]\n"  
969 - << " repeatable; include only specified objects in the\n"  
970 - << " \"objects\" section of the json. If absent, all\n"  
971 - << " objects are shown\n"  
972 - << "\n"  
973 - << "The json representation generated by qpdf is designed to facilitate\n"  
974 - << "processing of qpdf from other programming languages that have a hard\n"  
975 - << "time calling C++ APIs. Run qpdf --json-help for details on the format.\n"  
976 - << "The manual has more in-depth information about the json representation\n"  
977 - << "and certain compatibility guarantees that qpdf provides.\n"  
978 - << "\n"  
979 - << "The --raw-stream-data and --filtered-stream-data options are ignored\n"  
980 - << "unless --show-object is given. Either of these options will cause the\n"  
981 - << "stream data to be written to standard output.\n"  
982 - << "\n"  
983 - << "If --filtered-stream-data is given and --normalize-content=y is also\n"  
984 - << "given, qpdf will attempt to normalize the stream data as if it is a\n"  
985 - << "page content stream. This attempt will be made even if it is not a\n"  
986 - << "page content stream, in which case it will produce unusable results.\n"  
987 - << "\n"  
988 - << "Ordinarily, qpdf exits with a status of 0 on success or a status of 2\n"  
989 - << "if any errors occurred. If there were warnings but not errors, qpdf\n"  
990 - << "exits with a status of 3. If warnings would have been issued but --no-warn\n"  
991 - << "was given, an exit status of 3 is still used. If you want qpdf to exit\n"  
992 - << "with status 0 when there are warnings, use the --warning-exit-0 flag.\n"  
993 - << "When --no-warn and --warning-exit-0 are used together, the effect is for\n"  
994 - << "qpdf to completely ignore warnings. qpdf does not use exit status 1,\n"  
995 - << "since that is used by the shell if it can't execute qpdf.\n";  
996 -}  
997 -  
998 -void  
999 -ArgParser::argJsonHelp()  
1000 -{  
1001 - // Make sure the output looks right on an 80-column display.  
1002 - // 1 2 3 4 5 6 7 8  
1003 - // 12345678901234567890123456789012345678901234567890123456789012345678901234567890  
1004 - std::cout  
1005 - << "The json block below contains the same structure with the same keys as the"  
1006 - << std::endl  
1007 - << "json generated by qpdf. In the block below, the values are descriptions of"  
1008 - << std::endl  
1009 - << "the meanings of those entries. The specific contract guaranteed by qpdf in"  
1010 - << std::endl  
1011 - << "its json representation is explained in more detail in the manual. You can"  
1012 - << std::endl  
1013 - << "specify a subset of top-level keys when you invoke qpdf, but the \"version\""  
1014 - << std::endl  
1015 - << "and \"parameters\" keys will always be present. Note that the \"encrypt\""  
1016 - << std::endl  
1017 - << "key's values will be populated for non-encrypted files. Some values will"  
1018 - << std::endl  
1019 - << "be null, and others will have values that apply to unencrypted files."  
1020 - << std::endl  
1021 - << QPDFJob::json_schema().unparse()  
1022 - << std::endl;  
1023 -}  
1024 -  
1025 -void  
1026 -ArgParser::argShowCrypto()  
1027 -{  
1028 - auto crypto = QPDFCryptoProvider::getRegisteredImpls();  
1029 - std::string default_crypto = QPDFCryptoProvider::getDefaultProvider();  
1030 - std::cout << default_crypto << std::endl;  
1031 - for (auto const& iter: crypto)  
1032 - {  
1033 - if (iter != default_crypto)  
1034 - {  
1035 - std::cout << iter << std::endl;  
1036 - }  
1037 - }  
1038 -}  
1039 -  
1040 -void  
1041 -ArgParser::argPassword(char* parameter)  
1042 -{  
1043 - o.password = parameter;  
1044 -}  
1045 -  
1046 -void  
1047 -ArgParser::argPasswordFile(char* parameter)  
1048 -{  
1049 - std::list<std::string> lines;  
1050 - if (strcmp(parameter, "-") == 0)  
1051 - {  
1052 - QTC::TC("qpdf", "qpdf password stdin");  
1053 - lines = QUtil::read_lines_from_file(std::cin);  
1054 - }  
1055 - else  
1056 - {  
1057 - QTC::TC("qpdf", "qpdf password file");  
1058 - lines = QUtil::read_lines_from_file(parameter);  
1059 - }  
1060 - if (lines.size() >= 1)  
1061 - {  
1062 - // Make sure the memory for this stays in scope.  
1063 - o.password_alloc = std::shared_ptr<char>(  
1064 - QUtil::copy_string(lines.front().c_str()),  
1065 - std::default_delete<char[]>());  
1066 - o.password = o.password_alloc.get();  
1067 -  
1068 - if (lines.size() > 1)  
1069 - {  
1070 - std::cerr << whoami << ": WARNING: all but the first line of"  
1071 - << " the password file are ignored" << std::endl;  
1072 - }  
1073 - }  
1074 -}  
1075 -  
1076 -void  
1077 -ArgParser::argEmpty()  
1078 -{  
1079 - o.infilename = "";  
1080 -}  
1081 -  
1082 -void  
1083 -ArgParser::argLinearize()  
1084 -{  
1085 - o.linearize = true;  
1086 -}  
1087 -  
1088 -void  
1089 -ArgParser::argEncrypt()  
1090 -{  
1091 - this->accumulated_args.clear();  
1092 - if (this->ap.isCompleting() && this->ap.argsLeft() == 0)  
1093 - {  
1094 - this->ap.insertCompletion("user-password");  
1095 - }  
1096 - this->ap.selectOptionTable(O_ENCRYPT);  
1097 -}  
1098 -  
1099 -void  
1100 -ArgParser::argEncryptPositional(char* arg)  
1101 -{  
1102 - this->accumulated_args.push_back(arg);  
1103 - size_t n_args = this->accumulated_args.size();  
1104 - if (n_args < 3)  
1105 - {  
1106 - if (this->ap.isCompleting() && (this->ap.argsLeft() == 0))  
1107 - {  
1108 - if (n_args == 1)  
1109 - {  
1110 - this->ap.insertCompletion("owner-password");  
1111 - }  
1112 - else if (n_args == 2)  
1113 - {  
1114 - this->ap.insertCompletion("40");  
1115 - this->ap.insertCompletion("128");  
1116 - this->ap.insertCompletion("256");  
1117 - }  
1118 - }  
1119 - return;  
1120 - }  
1121 - o.user_password = this->accumulated_args.at(0);  
1122 - o.owner_password = this->accumulated_args.at(1);  
1123 - std::string len_str = this->accumulated_args.at(2);  
1124 - if (len_str == "40")  
1125 - {  
1126 - o.keylen = 40;  
1127 - this->ap.selectOptionTable(O_ENCRYPT_40);  
1128 - }  
1129 - else if (len_str == "128")  
1130 - {  
1131 - o.keylen = 128;  
1132 - this->ap.selectOptionTable(O_ENCRYPT_128);  
1133 - }  
1134 - else if (len_str == "256")  
1135 - {  
1136 - o.keylen = 256;  
1137 - o.use_aes = true;  
1138 - this->ap.selectOptionTable(O_ENCRYPT_256);  
1139 - }  
1140 - else  
1141 - {  
1142 - usage("encryption key length must be 40, 128, or 256");  
1143 - }  
1144 -}  
1145 -  
1146 -void  
1147 -ArgParser::argDecrypt()  
1148 -{  
1149 - o.decrypt = true;  
1150 - o.encrypt = false;  
1151 - o.copy_encryption = false;  
1152 -}  
1153 -  
1154 -void  
1155 -ArgParser::argPasswordIsHexKey()  
1156 -{  
1157 - o.password_is_hex_key = true;  
1158 -}  
1159 -  
1160 -void  
1161 -ArgParser::argSuppressPasswordRecovery()  
1162 -{  
1163 - o.suppress_password_recovery = true;  
1164 -}  
1165 -  
1166 -void  
1167 -ArgParser::argPasswordMode(char* parameter)  
1168 -{  
1169 - if (strcmp(parameter, "bytes") == 0)  
1170 - {  
1171 - o.password_mode = QPDFJob::pm_bytes;  
1172 - }  
1173 - else if (strcmp(parameter, "hex-bytes") == 0)  
1174 - {  
1175 - o.password_mode = QPDFJob::pm_hex_bytes;  
1176 - }  
1177 - else if (strcmp(parameter, "unicode") == 0)  
1178 - {  
1179 - o.password_mode = QPDFJob::pm_unicode;  
1180 - }  
1181 - else if (strcmp(parameter, "auto") == 0)  
1182 - {  
1183 - o.password_mode = QPDFJob::pm_auto;  
1184 - }  
1185 - else  
1186 - {  
1187 - usage("invalid password-mode option");  
1188 - }  
1189 -}  
1190 -  
1191 -void  
1192 -ArgParser::argAllowInsecure()  
1193 -{  
1194 - o.allow_insecure = true;  
1195 -}  
1196 -  
1197 -void  
1198 -ArgParser::argAllowWeakCrypto()  
1199 -{  
1200 - o.allow_weak_crypto = true;  
1201 -}  
1202 -  
1203 -void  
1204 -ArgParser::argCopyEncryption(char* parameter)  
1205 -{  
1206 - o.encryption_file = parameter;  
1207 - o.copy_encryption = true;  
1208 - o.encrypt = false;  
1209 - o.decrypt = false;  
1210 -}  
1211 -  
1212 -void  
1213 -ArgParser::argEncryptionFilePassword(char* parameter)  
1214 -{  
1215 - o.encryption_file_password = parameter;  
1216 -}  
1217 -  
1218 -void  
1219 -ArgParser::argCollate(char* parameter)  
1220 -{  
1221 - auto n = ((parameter == 0) ? 1 :  
1222 - QUtil::string_to_uint(parameter));  
1223 - o.collate = QIntC::to_size(n);  
1224 -}  
1225 -  
1226 -void  
1227 -ArgParser::argPages()  
1228 -{  
1229 - if (! o.page_specs.empty())  
1230 - {  
1231 - usage("the --pages may only be specified one time");  
1232 - }  
1233 - this->accumulated_args.clear();  
1234 - this->ap.selectOptionTable(O_PAGES);  
1235 -}  
1236 -  
1237 -void  
1238 -ArgParser::argPagesPassword(char* parameter)  
1239 -{  
1240 - if (this->pages_password != nullptr)  
1241 - {  
1242 - QTC::TC("qpdf", "qpdf duplicated pages password");  
1243 - usage("--password already specified for this file");  
1244 - }  
1245 - if (this->accumulated_args.size() != 1)  
1246 - {  
1247 - QTC::TC("qpdf", "qpdf misplaced pages password");  
1248 - usage("in --pages, --password must immediately follow a file name");  
1249 - }  
1250 - this->pages_password = parameter;  
1251 -}  
1252 -  
1253 -void  
1254 -ArgParser::argPagesPositional(char* arg)  
1255 -{  
1256 - if (arg == nullptr)  
1257 - {  
1258 - if (this->accumulated_args.empty())  
1259 - {  
1260 - return;  
1261 - }  
1262 - }  
1263 - else  
1264 - {  
1265 - this->accumulated_args.push_back(arg);  
1266 - }  
1267 -  
1268 - char const* file = this->accumulated_args.at(0);  
1269 - char const* range = nullptr;  
1270 -  
1271 - size_t n_args = this->accumulated_args.size();  
1272 - if (n_args >= 2)  
1273 - {  
1274 - range = this->accumulated_args.at(1);  
1275 - }  
1276 -  
1277 - // See if the user omitted the range entirely, in which case we  
1278 - // assume "1-z".  
1279 - char* next_file = nullptr;  
1280 - if (range == nullptr)  
1281 - {  
1282 - if (arg == nullptr)  
1283 - {  
1284 - // The filename or password was the last argument  
1285 - QTC::TC("qpdf", "qpdf pages range omitted at end",  
1286 - this->pages_password == nullptr ? 0 : 1);  
1287 - }  
1288 - else  
1289 - {  
1290 - // We need to accumulate some more arguments  
1291 - return;  
1292 - }  
1293 - }  
1294 - else  
1295 - {  
1296 - try  
1297 - {  
1298 - parseNumrange(range, 0, true);  
1299 - }  
1300 - catch (std::runtime_error& e1)  
1301 - {  
1302 - // The range is invalid. Let's see if it's a file.  
1303 - if (strcmp(range, ".") == 0)  
1304 - {  
1305 - // "." means the input file.  
1306 - QTC::TC("qpdf", "qpdf pages range omitted with .");  
1307 - }  
1308 - else if (QUtil::file_can_be_opened(range))  
1309 - {  
1310 - QTC::TC("qpdf", "qpdf pages range omitted in middle");  
1311 - // Yup, it's a file.  
1312 - }  
1313 - else  
1314 - {  
1315 - // Give the range error  
1316 - usage(e1.what());  
1317 - }  
1318 - next_file = const_cast<char*>(range);  
1319 - range = nullptr;  
1320 - }  
1321 - }  
1322 - if (range == nullptr)  
1323 - {  
1324 - range = "1-z";  
1325 - }  
1326 - o.page_specs.push_back(QPDFJob::PageSpec(file, this->pages_password, range));  
1327 - this->accumulated_args.clear();  
1328 - this->pages_password = nullptr;  
1329 - if (next_file != nullptr)  
1330 - {  
1331 - this->accumulated_args.push_back(next_file);  
1332 - }  
1333 -}  
1334 -  
1335 -void  
1336 -ArgParser::argEndPages()  
1337 -{  
1338 - argPagesPositional(nullptr);  
1339 - if (o.page_specs.empty())  
1340 - {  
1341 - usage("--pages: no page specifications given");  
1342 - }  
1343 -}  
1344 -  
1345 -void  
1346 -ArgParser::argUnderlay()  
1347 -{  
1348 - parseUnderOverlayOptions(&o.underlay);  
1349 -}  
1350 -  
1351 -void  
1352 -ArgParser::argOverlay()  
1353 -{  
1354 - parseUnderOverlayOptions(&o.overlay);  
1355 -}  
1356 -  
1357 -void  
1358 -ArgParser::argRotate(char* parameter)  
1359 -{  
1360 - parseRotationParameter(parameter);  
1361 -}  
1362 -  
1363 -void  
1364 -ArgParser::argFlattenRotation()  
1365 -{  
1366 - o.flatten_rotation = true;  
1367 -}  
1368 -  
1369 -void  
1370 -ArgParser::argListAttachments()  
1371 -{  
1372 - o.list_attachments = true;  
1373 - o.require_outfile = false;  
1374 -}  
1375 -  
1376 -void  
1377 -ArgParser::argShowAttachment(char* parameter)  
1378 -{  
1379 - o.attachment_to_show = parameter;  
1380 - o.require_outfile = false;  
1381 -}  
1382 -  
1383 -void  
1384 -ArgParser::argRemoveAttachment(char* parameter)  
1385 -{  
1386 - o.attachments_to_remove.push_back(parameter);  
1387 -}  
1388 -  
1389 -void  
1390 -ArgParser::argAddAttachment()  
1391 -{  
1392 - o.attachments_to_add.push_back(QPDFJob::AddAttachment());  
1393 - this->ap.selectOptionTable(O_ATTACHMENT);  
1394 -}  
1395 -  
1396 -void  
1397 -ArgParser::argCopyAttachments()  
1398 -{  
1399 - o.attachments_to_copy.push_back(QPDFJob::CopyAttachmentFrom());  
1400 - this->ap.selectOptionTable(O_COPY_ATTACHMENT);  
1401 -}  
1402 -  
1403 -void  
1404 -ArgParser::argStreamData(char* parameter)  
1405 -{  
1406 - o.stream_data_set = true;  
1407 - if (strcmp(parameter, "compress") == 0)  
1408 - {  
1409 - o.stream_data_mode = qpdf_s_compress;  
1410 - }  
1411 - else if (strcmp(parameter, "preserve") == 0)  
1412 - {  
1413 - o.stream_data_mode = qpdf_s_preserve;  
1414 - }  
1415 - else if (strcmp(parameter, "uncompress") == 0)  
1416 - {  
1417 - o.stream_data_mode = qpdf_s_uncompress;  
1418 - }  
1419 - else  
1420 - {  
1421 - // If this happens, it means streamDataChoices in  
1422 - // ArgParser::initOptionTable is wrong.  
1423 - usage("invalid stream-data option");  
1424 - }  
1425 -}  
1426 -  
1427 -void  
1428 -ArgParser::argCompressStreams(char* parameter)  
1429 -{  
1430 - o.compress_streams_set = true;  
1431 - o.compress_streams = (strcmp(parameter, "y") == 0);  
1432 -}  
1433 -  
1434 -void  
1435 -ArgParser::argRecompressFlate()  
1436 -{  
1437 - o.recompress_flate_set = true;  
1438 - o.recompress_flate = true;  
1439 -}  
1440 -  
1441 -void  
1442 -ArgParser::argCompressionLevel(char* parameter)  
1443 -{  
1444 - o.compression_level = QUtil::string_to_int(parameter);  
1445 -}  
1446 -  
1447 -void  
1448 -ArgParser::argDecodeLevel(char* parameter)  
1449 -{  
1450 - o.decode_level_set = true;  
1451 - if (strcmp(parameter, "none") == 0)  
1452 - {  
1453 - o.decode_level = qpdf_dl_none;  
1454 - }  
1455 - else if (strcmp(parameter, "generalized") == 0)  
1456 - {  
1457 - o.decode_level = qpdf_dl_generalized;  
1458 - }  
1459 - else if (strcmp(parameter, "specialized") == 0)  
1460 - {  
1461 - o.decode_level = qpdf_dl_specialized;  
1462 - }  
1463 - else if (strcmp(parameter, "all") == 0)  
1464 - {  
1465 - o.decode_level = qpdf_dl_all;  
1466 - }  
1467 - else  
1468 - {  
1469 - // If this happens, it means decodeLevelChoices in  
1470 - // ArgParser::initOptionTable is wrong.  
1471 - usage("invalid option");  
1472 - }  
1473 -}  
1474 -  
1475 -void  
1476 -ArgParser::argNormalizeContent(char* parameter)  
1477 -{  
1478 - o.normalize_set = true;  
1479 - o.normalize = (strcmp(parameter, "y") == 0);  
1480 -}  
1481 -  
1482 -void  
1483 -ArgParser::argSuppressRecovery()  
1484 -{  
1485 - o.suppress_recovery = true;  
1486 -}  
1487 -  
1488 -void  
1489 -ArgParser::argObjectStreams(char* parameter)  
1490 -{  
1491 - o.object_stream_set = true;  
1492 - if (strcmp(parameter, "disable") == 0)  
1493 - {  
1494 - o.object_stream_mode = qpdf_o_disable;  
1495 - }  
1496 - else if (strcmp(parameter, "preserve") == 0)  
1497 - {  
1498 - o.object_stream_mode = qpdf_o_preserve;  
1499 - }  
1500 - else if (strcmp(parameter, "generate") == 0)  
1501 - {  
1502 - o.object_stream_mode = qpdf_o_generate;  
1503 - }  
1504 - else  
1505 - {  
1506 - // If this happens, it means objectStreamsChoices in  
1507 - // ArgParser::initOptionTable is wrong.  
1508 - usage("invalid object stream mode");  
1509 - }  
1510 -}  
1511 -  
1512 -void  
1513 -ArgParser::argIgnoreXrefStreams()  
1514 -{  
1515 - o.ignore_xref_streams = true;  
1516 -}  
1517 -  
1518 -void  
1519 -ArgParser::argQdf()  
1520 -{  
1521 - o.qdf_mode = true;  
1522 -}  
1523 -  
1524 -void  
1525 -ArgParser::argPreserveUnreferenced()  
1526 -{  
1527 - o.preserve_unreferenced_objects = true;  
1528 -}  
1529 -  
1530 -void  
1531 -ArgParser::argPreserveUnreferencedResources()  
1532 -{  
1533 - o.remove_unreferenced_page_resources = QPDFJob::re_no;  
1534 -}  
1535 -  
1536 -void  
1537 -ArgParser::argRemoveUnreferencedResources(char* parameter)  
1538 -{  
1539 - if (strcmp(parameter, "auto") == 0)  
1540 - {  
1541 - o.remove_unreferenced_page_resources = QPDFJob::re_auto;  
1542 - }  
1543 - else if (strcmp(parameter, "yes") == 0)  
1544 - {  
1545 - o.remove_unreferenced_page_resources = QPDFJob::re_yes;  
1546 - }  
1547 - else if (strcmp(parameter, "no") == 0)  
1548 - {  
1549 - o.remove_unreferenced_page_resources = QPDFJob::re_no;  
1550 - }  
1551 - else  
1552 - {  
1553 - // If this happens, it means remove_unref_choices in  
1554 - // ArgParser::initOptionTable is wrong.  
1555 - usage("invalid value for --remove-unreferenced-page-resources");  
1556 - }  
1557 -}  
1558 -  
1559 -void  
1560 -ArgParser::argKeepFilesOpen(char* parameter)  
1561 -{  
1562 - o.keep_files_open_set = true;  
1563 - o.keep_files_open = (strcmp(parameter, "y") == 0);  
1564 -}  
1565 -  
1566 -void  
1567 -ArgParser::argKeepFilesOpenThreshold(char* parameter)  
1568 -{  
1569 - o.keep_files_open_threshold = QUtil::string_to_uint(parameter);  
1570 -}  
1571 -  
1572 -void  
1573 -ArgParser::argNewlineBeforeEndstream()  
1574 -{  
1575 - o.newline_before_endstream = true;  
1576 -}  
1577 -  
1578 -void  
1579 -ArgParser::argLinearizePass1(char* parameter)  
1580 -{  
1581 - o.linearize_pass1 = parameter;  
1582 -}  
1583 -  
1584 -void  
1585 -ArgParser::argCoalesceContents()  
1586 -{  
1587 - o.coalesce_contents = true;  
1588 -}  
1589 -  
1590 -void  
1591 -ArgParser::argFlattenAnnotations(char* parameter)  
1592 -{  
1593 - o.flatten_annotations = true;  
1594 - if (strcmp(parameter, "screen") == 0)  
1595 - {  
1596 - o.flatten_annotations_forbidden |= an_no_view;  
1597 - }  
1598 - else if (strcmp(parameter, "print") == 0)  
1599 - {  
1600 - o.flatten_annotations_required |= an_print;  
1601 - }  
1602 -}  
1603 -  
1604 -void  
1605 -ArgParser::argGenerateAppearances()  
1606 -{  
1607 - o.generate_appearances = true;  
1608 -}  
1609 -  
1610 -void  
1611 -ArgParser::argMinVersion(char* parameter)  
1612 -{  
1613 - o.min_version = parameter;  
1614 -}  
1615 -  
1616 -void  
1617 -ArgParser::argForceVersion(char* parameter)  
1618 -{  
1619 - o.force_version = parameter;  
1620 -}  
1621 -  
1622 -void  
1623 -ArgParser::argSplitPages(char* parameter)  
1624 -{  
1625 - int n = ((parameter == 0) ? 1 :  
1626 - QUtil::string_to_int(parameter));  
1627 - o.split_pages = n;  
1628 -}  
1629 -  
1630 -void  
1631 -ArgParser::argVerbose()  
1632 -{  
1633 - o.verbose = true;  
1634 -}  
1635 -  
1636 -void  
1637 -ArgParser::argProgress()  
1638 -{  
1639 - o.progress = true;  
1640 -}  
1641 -  
1642 -void  
1643 -ArgParser::argNoWarn()  
1644 -{  
1645 - o.suppress_warnings = true;  
1646 -}  
1647 -  
1648 -void  
1649 -ArgParser::argWarningExitZero()  
1650 -{  
1651 - ::EXIT_WARNING = 0;  
1652 -}  
1653 -  
1654 -void  
1655 -ArgParser::argDeterministicId()  
1656 -{  
1657 - o.deterministic_id = true;  
1658 -}  
1659 -  
1660 -void  
1661 -ArgParser::argStaticId()  
1662 -{  
1663 - o.static_id = true;  
1664 -}  
1665 -  
1666 -void  
1667 -ArgParser::argStaticAesIv()  
1668 -{  
1669 - o.static_aes_iv = true;  
1670 -}  
1671 -  
1672 -void  
1673 -ArgParser::argNoOriginalObjectIds()  
1674 -{  
1675 - o.suppress_original_object_id = true;  
1676 -}  
1677 -  
1678 -void  
1679 -ArgParser::argShowEncryption()  
1680 -{  
1681 - o.show_encryption = true;  
1682 - o.require_outfile = false;  
1683 -}  
1684 -  
1685 -void  
1686 -ArgParser::argShowEncryptionKey()  
1687 -{  
1688 - o.show_encryption_key = true;  
1689 -}  
1690 -  
1691 -void  
1692 -ArgParser::argCheckLinearization()  
1693 -{  
1694 - o.check_linearization = true;  
1695 - o.require_outfile = false;  
1696 -}  
1697 -  
1698 -void  
1699 -ArgParser::argShowLinearization()  
1700 -{  
1701 - o.show_linearization = true;  
1702 - o.require_outfile = false;  
1703 -}  
1704 -  
1705 -void  
1706 -ArgParser::argShowXref()  
1707 -{  
1708 - o.show_xref = true;  
1709 - o.require_outfile = false;  
1710 -}  
1711 -  
1712 -void  
1713 -ArgParser::argShowObject(char* parameter)  
1714 -{  
1715 - QPDFJob::parse_object_id(parameter, o.show_trailer, o.show_obj, o.show_gen);  
1716 - o.require_outfile = false;  
1717 -}  
1718 -  
1719 -void  
1720 -ArgParser::argRawStreamData()  
1721 -{  
1722 - o.show_raw_stream_data = true;  
1723 -}  
1724 -  
1725 -void  
1726 -ArgParser::argFilteredStreamData()  
1727 -{  
1728 - o.show_filtered_stream_data = true;  
1729 -}  
1730 -  
1731 -void  
1732 -ArgParser::argShowNpages()  
1733 -{  
1734 - o.show_npages = true;  
1735 - o.require_outfile = false;  
1736 -}  
1737 -  
1738 -void  
1739 -ArgParser::argShowPages()  
1740 -{  
1741 - o.show_pages = true;  
1742 - o.require_outfile = false;  
1743 -}  
1744 -  
1745 -void  
1746 -ArgParser::argWithImages()  
1747 -{  
1748 - o.show_page_images = true;  
1749 -}  
1750 -  
1751 -void  
1752 -ArgParser::argJson()  
1753 -{  
1754 - o.json = true;  
1755 - o.require_outfile = false;  
1756 -}  
1757 -  
1758 -void  
1759 -ArgParser::argJsonKey(char* parameter)  
1760 -{  
1761 - o.json_keys.insert(parameter);  
1762 -}  
1763 -  
1764 -void  
1765 -ArgParser::argJsonObject(char* parameter)  
1766 -{  
1767 - o.json_objects.insert(parameter);  
1768 -}  
1769 -  
1770 -void  
1771 -ArgParser::argCheck()  
1772 -{  
1773 - o.check = true;  
1774 - o.require_outfile = false;  
1775 -}  
1776 -  
1777 -void  
1778 -ArgParser::argOptimizeImages()  
1779 -{  
1780 - o.optimize_images = true;  
1781 -}  
1782 -  
1783 -void  
1784 -ArgParser::argExternalizeInlineImages()  
1785 -{  
1786 - o.externalize_inline_images = true;  
1787 -}  
1788 -  
1789 -void  
1790 -ArgParser::argKeepInlineImages()  
1791 -{  
1792 - o.keep_inline_images = true;  
1793 -}  
1794 -  
1795 -void  
1796 -ArgParser::argRemovePageLabels()  
1797 -{  
1798 - o.remove_page_labels = true;  
1799 -}  
1800 -  
1801 -void  
1802 -ArgParser::argOiMinWidth(char* parameter)  
1803 -{  
1804 - o.oi_min_width = QUtil::string_to_uint(parameter);  
1805 -}  
1806 -  
1807 -void  
1808 -ArgParser::argOiMinHeight(char* parameter)  
1809 -{  
1810 - o.oi_min_height = QUtil::string_to_uint(parameter);  
1811 -}  
1812 -  
1813 -void  
1814 -ArgParser::argOiMinArea(char* parameter)  
1815 -{  
1816 - o.oi_min_area = QUtil::string_to_uint(parameter);  
1817 -}  
1818 -  
1819 -void  
1820 -ArgParser::argIiMinBytes(char* parameter)  
1821 -{  
1822 - o.ii_min_bytes = QUtil::string_to_uint(parameter);  
1823 -}  
1824 -  
1825 -void  
1826 -ArgParser::arg40Print(char* parameter)  
1827 -{  
1828 - o.r2_print = (strcmp(parameter, "y") == 0);  
1829 -}  
1830 -  
1831 -void  
1832 -ArgParser::arg40Modify(char* parameter)  
1833 -{  
1834 - o.r2_modify = (strcmp(parameter, "y") == 0);  
1835 -}  
1836 -  
1837 -void  
1838 -ArgParser::arg40Extract(char* parameter)  
1839 -{  
1840 - o.r2_extract = (strcmp(parameter, "y") == 0);  
1841 -}  
1842 -  
1843 -void  
1844 -ArgParser::arg40Annotate(char* parameter)  
1845 -{  
1846 - o.r2_annotate = (strcmp(parameter, "y") == 0);  
1847 -}  
1848 -  
1849 -void  
1850 -ArgParser::arg128Accessibility(char* parameter)  
1851 -{  
1852 - o.r3_accessibility = (strcmp(parameter, "y") == 0);  
1853 -}  
1854 -  
1855 -void  
1856 -ArgParser::arg128Extract(char* parameter)  
1857 -{  
1858 - o.r3_extract = (strcmp(parameter, "y") == 0);  
1859 -}  
1860 -  
1861 -void  
1862 -ArgParser::arg128Print(char* parameter)  
1863 -{  
1864 - if (strcmp(parameter, "full") == 0)  
1865 - {  
1866 - o.r3_print = qpdf_r3p_full;  
1867 - }  
1868 - else if (strcmp(parameter, "low") == 0)  
1869 - {  
1870 - o.r3_print = qpdf_r3p_low;  
1871 - }  
1872 - else if (strcmp(parameter, "none") == 0)  
1873 - {  
1874 - o.r3_print = qpdf_r3p_none;  
1875 - }  
1876 - else  
1877 - {  
1878 - usage("invalid print option");  
1879 - }  
1880 -}  
1881 -  
1882 -void  
1883 -ArgParser::arg128Modify(char* parameter)  
1884 -{  
1885 - if (strcmp(parameter, "all") == 0)  
1886 - {  
1887 - o.r3_assemble = true;  
1888 - o.r3_annotate_and_form = true;  
1889 - o.r3_form_filling = true;  
1890 - o.r3_modify_other = true;  
1891 - }  
1892 - else if (strcmp(parameter, "annotate") == 0)  
1893 - {  
1894 - o.r3_assemble = true;  
1895 - o.r3_annotate_and_form = true;  
1896 - o.r3_form_filling = true;  
1897 - o.r3_modify_other = false;  
1898 - }  
1899 - else if (strcmp(parameter, "form") == 0)  
1900 - {  
1901 - o.r3_assemble = true;  
1902 - o.r3_annotate_and_form = false;  
1903 - o.r3_form_filling = true;  
1904 - o.r3_modify_other = false;  
1905 - }  
1906 - else if (strcmp(parameter, "assembly") == 0)  
1907 - {  
1908 - o.r3_assemble = true;  
1909 - o.r3_annotate_and_form = false;  
1910 - o.r3_form_filling = false;  
1911 - o.r3_modify_other = false;  
1912 - }  
1913 - else if (strcmp(parameter, "none") == 0)  
1914 - {  
1915 - o.r3_assemble = false;  
1916 - o.r3_annotate_and_form = false;  
1917 - o.r3_form_filling = false;  
1918 - o.r3_modify_other = false;  
1919 - }  
1920 - else  
1921 - {  
1922 - usage("invalid modify option");  
1923 - }  
1924 -}  
1925 -  
1926 -void  
1927 -ArgParser::arg128ClearTextMetadata()  
1928 -{  
1929 - o.cleartext_metadata = true;  
1930 -}  
1931 -  
1932 -void  
1933 -ArgParser::arg128Assemble(char* parameter)  
1934 -{  
1935 - o.r3_assemble = (strcmp(parameter, "y") == 0);  
1936 -}  
1937 -  
1938 -void  
1939 -ArgParser::arg128Annotate(char* parameter)  
1940 -{  
1941 - o.r3_annotate_and_form = (strcmp(parameter, "y") == 0);  
1942 -}  
1943 -  
1944 -void  
1945 -ArgParser::arg128Form(char* parameter)  
1946 -{  
1947 - o.r3_form_filling = (strcmp(parameter, "y") == 0);  
1948 -}  
1949 -  
1950 -void  
1951 -ArgParser::arg128ModOther(char* parameter)  
1952 -{  
1953 - o.r3_modify_other = (strcmp(parameter, "y") == 0);  
1954 -}  
1955 -  
1956 -void  
1957 -ArgParser::arg128UseAes(char* parameter)  
1958 -{  
1959 - o.use_aes = (strcmp(parameter, "y") == 0);  
1960 -}  
1961 -  
1962 -void  
1963 -ArgParser::arg128ForceV4()  
1964 -{  
1965 - o.force_V4 = true;  
1966 -}  
1967 -  
1968 -void  
1969 -ArgParser::arg256ForceR5()  
1970 -{  
1971 - o.force_R5 = true;  
1972 -}  
1973 -  
1974 -void  
1975 -ArgParser::argEndEncrypt()  
1976 -{  
1977 - o.encrypt = true;  
1978 - o.decrypt = false;  
1979 - o.copy_encryption = false;  
1980 -}  
1981 -  
1982 -void  
1983 -ArgParser::argUOpositional(char* arg)  
1984 -{  
1985 - if (o.under_overlay->filename)  
1986 - {  
1987 - usage(o.under_overlay->which + " file already specified");  
1988 - }  
1989 - else  
1990 - {  
1991 - o.under_overlay->filename = arg;  
1992 - }  
1993 -}  
1994 -  
1995 -void  
1996 -ArgParser::argUOto(char* parameter)  
1997 -{  
1998 - parseNumrange(parameter, 0);  
1999 - o.under_overlay->to_nr = parameter;  
2000 -}  
2001 -  
2002 -void  
2003 -ArgParser::argUOfrom(char* parameter)  
2004 -{  
2005 - if (strlen(parameter))  
2006 - {  
2007 - parseNumrange(parameter, 0);  
2008 - }  
2009 - o.under_overlay->from_nr = parameter;  
2010 -}  
2011 -  
2012 -void  
2013 -ArgParser::argUOrepeat(char* parameter)  
2014 -{  
2015 - if (strlen(parameter))  
2016 - {  
2017 - parseNumrange(parameter, 0);  
2018 - }  
2019 - o.under_overlay->repeat_nr = parameter;  
2020 -}  
2021 -  
2022 -void  
2023 -ArgParser::argUOpassword(char* parameter)  
2024 -{  
2025 - o.under_overlay->password = parameter;  
2026 -}  
2027 -  
2028 -void  
2029 -ArgParser::argEndUnderOverlay()  
2030 -{  
2031 - if (0 == o.under_overlay->filename)  
2032 - {  
2033 - usage(o.under_overlay->which + " file not specified");  
2034 - }  
2035 - o.under_overlay = 0;  
2036 -}  
2037 -  
2038 -void  
2039 -ArgParser::argReplaceInput()  
2040 -{  
2041 - o.replace_input = true;  
2042 -}  
2043 -  
2044 -void  
2045 -ArgParser::argIsEncrypted()  
2046 -{  
2047 - o.check_is_encrypted = true;  
2048 - o.require_outfile = false;  
2049 -}  
2050 -  
2051 -void  
2052 -ArgParser::argRequiresPassword()  
2053 -{  
2054 - o.check_requires_password = true;  
2055 - o.require_outfile = false;  
2056 -}  
2057 -  
2058 -void  
2059 -ArgParser::argAApositional(char* arg)  
2060 -{  
2061 - o.attachments_to_add.back().path = arg;  
2062 -}  
2063 -  
2064 -void  
2065 -ArgParser::argAAKey(char* parameter)  
2066 -{  
2067 - o.attachments_to_add.back().key = parameter;  
2068 -}  
2069 -  
2070 -void  
2071 -ArgParser::argAAFilename(char* parameter)  
2072 -{  
2073 - o.attachments_to_add.back().filename = parameter;  
2074 -}  
2075 -  
2076 -void  
2077 -ArgParser::argAACreationDate(char* parameter)  
2078 -{  
2079 - if (! QUtil::pdf_time_to_qpdf_time(parameter))  
2080 - {  
2081 - usage(std::string(parameter) + " is not a valid PDF timestamp");  
2082 - }  
2083 - o.attachments_to_add.back().creationdate = parameter;  
2084 -}  
2085 -  
2086 -void  
2087 -ArgParser::argAAModDate(char* parameter)  
2088 -{  
2089 - if (! QUtil::pdf_time_to_qpdf_time(parameter))  
2090 - {  
2091 - usage(std::string(parameter) + " is not a valid PDF timestamp");  
2092 - }  
2093 - o.attachments_to_add.back().moddate = parameter;  
2094 -}  
2095 -  
2096 -void  
2097 -ArgParser::argAAMimeType(char* parameter)  
2098 -{  
2099 - if (strchr(parameter, '/') == nullptr)  
2100 - {  
2101 - usage("mime type should be specified as type/subtype");  
2102 - }  
2103 - o.attachments_to_add.back().mimetype = parameter;  
2104 -}  
2105 -  
2106 -void  
2107 -ArgParser::argAADescription(char* parameter)  
2108 -{  
2109 - o.attachments_to_add.back().description = parameter;  
2110 -}  
2111 -  
2112 -void  
2113 -ArgParser::argAAReplace()  
2114 -{  
2115 - o.attachments_to_add.back().replace = true;  
2116 -}  
2117 -  
2118 -void  
2119 -ArgParser::argEndAddAttachment()  
2120 -{  
2121 - static std::string now = QUtil::qpdf_time_to_pdf_time(  
2122 - QUtil::get_current_qpdf_time());  
2123 - auto& cur = o.attachments_to_add.back();  
2124 - if (cur.path.empty())  
2125 - {  
2126 - usage("add attachment: no path specified");  
2127 - }  
2128 - std::string last_element = QUtil::path_basename(cur.path);  
2129 - if (last_element.empty())  
2130 - {  
2131 - usage("path for --add-attachment may not be empty");  
2132 - }  
2133 - if (cur.filename.empty())  
2134 - {  
2135 - cur.filename = last_element;  
2136 - }  
2137 - if (cur.key.empty())  
2138 - {  
2139 - cur.key = last_element;  
2140 - }  
2141 - if (cur.creationdate.empty())  
2142 - {  
2143 - cur.creationdate = now;  
2144 - }  
2145 - if (cur.moddate.empty())  
2146 - {  
2147 - cur.moddate = now;  
2148 - }  
2149 -} 1 +#include <qpdf/QPDFJob.hh>
  2 +#include <qpdf/QTC.hh>
  3 +#include <qpdf/QUtil.hh>
2150 4
2151 -void  
2152 -ArgParser::argCApositional(char* arg)  
2153 -{  
2154 - o.attachments_to_copy.back().path = arg;  
2155 -} 5 +#include <cstdio>
  6 +#include <cstdlib>
  7 +#include <cstring>
  8 +#include <iostream>
2156 9
2157 -void  
2158 -ArgParser::argCAprefix(char* parameter)  
2159 -{  
2160 - o.attachments_to_copy.back().prefix = parameter;  
2161 -} 10 +static int constexpr EXIT_ERROR = 2;
  11 +static int constexpr EXIT_WARNING = 3;
2162 12
2163 -void  
2164 -ArgParser::argCApassword(char* parameter)  
2165 -{  
2166 - o.attachments_to_copy.back().password = parameter;  
2167 -} 13 +// For is-encrypted and requires-password
  14 +static int constexpr EXIT_IS_NOT_ENCRYPTED = 2;
  15 +static int constexpr EXIT_CORRECT_PASSWORD = 3;
2168 16
2169 -void  
2170 -ArgParser::argEndCopyAttachments()  
2171 -{  
2172 - if (o.attachments_to_copy.back().path.empty())  
2173 - {  
2174 - usage("copy attachments: no path specified");  
2175 - }  
2176 -} 17 +static char const* whoami = 0;
2177 18
2178 -void usageExit(std::string const& msg) 19 +static void usageExit(std::string const& msg)
2179 { 20 {
2180 std::cerr 21 std::cerr
2181 << std::endl 22 << std::endl
2182 << whoami << ": " << msg << std::endl 23 << whoami << ": " << msg << std::endl
2183 << std::endl 24 << std::endl
2184 - << "Usage: " << whoami << " [options] {infile | --empty} [page_selection_options] outfile" << std::endl  
2185 << "For detailed help, run " << whoami << " --help" << std::endl 25 << "For detailed help, run " << whoami << " --help" << std::endl
2186 << std::endl; 26 << std::endl;
2187 - exit(EXIT_ERROR); // QXXXQ  
2188 -}  
2189 -  
2190 -void  
2191 -ArgParser::usage(std::string const& message)  
2192 -{  
2193 - if (this->ap.isCompleting())  
2194 - {  
2195 - // This will cause bash to fall back to regular file completion.  
2196 - exit(0);  
2197 - }  
2198 - else  
2199 - {  
2200 - usageExit(message);  
2201 - }  
2202 -}  
2203 -  
2204 -std::vector<int>  
2205 -ArgParser::parseNumrange(char const* range, int max, bool throw_error)  
2206 -{  
2207 - try  
2208 - {  
2209 - return QUtil::parse_numrange(range, max);  
2210 - }  
2211 - catch (std::runtime_error& e)  
2212 - {  
2213 - if (throw_error)  
2214 - {  
2215 - throw(e);  
2216 - }  
2217 - else  
2218 - {  
2219 - usage(e.what());  
2220 - }  
2221 - }  
2222 - return std::vector<int>();  
2223 -}  
2224 -  
2225 -void  
2226 -ArgParser::parseUnderOverlayOptions(QPDFJob::UnderOverlay* uo)  
2227 -{  
2228 - o.under_overlay = uo;  
2229 - this->ap.selectOptionTable(O_UNDER_OVERLAY);  
2230 -}  
2231 -  
2232 -void  
2233 -ArgParser::parseRotationParameter(std::string const& parameter)  
2234 -{  
2235 - std::string angle_str;  
2236 - std::string range;  
2237 - size_t colon = parameter.find(':');  
2238 - int relative = 0;  
2239 - if (colon != std::string::npos)  
2240 - {  
2241 - if (colon > 0)  
2242 - {  
2243 - angle_str = parameter.substr(0, colon);  
2244 - }  
2245 - if (colon + 1 < parameter.length())  
2246 - {  
2247 - range = parameter.substr(colon + 1);  
2248 - }  
2249 - }  
2250 - else  
2251 - {  
2252 - angle_str = parameter;  
2253 - }  
2254 - if (angle_str.length() > 0)  
2255 - {  
2256 - char first = angle_str.at(0);  
2257 - if ((first == '+') || (first == '-'))  
2258 - {  
2259 - relative = ((first == '+') ? 1 : -1);  
2260 - angle_str = angle_str.substr(1);  
2261 - }  
2262 - else if (! QUtil::is_digit(angle_str.at(0)))  
2263 - {  
2264 - angle_str = "";  
2265 - }  
2266 - }  
2267 - if (range.empty())  
2268 - {  
2269 - range = "1-z";  
2270 - }  
2271 - bool range_valid = false;  
2272 - try  
2273 - {  
2274 - parseNumrange(range.c_str(), 0, true);  
2275 - range_valid = true;  
2276 - }  
2277 - catch (std::runtime_error const&)  
2278 - {  
2279 - // ignore  
2280 - }  
2281 - if (range_valid &&  
2282 - ((angle_str == "0") ||(angle_str == "90") ||  
2283 - (angle_str == "180") || (angle_str == "270")))  
2284 - {  
2285 - int angle = QUtil::string_to_int(angle_str.c_str());  
2286 - if (relative == -1)  
2287 - {  
2288 - angle = -angle;  
2289 - }  
2290 - o.rotations[range] = QPDFJob::RotationSpec(angle, (relative != 0));  
2291 - }  
2292 - else  
2293 - {  
2294 - usage("invalid parameter to rotate: " + parameter);  
2295 - }  
2296 -}  
2297 -  
2298 -void  
2299 -ArgParser::parseOptions()  
2300 -{  
2301 - try  
2302 - {  
2303 - this->ap.parseArgs();  
2304 - }  
2305 - catch (QPDFArgParser::Usage& e)  
2306 - {  
2307 - usage(e.what());  
2308 - }  
2309 -}  
2310 -  
2311 -void  
2312 -ArgParser::doFinalChecks()  
2313 -{  
2314 - if (o.replace_input)  
2315 - {  
2316 - if (o.outfilename)  
2317 - {  
2318 - usage("--replace-input may not be used when"  
2319 - " an output file is specified");  
2320 - }  
2321 - else if (o.split_pages)  
2322 - {  
2323 - usage("--split-pages may not be used with --replace-input");  
2324 - }  
2325 - }  
2326 - if (o.infilename == 0)  
2327 - {  
2328 - usage("an input file name is required");  
2329 - }  
2330 - else if (o.require_outfile && (o.outfilename == 0) && (! o.replace_input))  
2331 - {  
2332 - usage("an output file name is required; use - for standard output");  
2333 - }  
2334 - else if ((! o.require_outfile) &&  
2335 - ((o.outfilename != 0) || o.replace_input))  
2336 - {  
2337 - usage("no output file may be given for this option");  
2338 - }  
2339 - if (o.optimize_images && (! o.keep_inline_images))  
2340 - {  
2341 - // QXXXQ this is not a check and doesn't belong here  
2342 - o.externalize_inline_images = true;  
2343 - }  
2344 - if (o.check_requires_password && o.check_is_encrypted)  
2345 - {  
2346 - usage("--requires-password and --is-encrypted may not be given"  
2347 - " together");  
2348 - }  
2349 -  
2350 - if (o.encrypt && (! o.allow_insecure) &&  
2351 - (o.owner_password.empty() &&  
2352 - (! o.user_password.empty()) &&  
2353 - (o.keylen == 256)))  
2354 - {  
2355 - // Note that empty owner passwords for R < 5 are copied from  
2356 - // the user password, so this lack of security is not an issue  
2357 - // for those files. Also we are consider only the ability to  
2358 - // open the file without a password to be insecure. We are not  
2359 - // concerned about whether the viewer enforces security  
2360 - // settings when the user and owner password match.  
2361 - usage("A PDF with a non-empty user password and an empty owner"  
2362 - " password encrypted with a 256-bit key is insecure as it"  
2363 - " can be opened without a password. If you really want to"  
2364 - " do this, you must also give the --allow-insecure option"  
2365 - " before the -- that follows --encrypt.");  
2366 - }  
2367 -  
2368 - if (o.require_outfile && o.outfilename &&  
2369 - (strcmp(o.outfilename, "-") == 0))  
2370 - {  
2371 - if (o.split_pages)  
2372 - {  
2373 - usage("--split-pages may not be used when"  
2374 - " writing to standard output");  
2375 - }  
2376 - if (o.verbose) // QXXXQ  
2377 - {  
2378 - usage("--verbose may not be used when"  
2379 - " writing to standard output");  
2380 - }  
2381 - if (o.progress)  
2382 - {  
2383 - usage("--progress may not be used when"  
2384 - " writing to standard output");  
2385 - }  
2386 - }  
2387 -  
2388 - if ((! o.split_pages) && QUtil::same_file(o.infilename, o.outfilename))  
2389 - {  
2390 - QTC::TC("qpdf", "qpdf same file error");  
2391 - usage("input file and output file are the same;"  
2392 - " use --replace-input to intentionally overwrite the input file");  
2393 - } 27 + exit(EXIT_ERROR);
2394 } 28 }
2395 29
2396 int realmain(int argc, char* argv[]) 30 int realmain(int argc, char* argv[])
@@ -2404,27 +38,25 @@ int realmain(int argc, char* argv[]) @@ -2404,27 +38,25 @@ int realmain(int argc, char* argv[])
2404 whoami += 3; 38 whoami += 3;
2405 } 39 }
2406 40
2407 - // ArgParser must stay in scope for the duration of qpdf's run as  
2408 - // it holds dynamic memory used for argv.  
2409 QPDFJob j; 41 QPDFJob j;
2410 - j.setMessagePrefix(whoami);  
2411 - ArgParser ap(argc, argv, j);  
2412 42
2413 bool errors = false; 43 bool errors = false;
2414 try 44 try
2415 { 45 {
2416 - ap.parseOptions(); 46 + j.initializeFromArgv(argc, argv);
2417 j.run(); 47 j.run();
2418 } 48 }
  49 + catch (QPDFArgParser::Usage& e)
  50 + {
  51 + usageExit(e.what());
  52 + }
2419 catch (std::exception& e) 53 catch (std::exception& e)
2420 { 54 {
2421 std::cerr << whoami << ": " << e.what() << std::endl; 55 std::cerr << whoami << ": " << e.what() << std::endl;
2422 errors = true; 56 errors = true;
2423 } 57 }
2424 58
2425 - // QXXXQ  
2426 bool warnings = j.hasWarnings(); 59 bool warnings = j.hasWarnings();
2427 -  
2428 if (warnings) 60 if (warnings)
2429 { 61 {
2430 if (! j.suppressWarnings()) 62 if (! j.suppressWarnings())
@@ -2442,7 +74,12 @@ int realmain(int argc, char* argv[]) @@ -2442,7 +74,12 @@ int realmain(int argc, char* argv[])
2442 } 74 }
2443 } 75 }
2444 // Still return with warning code even if warnings were 76 // Still return with warning code even if warnings were
2445 - // suppressed, so leave warnings == true. 77 + // suppressed, so leave warnings == true unless we've been
  78 + // specifically instructed to do otherwise.
  79 + if (j.warningsExitZero())
  80 + {
  81 + warnings = false;
  82 + }
2446 } 83 }
2447 84
2448 unsigned long encryption_status = j.getEncryptionStatus(); 85 unsigned long encryption_status = j.getEncryptionStatus();
qpdf/qtest/qpdf/split-pages-stdout.out
1 1
2 qpdf: --split-pages may not be used when writing to standard output 2 qpdf: --split-pages may not be used when writing to standard output
3 3
4 -Usage: qpdf [options] {infile | --empty} [page_selection_options] outfile  
5 For detailed help, run qpdf --help 4 For detailed help, run qpdf --help
6 5