Commit 47f33cec2582101485ed93d041d069118a62b5cf

Authored by Jay Berkenbilt
1 parent e3506253

QPDFJob: add test cases

Showing 51 changed files with 802 additions and 2 deletions
libqpdf/QPDFJob_json.cc
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 #include <qpdf/JSONHandler.hh> 2 #include <qpdf/JSONHandler.hh>
3 #include <qpdf/QPDFUsage.hh> 3 #include <qpdf/QPDFUsage.hh>
4 #include <qpdf/QUtil.hh> 4 #include <qpdf/QUtil.hh>
  5 +#include <qpdf/QTC.hh>
5 6
6 #include <memory> 7 #include <memory>
7 #include <stdexcept> 8 #include <stdexcept>
@@ -108,6 +109,7 @@ Handlers::addBare(bare_handler_t fn) @@ -108,6 +109,7 @@ Handlers::addBare(bare_handler_t fn)
108 jh->addBoolHandler([this, fn](std::string const& path, bool v){ 109 jh->addBoolHandler([this, fn](std::string const& path, bool v){
109 if (! v) 110 if (! v)
110 { 111 {
  112 + QTC::TC("qpdf", "QPDFJob json bare not true");
111 usage(path + ": value must be true"); 113 usage(path + ": value must be true");
112 } 114 }
113 else 115 else
@@ -140,12 +142,14 @@ Handlers::addChoices(char const** choices, @@ -140,12 +142,14 @@ Handlers::addChoices(char const** choices,
140 { 142 {
141 if (strcmp(*i, p) == 0) 143 if (strcmp(*i, p) == 0)
142 { 144 {
  145 + QTC::TC("qpdf", "QPDFJob json choice match");
143 matches = true; 146 matches = true;
144 break; 147 break;
145 } 148 }
146 } 149 }
147 if (! matches) 150 if (! matches)
148 { 151 {
  152 + QTC::TC("qpdf", "QPDFJob json choice mismatch");
149 std::ostringstream msg; 153 std::ostringstream msg;
150 msg << path + ": unexpected value; expected one of "; 154 msg << path + ": unexpected value; expected one of ";
151 bool first = true; 155 bool first = true;
@@ -307,6 +311,7 @@ Handlers::beginOutputOptionsEncrypt(JSON j) @@ -307,6 +311,7 @@ Handlers::beginOutputOptionsEncrypt(JSON j)
307 { 311 {
308 if (key_len != 0) 312 if (key_len != 0)
309 { 313 {
  314 + QTC::TC("qpdf", "QPDFJob json encrypt duplicate key length");
310 usage("exactly one of 40bit, 128bit, or 256bit must be given"); 315 usage("exactly one of 40bit, 128bit, or 256bit must be given");
311 } 316 }
312 key_len = QUtil::string_to_int(key.c_str()); 317 key_len = QUtil::string_to_int(key.c_str());
@@ -322,12 +327,14 @@ Handlers::beginOutputOptionsEncrypt(JSON j) @@ -322,12 +327,14 @@ Handlers::beginOutputOptionsEncrypt(JSON j)
322 }); 327 });
323 if (key_len == 0) 328 if (key_len == 0)
324 { 329 {
  330 + QTC::TC("qpdf", "QPDFJob json encrypt no key length");
325 usage("exactly one of 40bit, 128bit, or 256bit must be given;" 331 usage("exactly one of 40bit, 128bit, or 256bit must be given;"
326 " an empty dictionary may be supplied for one of them" 332 " an empty dictionary may be supplied for one of them"
327 " to set the key length without imposing any restrictions"); 333 " to set the key length without imposing any restrictions");
328 } 334 }
329 if (! (user_password_seen && owner_password_seen)) 335 if (! (user_password_seen && owner_password_seen))
330 { 336 {
  337 + QTC::TC("qpdf", "QPDFJob json encrypt missing password");
331 usage("the user and owner password are both required; use the empty" 338 usage("the user and owner password are both required; use the empty"
332 " string for the user password if you don't want a password"); 339 " string for the user password if you don't want a password");
333 } 340 }
@@ -550,6 +557,7 @@ Handlers::beginOptionsPages(JSON j) @@ -550,6 +557,7 @@ Handlers::beginOptionsPages(JSON j)
550 }); 557 });
551 if (! file_seen) 558 if (! file_seen)
552 { 559 {
  560 + QTC::TC("qpdf", "QPDFJob json pages no file");
553 usage("file is required in page specification"); 561 usage("file is required in page specification");
554 } 562 }
555 this->c_pages->pageSpec( 563 this->c_pages->pageSpec(
qpdf/qpdf.testcov
@@ -630,3 +630,10 @@ qpdf check password password correct 0 @@ -630,3 +630,10 @@ qpdf check password password correct 0
630 qpdf check password not encrypted 0 630 qpdf check password not encrypted 0
631 QPDFJob_config password file 0 631 QPDFJob_config password file 0
632 QPDFJob_config password stdin 0 632 QPDFJob_config password stdin 0
  633 +QPDFJob json bare not true 0
  634 +QPDFJob json choice mismatch 0
  635 +QPDFJob json choice match 0
  636 +QPDFJob json encrypt no key length 0
  637 +QPDFJob json encrypt duplicate key length 0
  638 +QPDFJob json encrypt missing password 0
  639 +QPDFJob json pages no file 0
qpdf/qtest/qpdf.test
@@ -372,6 +372,93 @@ foreach my $f (@dangling) @@ -372,6 +372,93 @@ foreach my $f (@dangling)
372 } 372 }
373 show_ntests(); 373 show_ntests();
374 # ---------- 374 # ----------
  375 +$td->notify("--- QPDFJob Tests ---");
  376 +
  377 +open(F, ">auto-txt") or die;
  378 +print F "from file";
  379 +close(F);
  380 +
  381 +my @bad_json = (
  382 + "bare-option-false",
  383 + "choice-mismatch",
  384 + "encrypt-duplicate-key-length",
  385 + "encrypt-missing-password",
  386 + "encrypt-no-key-length",
  387 + "pages-no-file",
  388 + "schema-error",
  389 + "json-error"
  390 + );
  391 +my @good_json = (
  392 + "choice-match",
  393 + "input-file-password",
  394 + "empty-input",
  395 + "replace-input",
  396 + "encrypt-40",
  397 + "encrypt-128",
  398 + "encrypt-256-with-restrictions",
  399 + "add-attachments",
  400 + "copy-attachments",
  401 + "underlay-overlay",
  402 + "underlay-overlay-password",
  403 + "misc-options",
  404 + );
  405 +$n_tests += 4 + scalar(@bad_json) + (2 * scalar(@good_json));
  406 +
  407 +
  408 +foreach my $i (@bad_json)
  409 +{
  410 + $td->runtest("QPDFJob bad json: $i",
  411 + {$td->COMMAND => "qpdf --job-json-file=bad-json-$i.json"},
  412 + {$td->FILE => "bad-$i-json.out", $td->EXIT_STATUS => 2},
  413 + $td->NORMALIZE_NEWLINES);
  414 +}
  415 +
  416 +foreach my $i (@good_json)
  417 +{
  418 + if ($i eq 'replace-input')
  419 + {
  420 + copy("minimal.pdf", 'a.pdf');
  421 + }
  422 + $td->runtest("QPDFJob good json: $i",
  423 + {$td->COMMAND => "qpdf --job-json-file=job-json-$i.json"},
  424 + {$td->STRING => "", $td->EXIT_STATUS => 0},
  425 + $td->NORMALIZE_NEWLINES);
  426 + if ($i =~ m/encrypt-256/)
  427 + {
  428 + $td->runtest("check encryption $i",
  429 + {$td->COMMAND =>
  430 + "qpdf a.pdf --password=u" .
  431 + " --job-json-file=job-show-encryption.json"},
  432 + {$td->FILE => "job-json-$i.out", $td->EXIT_STATUS => 0},
  433 + $td->NORMALIZE_NEWLINES);
  434 + }
  435 + else
  436 + {
  437 + $td->runtest("check good json $i output",
  438 + {$td->FILE => "a.pdf"},
  439 + {$td->FILE => "job-json-$i.pdf"});
  440 + }
  441 +}
  442 +
  443 +
  444 +$td->runtest("QPDFJob json partial",
  445 + {$td->COMMAND => "test_driver 83 - job-partial.json"},
  446 + {$td->FILE => "job-partial-json.out", $td->EXIT_STATUS => 0},
  447 + $td->NORMALIZE_NEWLINES);
  448 +$td->runtest("QPDFJob API",
  449 + {$td->COMMAND => "test_driver 84 -"},
  450 + {$td->FILE => "job-api.out", $td->EXIT_STATUS => 0},
  451 + $td->NORMALIZE_NEWLINES);
  452 +$td->runtest("check output",
  453 + {$td->FILE => "a.pdf"},
  454 + {$td->FILE => "test84.pdf"});
  455 +$td->runtest("json output from job",
  456 + {$td->COMMAND => "qpdf --job-json-file=job-json-output.json"},
  457 + {$td->FILE => "job-json-output.out.json", $td->EXIT_STATUS => 0},
  458 + $td->NORMALIZE_NEWLINES);
  459 +
  460 +show_ntests();
  461 +# ----------
375 $td->notify("--- Form Tests ---"); 462 $td->notify("--- Form Tests ---");
376 463
377 my @form_tests = ( 464 my @form_tests = (
qpdf/qtest/qpdf/bad-bare-option-false-json.out 0 โ†’ 100644
  1 +
  2 +qpdf: error with job-json file bad-json-bare-option-false.json: .output.options.qdf: value must be true
  3 +Run qpdf--job-json-help for information on the file format.
  4 +
  5 +For help:
  6 + qpdf--help=usage usage information
  7 + qpdf--help=topic help on a topic
  8 + qpdf--help=--option help on an option
  9 + qpdf--help general help and a topic list
  10 +
qpdf/qtest/qpdf/bad-choice-mismatch-json.out 0 โ†’ 100644
  1 +
  2 +qpdf: error with job-json file bad-json-choice-mismatch.json: .output.options.objectStreams: unexpected value; expected one of disable, preserve, generate
  3 +Run qpdf--job-json-help for information on the file format.
  4 +
  5 +For help:
  6 + qpdf--help=usage usage information
  7 + qpdf--help=topic help on a topic
  8 + qpdf--help=--option help on an option
  9 + qpdf--help general help and a topic list
  10 +
qpdf/qtest/qpdf/bad-encrypt-duplicate-key-length-json.out 0 โ†’ 100644
  1 +
  2 +qpdf: error with job-json file bad-json-encrypt-duplicate-key-length.json: exactly one of 40bit, 128bit, or 256bit must be given
  3 +Run qpdf--job-json-help for information on the file format.
  4 +
  5 +For help:
  6 + qpdf--help=usage usage information
  7 + qpdf--help=topic help on a topic
  8 + qpdf--help=--option help on an option
  9 + qpdf--help general help and a topic list
  10 +
qpdf/qtest/qpdf/bad-encrypt-missing-password-json.out 0 โ†’ 100644
  1 +
  2 +qpdf: error with job-json file bad-json-encrypt-missing-password.json: the user and owner password are both required; use the empty string for the user password if you don't want a password
  3 +Run qpdf--job-json-help for information on the file format.
  4 +
  5 +For help:
  6 + qpdf--help=usage usage information
  7 + qpdf--help=topic help on a topic
  8 + qpdf--help=--option help on an option
  9 + qpdf--help general help and a topic list
  10 +
qpdf/qtest/qpdf/bad-encrypt-no-key-length-json.out 0 โ†’ 100644
  1 +
  2 +qpdf: error with job-json file bad-json-encrypt-no-key-length.json: exactly one of 40bit, 128bit, or 256bit must be given; an empty dictionary may be supplied for one of them to set the key length without imposing any restrictions
  3 +Run qpdf--job-json-help for information on the file format.
  4 +
  5 +For help:
  6 + qpdf--help=usage usage information
  7 + qpdf--help=topic help on a topic
  8 + qpdf--help=--option help on an option
  9 + qpdf--help general help and a topic list
  10 +
qpdf/qtest/qpdf/bad-json-bare-option-false.json 0 โ†’ 100644
  1 +{
  2 + "output": {
  3 + "options": {
  4 + "qdf": false
  5 + }
  6 + }
  7 +}
qpdf/qtest/qpdf/bad-json-choice-mismatch.json 0 โ†’ 100644
  1 +{
  2 + "output": {
  3 + "options": {
  4 + "objectStreams": "potato"
  5 + }
  6 + }
  7 +}
qpdf/qtest/qpdf/bad-json-encrypt-duplicate-key-length.json 0 โ†’ 100644
  1 +{
  2 + "output": {
  3 + "options": {
  4 + "encrypt": {
  5 + "userPassword": "",
  6 + "ownerPassword": "someOwnerThing",
  7 + "256bit": {
  8 + },
  9 + "128bit": {
  10 + }
  11 + }
  12 + }
  13 + }
  14 +}
qpdf/qtest/qpdf/bad-json-encrypt-missing-password.json 0 โ†’ 100644
  1 +{
  2 + "output": {
  3 + "options": {
  4 + "encrypt": {
  5 + "userPassword": "",
  6 + "256bit": {
  7 + }
  8 + }
  9 + }
  10 + }
  11 +}
qpdf/qtest/qpdf/bad-json-encrypt-no-key-length.json 0 โ†’ 100644
  1 +{
  2 + "output": {
  3 + "options": {
  4 + "encrypt": {
  5 + "userPassword": "",
  6 + "ownerPassword": "someOwnerThing"
  7 + }
  8 + }
  9 + }
  10 +}
qpdf/qtest/qpdf/bad-json-error-json.out 0 โ†’ 100644
  1 +
  2 +qpdf: error with job-json file bad-json-json-error.json: JSON: offset 130: unexpected dictionary end delimiter
  3 +Run qpdf--job-json-help for information on the file format.
  4 +
  5 +For help:
  6 + qpdf--help=usage usage information
  7 + qpdf--help=topic help on a topic
  8 + qpdf--help=--option help on an option
  9 + qpdf--help general help and a topic list
  10 +
qpdf/qtest/qpdf/bad-json-json-error.json 0 โ†’ 100644
  1 +{
  2 + "output": {
  3 + "options": {
  4 + "encrypt": {
  5 + "userPassword": "",
  6 + "ownerPassword": "someOwnerThing",
  7 + }
  8 + }
  9 + }
  10 +}
qpdf/qtest/qpdf/bad-json-pages-no-file.json 0 โ†’ 100644
  1 +{
  2 + "options": {
  3 + "pages": [
  4 + {
  5 + "range": "1-z"
  6 + }
  7 + ]
  8 + }
  9 +}
qpdf/qtest/qpdf/bad-json-schema-error.json 0 โ†’ 100644
  1 +{
  2 + "output": {
  3 + "potato": {
  4 + "encrypt": {
  5 + "userPassword": "",
  6 + "ownerPassword": "someOwnerThing",
  7 + "256bit": {
  8 + }
  9 + }
  10 + }
  11 + }
  12 +}
qpdf/qtest/qpdf/bad-pages-no-file-json.out 0 โ†’ 100644
  1 +
  2 +qpdf: error with job-json file bad-json-pages-no-file.json: file is required in page specification
  3 +Run qpdf--job-json-help for information on the file format.
  4 +
  5 +For help:
  6 + qpdf--help=usage usage information
  7 + qpdf--help=topic help on a topic
  8 + qpdf--help=--option help on an option
  9 + qpdf--help general help and a topic list
  10 +
qpdf/qtest/qpdf/bad-schema-error-json.out 0 โ†’ 100644
  1 +
  2 +qpdf: error with job-json file bad-json-schema-error.json: qpdf: job json has errors:
  3 + json key ".output": key "potato" is not present in schema but appears in object
  4 +Run qpdf--job-json-help for information on the file format.
  5 +
  6 +For help:
  7 + qpdf--help=usage usage information
  8 + qpdf--help=topic help on a topic
  9 + qpdf--help=--option help on an option
  10 + qpdf--help general help and a topic list
  11 +
qpdf/qtest/qpdf/job-api.out 0 โ†’ 100644
  1 +normal
  2 +error caught by check
  3 +finished config
  4 +usage: an input file name is required
  5 +error caught by run
  6 +finished config
  7 +usage: an input file name is required
  8 +test 84 done
qpdf/qtest/qpdf/job-json-add-attachments.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "minimal.pdf"
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "staticId": true
  9 + }
  10 + },
  11 + "options": {
  12 + "addAttachment": [
  13 + {
  14 + "file": "auto-txt",
  15 + "moddate": "D:20220131134246-05'00'",
  16 + "creationdate": "D:20220131134246-05'00'"
  17 + },
  18 + {
  19 + "file": "auto-txt",
  20 + "moddate": "D:20220131134246-05'00'",
  21 + "creationdate": "D:20220131134246-05'00'",
  22 + "filename": "auto2",
  23 + "key": "auto2-key"
  24 + }
  25 + ]
  26 + }
  27 +}
qpdf/qtest/qpdf/job-json-add-attachments.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-choice-match.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "minimal.pdf"
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "deterministicId": true,
  9 + "objectStreams": "generate"
  10 + }
  11 + }
  12 +}
