diff --git a/generate_auto_job b/generate_auto_job index f6bb908b..10d74d89 100755 --- a/generate_auto_job +++ b/generate_auto_job @@ -738,6 +738,11 @@ class Main: if flag in expected: options_seen.add(flag) + elif flag.startswith('__'): + # This marks a flag that has no JSON equivalent because it + # is handled in some other fashion. + options_seen.add(flag[2:]) + return elif isinstance(j, str): if not flag.startswith('_'): raise Exception(f'json: {flag} has a description' diff --git a/job.sums b/job.sums index 8ab0d576..43318803 100644 --- a/job.sums +++ b/job.sums @@ -1,17 +1,17 @@ # Generated by generate_auto_job -generate_auto_job 9abe2ec994fb98526f5e3c0c199ce2e61a868463cb522a5bc6e9730b65534187 +generate_auto_job bf44181b610d335511a41b6c2b9c3497d0b023a1ca2c8e4537b34cb6262ce173 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 include/qpdf/auto_job_c_main.hh dbfc221d1533120d1aa9c361d8d2483dea5fcb1c0fd95144d98d305e64ed32a6 include/qpdf/auto_job_c_pages.hh b3cc0f21029f6d89efa043dcdbfa183cb59325b6506001c18911614fe8e568ec include/qpdf/auto_job_c_uo.hh ae21b69a1efa9333050f4833d465f6daff87e5b38e5106e49bbef5d4132e4ed1 -job.yml 1f5db325600a763692034835117ac6b1a4b6aba8e73faa13a004ddc086834545 -libqpdf/qpdf/auto_job_decl.hh 7844eba58edffb9494b19e8eca6fd59a24d6e152ca606c3b07da569f753df2da -libqpdf/qpdf/auto_job_help.hh 319c7bd0bfe2f3cb96bb16b66a9331a81b312805a64acaad54737b5623773e31 -libqpdf/qpdf/auto_job_init.hh e6c09e94c92bd17e9c728b11437e3c98266c93c42c3efbce050c8d288e6b3473 +job.yml 4f89fc7b622df897d30d403d8035aa36fc7de8d8c43042c736e0300d904cb05c +libqpdf/qpdf/auto_job_decl.hh 9c6f701c29f3f764d620186bed92685a2edf2e4d11e4f4532862c05470cfc4d2 +libqpdf/qpdf/auto_job_help.hh 788320d439519ecd284621531e96ee698965a9ad342fd423c5fb1de75d2a06b1 +libqpdf/qpdf/auto_job_init.hh b4c2b3724fba61f1206fd3bae81951636852592f67a63ef9539839c2c5995065 libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a9474319fb7c86d92634cc8297 -libqpdf/qpdf/auto_job_json_init.hh 85ac7e5c66f14c767419823eac84bdea4bd72d690bfe12b533321e5708e644b7 -libqpdf/qpdf/auto_job_schema.hh 5e0f5cb7d462716fe52548b2ae1a8eb6f3c900016e915140eea37f78cee45b2b +libqpdf/qpdf/auto_job_json_init.hh f5acb9aa103131cb68dec0e12c4d237a6459bdb49b24773c24f0c2724a462b8f +libqpdf/qpdf/auto_job_schema.hh b53c006fec2e75b1b73588d242d49a32f7d3db820b1541de106c5d4c27fbb4d9 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 -manual/cli.rst 4f2806f7cf77f167fd41b065a8916f02fdfaab35ad1a74587578bf2954228464 +manual/cli.rst b524f96f2a6f338f3e4350703598c56ba22e8f12a8efb7a441648c6dbf0a455e diff --git a/job.yml b/job.yml index fb754b44..d342e7a2 100644 --- a/job.yml +++ b/job.yml @@ -60,6 +60,10 @@ choices: - 1 - 2 - latest + enc_bits: + - 40 + - 128 + - 256 print128: - full - low @@ -194,8 +198,18 @@ options: required_parameter: password: password - table: encryption + config: c_main prefix: Enc positional: true + manual: + - user-password + - owner-password + - bits + required_parameter: + user-password: user_password + owner-password: owner_password + required_choices: + bits: enc_bits - table: 40-bit encryption config: c_enc config_prefix: Enc @@ -276,10 +290,14 @@ json: # match a command-line option, and its properties and help come from # other information known by generate_auto_job. This information is # used to construct a "schema" (as in JSON.hh) for the json input to - # QPDFJob. The leading underscore is removed. *NOTE*: all keys are - # converted to camelCase for the schema and must appear that way - # in the user-supplied json. This makes it more convenient to - # populate JSON objects in some languages. + # QPDFJob. The leading underscore is removed. If a key starts with a + # double underscore, it corresponds to a command-line argument that + # does not have a counterpart in the JSON. This signals that the + # command-line argument was not forgotten, but it is otherwise + # ignored by the JSON. *NOTE*: all keys are converted to camelCase + # for the schema and must appear that way in the user-supplied json. + # This makes it more convenient to populate JSON objects in some + # languages. # input _inputFile: "input filename" @@ -316,8 +334,9 @@ json: json-output: remove-restrictions: encrypt: - _user-password: "user password" - _owner-password: "owner password" + user-password: + owner-password: + __bits: _40bit: Enc40.annotate: Enc40.extract: diff --git a/libqpdf/QPDFJob_argv.cc b/libqpdf/QPDFJob_argv.cc index e7bc1ea8..adf7ba64 100644 --- a/libqpdf/QPDFJob_argv.cc +++ b/libqpdf/QPDFJob_argv.cc @@ -203,6 +203,24 @@ ArgParser::argEncPositional(std::string const& arg) this->c_enc = c_main->encrypt(keylen, user_password, owner_password); } +void +ArgParser::argEncUserPassword(std::string const& arg) +{ + // QXXXQ +} + +void +ArgParser::argEncOwnerPassword(std::string const& arg) +{ + // QXXXQ +} + +void +ArgParser::argEncBits(std::string const& arg) +{ + // QXXXQ +} + void ArgParser::argPages() { diff --git a/libqpdf/QPDFJob_json.cc b/libqpdf/QPDFJob_json.cc index 932cc84c..d44652f4 100644 --- a/libqpdf/QPDFJob_json.cc +++ b/libqpdf/QPDFJob_json.cc @@ -109,6 +109,10 @@ Handlers::initHandlers() #include + // We have `bits` in the CLI but not in the JSON. Reference this variable so it doesn't generate + // a warning. + [](char const**) {}(enc_bits_choices); + if (this->json_handlers.size() != 1) { throw std::logic_error("QPDFJob_json: json_handlers size != 1 at end"); } diff --git a/libqpdf/qpdf/auto_job_decl.hh b/libqpdf/qpdf/auto_job_decl.hh index 196a4fe7..31ea5086 100644 --- a/libqpdf/qpdf/auto_job_decl.hh +++ b/libqpdf/qpdf/auto_job_decl.hh @@ -32,6 +32,9 @@ void argPagesPositional(std::string const&); void argPagesPassword(std::string const&); void argEndPages(); void argEncPositional(std::string const&); +void argEncUserPassword(std::string const&); +void argEncOwnerPassword(std::string const&); +void argEncBits(std::string const&); void argEndEncryption(); void argEnd40BitEncryption(); void argEnd128BitEncryption(); diff --git a/libqpdf/qpdf/auto_job_help.hh b/libqpdf/qpdf/auto_job_help.hh index a21fe3c1..9f7cadfe 100644 --- a/libqpdf/qpdf/auto_job_help.hh +++ b/libqpdf/qpdf/auto_job_help.hh @@ -152,10 +152,25 @@ ap.addOptionHelp("--encrypt", "transformation", "start encryption options", R"(- Run qpdf --help=encryption for details. )"); +ap.addOptionHelp("--user-password", "transformation", "specify user password", R"(--user-password=user-password + +Set the user password. +)"); +ap.addOptionHelp("--owner-password", "transformation", "specify owner password", R"(--owner-password=owner-password + +Set the owner password. +)"); +ap.addOptionHelp("--bits", "transformation", "specify encryption bit depth", R"(--bits={48|128|256} + +Set the encrypt bit depth. Use 256. +)"); ap.addOptionHelp("--decrypt", "transformation", "remove encryption from input file", R"(Create an unencrypted output file even if the input file was encrypted. Normally qpdf preserves whatever encryption was present on the input file. This option overrides that behavior. )"); +} +static void add_help_3(QPDFArgParser& ap) +{ ap.addOptionHelp("--remove-restrictions", "transformation", "remove security restrictions from input file", R"(Remove restrictions associated with digitally signed PDF files. This may be combined with --decrypt to allow free editing of previously signed/encrypted files. This option invalidates the @@ -172,9 +187,6 @@ ap.addOptionHelp("--encryption-file-password", "transformation", "supply passwor If the file named in --copy-encryption requires a password, use this option to supply the password. )"); -} -static void add_help_3(QPDFArgParser& ap) -{ ap.addOptionHelp("--qdf", "transformation", "enable viewing PDF code in a text editor", R"(Create a PDF file suitable for viewing in a text editor and even editing. This is for editing the PDF code, not the page contents. All streams that can be uncompressed are uncompressed, and @@ -270,6 +282,9 @@ ap.addOptionHelp("--ii-min-bytes", "transformation", "set minimum size for --ext Don't externalize inline images smaller than this size. The default is 1,024. Use 0 for no minimum. )"); +} +static void add_help_4(QPDFArgParser& ap) +{ ap.addOptionHelp("--min-version", "transformation", "set minimum PDF version", R"(--min-version=version Force the PDF version of the output to be at least the specified @@ -297,9 +312,6 @@ resulting set of pages, where :odd starts with the first page and :even starts with the second page. These are odd and even pages from the resulting set, not based on the original page numbers. )"); -} -static void add_help_4(QPDFArgParser& ap) -{ ap.addHelpTopic("modification", "change parts of the PDF", R"(Modification options make systematic changes to certain parts of the PDF, causing the PDF to render differently from the original. )"); @@ -392,6 +404,9 @@ ap.addOptionHelp("--keep-inline-images", "modification", "exclude inline images )"); ap.addOptionHelp("--remove-page-labels", "modification", "remove explicit page numbers", R"(Exclude page labels (explicit page numbers) from the output file. )"); +} +static void add_help_5(QPDFArgParser& ap) +{ ap.addHelpTopic("encryption", "create encrypted files", R"(Create encrypted files. Usage: --encrypt user-password owner-password key-length [options] -- @@ -467,9 +482,6 @@ and filling in form fields. For 128-bit and 256-bit encryption, this also enables editing, creating, and deleting form fields unless --modify-other=n or --modify=none is also specified. )"); -} -static void add_help_5(QPDFArgParser& ap) -{ ap.addOptionHelp("--assemble", "encryption", "restrict document assembly", R"(--assemble=[y|n] Enable/disable document assembly (rotation and reordering of @@ -628,6 +640,9 @@ Specify pages from the overlay/underlay that are repeated after "from" pages have been exhausted. See qpdf --help=page-ranges for help with the page range syntax. )"); +} +static void add_help_6(QPDFArgParser& ap) +{ ap.addHelpTopic("attachments", "work with embedded files", R"(It is possible to list, add, or delete embedded files (also known as attachments) and to copy attachments from other files. See help on individual options for details. Run qpdf --help=add-attachment @@ -645,9 +660,6 @@ The --copy-attachments-from flag and its options may be repeated to copy attachments from multiple files. Run qpdf --help=copy-attachments for details. )"); -} -static void add_help_6(QPDFArgParser& ap) -{ ap.addOptionHelp("--remove-attachment", "attachments", "remove an embedded file", R"(--remove-attachment=key Remove an embedded file using its key. Get the key with @@ -746,6 +758,9 @@ of the actual PDF page content or semantic correctness of the PDF file. It merely checks that the PDF file is syntactically valid. See also qpdf --help=exit-status. )"); +} +static void add_help_7(QPDFArgParser& ap) +{ ap.addOptionHelp("--show-encryption", "inspection", "information about encrypted files", R"(Show document encryption parameters. Also show the document's user password if the owner password is given and the file was encrypted using older encryption formats that allow user @@ -757,9 +772,6 @@ underlying encryption key to be displayed. ap.addOptionHelp("--check-linearization", "inspection", "check linearization tables", R"(Check to see whether a file is linearized and, if so, whether the linearization hint tables are correct. )"); -} -static void add_help_7(QPDFArgParser& ap) -{ ap.addOptionHelp("--show-linearization", "inspection", "show linearization hint tables", R"(Check and display all data in the linearization hint tables. )"); ap.addOptionHelp("--show-xref", "inspection", "show cross reference data", R"(Show the contents of the cross-reference table or stream (object @@ -840,6 +852,9 @@ object number. The prefix can be overridden with when --json-output is specified, in which case the default is "inline". )"); +} +static void add_help_8(QPDFArgParser& ap) +{ ap.addOptionHelp("--json-stream-prefix", "json", "prefix for json stream data files", R"(--json-stream-prefix=file-prefix When used with --json-stream-data=file, --json-stream-data=file-prefix @@ -860,9 +875,6 @@ ap.addOptionHelp("--json-input", "json", "input file is qpdf JSON", R"(Treat the "qpdf JSON Format" section of the manual for information about how to use this option. )"); -} -static void add_help_8(QPDFArgParser& ap) -{ ap.addOptionHelp("--update-from-json", "json", "update a PDF from qpdf JSON", R"(--update-from-json=qpdf-json-file Update a PDF file from a JSON file. Please see the "qpdf JSON" diff --git a/libqpdf/qpdf/auto_job_init.hh b/libqpdf/qpdf/auto_job_init.hh index 177f2105..23b55c7f 100644 --- a/libqpdf/qpdf/auto_job_init.hh +++ b/libqpdf/qpdf/auto_job_init.hh @@ -23,6 +23,7 @@ static char const* json_key_choices[] = {"acroform", "attachments", "encrypt", " static char const* json_output_choices[] = {"2", "latest", 0}; static char const* json_stream_data_choices[] = {"none", "inline", "file", 0}; static char const* json_version_choices[] = {"1", "2", "latest", 0}; +static char const* enc_bits_choices[] = {"40", "128", "256", 0}; static char const* print128_choices[] = {"full", "low", "none", 0}; static char const* modify128_choices[] = {"all", "annotate", "form", "assembly", "none", 0}; @@ -128,6 +129,9 @@ this->ap.addPositional(p(&ArgParser::argPagesPositional)); this->ap.addRequiredParameter("password", p(&ArgParser::argPagesPassword), "password"); this->ap.registerOptionTable("encryption", b(&ArgParser::argEndEncryption)); this->ap.addPositional(p(&ArgParser::argEncPositional)); +this->ap.addRequiredParameter("user-password", p(&ArgParser::argEncUserPassword), "user_password"); +this->ap.addRequiredParameter("owner-password", p(&ArgParser::argEncOwnerPassword), "owner_password"); +this->ap.addChoices("bits", p(&ArgParser::argEncBits), true, enc_bits_choices); this->ap.registerOptionTable("40-bit encryption", b(&ArgParser::argEnd40BitEncryption)); this->ap.addChoices("extract", [this](std::string const& x){c_enc->extract(x);}, true, yn_choices); this->ap.addChoices("annotate", [this](std::string const& x){c_enc->annotate(x);}, true, yn_choices); diff --git a/libqpdf/qpdf/auto_job_json_init.hh b/libqpdf/qpdf/auto_job_json_init.hh index 8f642d7c..135a5159 100644 --- a/libqpdf/qpdf/auto_job_json_init.hh +++ b/libqpdf/qpdf/auto_job_json_init.hh @@ -16,6 +16,7 @@ static char const* json_key_choices[] = {"acroform", "attachments", "encrypt", " static char const* json_output_choices[] = {"2", "latest", 0}; static char const* json_stream_data_choices[] = {"none", "inline", "file", 0}; static char const* json_version_choices[] = {"1", "2", "latest", 0}; +static char const* enc_bits_choices[] = {"40", "128", "256", 0}; static char const* print128_choices[] = {"full", "low", "none", 0}; static char const* modify128_choices[] = {"all", "annotate", "form", "assembly", "none", 0}; @@ -120,6 +121,8 @@ popHandler(); // key: userPassword pushKey("ownerPassword"); setupEncryptOwnerPassword(); popHandler(); // key: ownerPassword +pushKey("Bits"); +popHandler(); // key: Bits pushKey("40bit"); beginDict(bindJSON(&Handlers::beginEncrypt40bit), bindBare(&Handlers::endEncrypt40bit)); // .encrypt.40bit pushKey("annotate"); diff --git a/libqpdf/qpdf/auto_job_schema.hh b/libqpdf/qpdf/auto_job_schema.hh index fbc13d8c..84f4cd4a 100644 --- a/libqpdf/qpdf/auto_job_schema.hh +++ b/libqpdf/qpdf/auto_job_schema.hh @@ -31,8 +31,9 @@ static constexpr char const* JOB_SCHEMA_DATA = R"({ "jsonOutput": "apply defaults for JSON serialization", "removeRestrictions": "remove security restrictions from input file", "encrypt": { - "userPassword": "user password", - "ownerPassword": "owner password", + "userPassword": "specify user password", + "ownerPassword": "specify owner password", + "Bits": null, "40bit": { "annotate": "restrict document annotation", "extract": "restrict text/graphic extraction", diff --git a/manual/cli.rst b/manual/cli.rst index 482d65f9..943b404e 100644 --- a/manual/cli.rst +++ b/manual/cli.rst @@ -723,6 +723,32 @@ Related Options This flag starts encryption options, used to create encrypted files. Please see :ref:`encryption-options` for details. +.. qpdf:option:: --user-password=user-password + + .. help: specify user password + + Set the user password. + + Set the user password for the encrypted file. + +.. qpdf:option:: --owner-password=owner-password + + .. help: specify owner password + + Set the owner password. + + Set the owner password for the encrypted file. + +.. qpdf:option:: --bits={48|128|256} + + .. help: specify encryption bit depth + + Set the encrypt bit depth. Use 256. + + Set the bit depth for encrypted files. You should always use + ``--bits=256`` unless you have a strong reason to create a file + with weaker encryption. + .. qpdf:option:: --decrypt .. help: remove encryption from input file