qpdf/qtest/qpdf/job-json-choice-match.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-copy-attachments.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "minimal.pdf"
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "staticId": true
  9 + }
  10 + },
  11 + "options": {
  12 + "copyAttachmentsFrom": [
  13 + {
  14 + "file": "job-json-add-attachments.pdf"
  15 + },
  16 + {
  17 + "file": "20-pages.pdf",
  18 + "password": "user"
  19 + },
  20 + {
  21 + "file": "job-json-add-attachments.pdf",
  22 + "prefix": "p-"
  23 + }
  24 + ]
  25 + }
  26 +}
qpdf/qtest/qpdf/job-json-copy-attachments.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-empty-input.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "empty": true
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "staticId": true
  9 + }
  10 + },
  11 + "options": {
  12 + "pages": [
  13 + {
  14 + "file": "minimal.pdf"
  15 + },
  16 + {
  17 + "file": "20-pages.pdf",
  18 + "password": "user",
  19 + "range": "1-5"
  20 + }
  21 + ]
  22 + }
  23 +}
qpdf/qtest/qpdf/job-json-empty-input.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-encrypt-128.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "fxo-blue.pdf"
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "staticId": true,
  9 + "staticAesIv": true,
  10 + "encrypt": {
  11 + "userPassword": "u",
  12 + "ownerPassword": "o",
  13 + "128bit": {
  14 + "useAes": "y"
  15 + }
  16 + }
  17 + }
  18 + }
  19 +}
qpdf/qtest/qpdf/job-json-encrypt-128.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-encrypt-256-with-restrictions.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "minimal.pdf"
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "staticId": true,
  9 + "staticAesIv": true,
  10 + "encrypt": {
  11 + "userPassword": "u",
  12 + "ownerPassword": "o",
  13 + "256bit": {
  14 + "print": "low",
  15 + "modify": "form"
  16 + }
  17 + }
  18 + }
  19 + }
  20 +}
qpdf/qtest/qpdf/job-json-encrypt-256-with-restrictions.out 0 โ†’ 100644
  1 +R = 6
  2 +P = -2092
  3 +User password = u
  4 +Supplied password is user password
  5 +extract for accessibility: allowed
  6 +extract for any purpose: allowed
  7 +print low resolution: allowed
  8 +print high resolution: not allowed
  9 +modify document assembly: allowed
  10 +modify forms: allowed
  11 +modify annotations: not allowed
  12 +modify other: not allowed
  13 +modify anything: not allowed
  14 +stream encryption method: AESv3
  15 +string encryption method: AESv3
  16 +file encryption method: AESv3
qpdf/qtest/qpdf/job-json-encrypt-40.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "minimal.pdf"
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "staticId": true,
  9 + "encrypt": {
  10 + "userPassword": "u",
  11 + "ownerPassword": "o",
  12 + "40bit": {}
  13 + }
  14 + }
  15 + },
  16 + "options": {
  17 + "allowWeakCrypto": true
  18 + }
  19 +}
qpdf/qtest/qpdf/job-json-encrypt-40.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-input-file-password.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "20-pages.pdf",
  4 + "password": "user"
  5 + },
  6 + "output": {
  7 + "file": "a.pdf",
  8 + "options": {
  9 + "staticId": true,
  10 + "staticAesIv": true,
  11 + "compressStreams": "n"
  12 + }
  13 + }
  14 +}
qpdf/qtest/qpdf/job-json-input-file-password.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-misc-options.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "minimal.pdf"
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "staticId": true,
  9 + "linearize": true,
  10 + "compressStreams": "n"
  11 + }
  12 + }
  13 +}
qpdf/qtest/qpdf/job-json-misc-options.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-output.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "minimal.pdf"
  4 + },
  5 + "inspect": {
  6 + "json": "1",
  7 + "jsonKey": [
  8 + "pages",
  9 + "objects"
  10 + ],
  11 + "jsonObject": [
  12 + "trailer",
  13 + "5"
  14 + ]
  15 + }
  16 +}
qpdf/qtest/qpdf/job-json-output.out.json 0 โ†’ 100644
  1 +{
  2 + "objects": {
  3 + "5 0 R": [
  4 + "/PDF",
  5 + "/Text"
  6 + ],
  7 + "trailer": {
  8 + "/Root": "1 0 R",
  9 + "/Size": 7
  10 + }
  11 + },
  12 + "pages": [
  13 + {
  14 + "contents": [
  15 + "4 0 R"
  16 + ],
  17 + "images": [],
  18 + "label": null,
  19 + "object": "3 0 R",
  20 + "outlines": [],
  21 + "pageposfrom1": 1
  22 + }
  23 + ],
  24 + "parameters": {
  25 + "decodelevel": "generalized"
  26 + },
  27 + "version": 1
  28 +}
qpdf/qtest/qpdf/job-json-replace-input.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "a.pdf"
  4 + },
  5 + "output": {
  6 + "replaceInput": true,
  7 + "options": {
  8 + "staticId": true,
  9 + "objectStreams": "generate"
  10 + }
  11 + }
  12 +}
qpdf/qtest/qpdf/job-json-replace-input.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-underlay-overlay-password.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "minimal.pdf"
  4 + },
  5 + "output": {
  6 + "file": "a.pdf",
  7 + "options": {
  8 + "staticId": true
  9 + }
  10 + },
  11 + "options": {
  12 + "underlay": {
  13 + "file": "20-pages.pdf",
  14 + "password": "user",
  15 + "from": "5"
  16 + },
  17 + "overlay": {
  18 + "file": "job-json-encrypt-128.pdf",
  19 + "password": "o",
  20 + "from": "7"
  21 + }
  22 + }
  23 +}
qpdf/qtest/qpdf/job-json-underlay-overlay-password.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-json-underlay-overlay.json 0 โ†’ 100644
  1 +{
  2 + "input": {
  3 + "file": "20-pages.pdf",
  4 + "password": "owner"
  5 + },
  6 + "output": {
  7 + "file": "a.pdf",
  8 + "options": {
  9 + "staticId": true,
  10 + "decrypt": true
  11 + }
  12 + },
  13 + "options": {
  14 + "underlay": {
  15 + "file": "fxo-green.pdf"
  16 + },
  17 + "overlay": {
  18 + "file": "fxo-red.pdf",
  19 + "from": "1,2",
  20 + "repeat": "3"
  21 + }
  22 + }
  23 +}
qpdf/qtest/qpdf/job-json-underlay-overlay.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/job-partial-json.out 0 โ†’ 100644
  1 +calling initializeFromJson
  2 +usage: an input file name is required
  3 +test 83 done
qpdf/qtest/qpdf/job-partial.json 0 โ†’ 100644
  1 +{
  2 + "output": {
  3 + "options": {
  4 + "encrypt": {
  5 + "userPassword": "",
  6 + "ownerPassword": "",
  7 + "256bit": {
  8 + }
  9 + }
  10 + }
  11 + }
  12 +}
qpdf/qtest/qpdf/job-show-encryption.json 0 โ†’ 100644
  1 +{
  2 + "inspect": {
  3 + "showEncryption": true
  4 + }
  5 +}
qpdf/qtest/qpdf/test84.pdf 0 โ†’ 100644
  1 +%PDF-1.3
  2 +%ยฟรทยขรพ
  3 +%QDF-1.0
  4 +
  5 +%% Original object ID: 1 0
  6 +1 0 obj
  7 +<<
  8 + /Pages 2 0 R
  9 + /Type /Catalog
  10 +>>
  11 +endobj
  12 +
  13 +%% Original object ID: 2 0
  14 +2 0 obj
  15 +<<
  16 + /Count 1
  17 + /Kids [
  18 + 3 0 R
  19 + ]
  20 + /Type /Pages
  21 +>>
  22 +endobj
  23 +
  24 +%% Page 1
  25 +%% Original object ID: 3 0
  26 +3 0 obj
  27 +<<
  28 + /Contents 4 0 R
  29 + /MediaBox [
  30 + 0
  31 + 0
  32 + 612
  33 + 792
  34 + ]
  35 + /Parent 2 0 R
  36 + /Resources <<
  37 + /Font <<
  38 + /F1 6 0 R
  39 + >>
  40 + /ProcSet 7 0 R
  41 + >>
  42 + /Type /Page
  43 +>>
  44 +endobj
  45 +
  46 +%% Contents for page 1
  47 +%% Original object ID: 4 0
  48 +4 0 obj
  49 +<<
  50 + /Length 5 0 R
  51 +>>
  52 +stream
  53 +BT
  54 + /F1 24 Tf
  55 + 72 720 Td
  56 + (Potato) Tj
  57 +ET
  58 +endstream
  59 +endobj
  60 +
  61 +5 0 obj
  62 +44
  63 +endobj
  64 +
  65 +%% Original object ID: 6 0
  66 +6 0 obj
  67 +<<
  68 + /BaseFont /Helvetica
  69 + /Encoding /WinAnsiEncoding
  70 + /Name /F1
  71 + /Subtype /Type1
  72 + /Type /Font
  73 +>>
  74 +endobj
  75 +
  76 +%% Original object ID: 5 0
  77 +7 0 obj
  78 +[
  79 + /PDF
  80 + /Text
  81 +]
  82 +endobj
  83 +
  84 +xref
  85 +0 8
  86 +0000000000 65535 f
  87 +0000000052 00000 n
  88 +0000000133 00000 n
  89 +0000000242 00000 n
  90 +0000000484 00000 n
  91 +0000000583 00000 n
  92 +0000000629 00000 n
  93 +0000000774 00000 n
  94 +trailer <<
  95 + /Root 1 0 R
  96 + /Size 8
  97 + /ID [<cd76a1aff58b062d8141f81511edbe38><cd76a1aff58b062d8141f81511edbe38>]
  98 +>>
  99 +startxref
  100 +809
  101 +%%EOF
qpdf/test_driver.cc
@@ -20,6 +20,8 @@ @@ -20,6 +20,8 @@
20 #include <qpdf/QPDFWriter.hh> 20 #include <qpdf/QPDFWriter.hh>
21 #include <qpdf/QPDFSystemError.hh> 21 #include <qpdf/QPDFSystemError.hh>
22 #include <qpdf/QIntC.hh> 22 #include <qpdf/QIntC.hh>
  23 +#include <qpdf/QPDFJob.hh>
  24 +#include <qpdf/QPDFUsage.hh>
23 #include <iostream> 25 #include <iostream>
24 #include <sstream> 26 #include <sstream>
25 #include <algorithm> 27 #include <algorithm>
@@ -3140,6 +3142,81 @@ static void test_82(QPDF&amp; pdf, char const* arg2) @@ -3140,6 +3142,81 @@ static void test_82(QPDF&amp; pdf, char const* arg2)
3140 assert(! str.isOrHasName("/Marvin")); 3142 assert(! str.isOrHasName("/Marvin"));
3141 } 3143 }
3142 3144
  3145 +static void test_83(QPDF& pdf, char const* arg2)
  3146 +{
  3147 + // Test QPDFJob json with partial = false. For testing with
  3148 + // partial = true, we just use qpdf --job-json-file.
  3149 +
  3150 + QPDFJob j;
  3151 + PointerHolder<char> file_buf;
  3152 + size_t size;
  3153 + QUtil::read_file_into_memory(arg2, file_buf, size);
  3154 + try
  3155 + {
  3156 + std::cout << "calling initializeFromJson" << std::endl;
  3157 + j.initializeFromJson(std::string(file_buf.getPointer(), size));
  3158 + std::cout << "called initializeFromJson" << std::endl;
  3159 + }
  3160 + catch (QPDFUsage& e)
  3161 + {
  3162 + std::cerr << "usage: " << e.what() << std::endl;
  3163 + }
  3164 + catch (std::exception& e)
  3165 + {
  3166 + std::cerr << "exception: " << e.what() << std::endl;
  3167 + }
  3168 +}
  3169 +
  3170 +static void test_84(QPDF& pdf, char const* arg2)
  3171 +{
  3172 + // Test QPDFJob API
  3173 +
  3174 + std::cout << "normal" << std::endl;
  3175 + {
  3176 + QPDFJob j;
  3177 + j.config()
  3178 + ->inputFile("minimal.pdf")
  3179 + ->outputFile("a.pdf")
  3180 + ->qdf()
  3181 + ->deterministicId()
  3182 + ->objectStreams("preserve")
  3183 + ->checkConfiguration();
  3184 + j.run();
  3185 + }
  3186 +
  3187 + std::cout << "error caught by check" << std::endl;
  3188 + try
  3189 + {
  3190 + QPDFJob j;
  3191 + j.config()
  3192 + ->outputFile("a.pdf")
  3193 + ->qdf();
  3194 + std::cout << "finished config" << std::endl;
  3195 + j.checkConfiguration();
  3196 + assert(false);
  3197 + }
  3198 + catch (QPDFUsage& e)
  3199 + {
  3200 + std::cout << "usage: " << e.what() << std::endl;
  3201 + }
  3202 +
  3203 + std::cout << "error caught by run" << std::endl;
  3204 + try
  3205 + {
  3206 + QPDFJob j;
  3207 + j.config()
  3208 + ->outputFile("a.pdf")
  3209 + ->qdf();
  3210 + std::cout << "finished config" << std::endl;
  3211 + j.run();
  3212 + assert(false);
  3213 + }
  3214 + catch (QPDFUsage& e)
  3215 + {
  3216 + std::cout << "usage: " << e.what() << std::endl;
  3217 + }
  3218 +}
  3219 +
3143 void runtest(int n, char const* filename1, char const* arg2) 3220 void runtest(int n, char const* filename1, char const* arg2)
3144 { 3221 {
3145 // Most tests here are crafted to work on specific files. Look at 3222 // Most tests here are crafted to work on specific files. Look at
@@ -3206,7 +3283,7 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -3206,7 +3283,7 @@ void runtest(int n, char const* filename1, char const* arg2)
3206 pdf.processMemoryFile((std::string(filename1) + ".pdf").c_str(), 3283 pdf.processMemoryFile((std::string(filename1) + ".pdf").c_str(),
3207 p, size); 3284 p, size);
3208 } 3285 }
3209 - else if ((n == 61) || (n == 81)) 3286 + else if ((n == 61) || (n == 81) || (n == 83) || (n == 84))
3210 { 3287 {
3211 // Ignore filename argument entirely 3288 // Ignore filename argument entirely
3212 } 3289 }
@@ -3253,7 +3330,8 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -3253,7 +3330,8 @@ void runtest(int n, char const* filename1, char const* arg2)
3253 {68, test_68}, {69, test_69}, {70, test_70}, {71, test_71}, 3330 {68, test_68}, {69, test_69}, {70, test_70}, {71, test_71},
3254 {72, test_72}, {73, test_73}, {74, test_74}, {75, test_75}, 3331 {72, test_72}, {73, test_73}, {74, test_74}, {75, test_75},
3255 {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79}, 3332 {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79},
3256 - {80, test_80}, {81, test_81}, {82, test_82}, 3333 + {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83},
  3334 + {84, test_84},
3257 }; 3335 };
3258 3336
3259 auto fn = test_functions.find(n); 3337 auto fn = test_functions.find(n);