Commit a000488d2bd197217c5e3ba1f2c935a64ca89cf3
1 parent
3e39e0dc
Refactor `QPDFJob`: replace `PageSpec` with `Selection`, introduce `new_selectio…
…n` method, and update logic for handling page specifications.
Showing
5 changed files
with
53 additions
and
53 deletions
include/qpdf/QPDFJob.hh
| @@ -156,16 +156,6 @@ class QPDFJob | @@ -156,16 +156,6 @@ class QPDFJob | ||
| 156 | bool replace{false}; | 156 | bool replace{false}; |
| 157 | }; | 157 | }; |
| 158 | 158 | ||
| 159 | - struct PageSpec | ||
| 160 | - { | ||
| 161 | - PageSpec( | ||
| 162 | - std::string const& filename, std::string const& password, std::string const& range); | ||
| 163 | - | ||
| 164 | - std::string filename; | ||
| 165 | - std::string password; | ||
| 166 | - std::string range; | ||
| 167 | - }; | ||
| 168 | - | ||
| 169 | public: | 159 | public: |
| 170 | // CONFIGURATION | 160 | // CONFIGURATION |
| 171 | 161 | ||
| @@ -426,6 +416,7 @@ class QPDFJob | @@ -426,6 +416,7 @@ class QPDFJob | ||
| 426 | 416 | ||
| 427 | private: | 417 | private: |
| 428 | struct PageNo; | 418 | struct PageNo; |
| 419 | + struct Selection; | ||
| 429 | struct RotationSpec; | 420 | struct RotationSpec; |
| 430 | struct UnderOverlay; | 421 | struct UnderOverlay; |
| 431 | struct PageLabelSpec; | 422 | struct PageLabelSpec; |
| @@ -470,6 +461,8 @@ class QPDFJob | @@ -470,6 +461,8 @@ class QPDFJob | ||
| 470 | void setQPDFOptions(QPDF& pdf); | 461 | void setQPDFOptions(QPDF& pdf); |
| 471 | bool handlePageSpecs(QPDF& pdf); | 462 | bool handlePageSpecs(QPDF& pdf); |
| 472 | bool shouldRemoveUnreferencedResources(QPDF& pdf); | 463 | bool shouldRemoveUnreferencedResources(QPDF& pdf); |
| 464 | + void new_selection( | ||
| 465 | + std::string const& filename, std::string const& password, std::string const& range); | ||
| 473 | void handleRotations(QPDF& pdf); | 466 | void handleRotations(QPDF& pdf); |
| 474 | void getUOPagenos( | 467 | void getUOPagenos( |
| 475 | std::vector<UnderOverlay>& uo, std::vector<std::map<size_t, std::vector<int>>>& pagenos); | 468 | std::vector<UnderOverlay>& uo, std::vector<std::map<size_t, std::vector<int>>>& pagenos); |
libqpdf/QPDFJob.cc
| @@ -267,14 +267,6 @@ struct QPDFJob::PageNo | @@ -267,14 +267,6 @@ struct QPDFJob::PageNo | ||
| 267 | int no{1}; | 267 | int no{1}; |
| 268 | }; | 268 | }; |
| 269 | 269 | ||
| 270 | -QPDFJob::PageSpec::PageSpec( | ||
| 271 | - std::string const& filename, std::string const& password, std::string const& range) : | ||
| 272 | - filename(filename), | ||
| 273 | - password(password.empty() ? "" : password), | ||
| 274 | - range(range) | ||
| 275 | -{ | ||
| 276 | -} | ||
| 277 | - | ||
| 278 | QPDFPageData::QPDFPageData(std::string const& filename, QPDF* qpdf, std::string const& range) : | 270 | QPDFPageData::QPDFPageData(std::string const& filename, QPDF* qpdf, std::string const& range) : |
| 279 | filename(filename), | 271 | filename(filename), |
| 280 | qpdf(qpdf), | 272 | qpdf(qpdf), |
| @@ -464,7 +456,7 @@ QPDFJob::createQPDF() | @@ -464,7 +456,7 @@ QPDFJob::createQPDF() | ||
| 464 | pdf.updateFromJSON(m->update_from_json); | 456 | pdf.updateFromJSON(m->update_from_json); |
| 465 | } | 457 | } |
| 466 | 458 | ||
| 467 | - if (!m->page_specs.empty()) { | 459 | + if (!m->selections.empty()) { |
| 468 | if (!handlePageSpecs(pdf)) { | 460 | if (!handlePageSpecs(pdf)) { |
| 469 | m->warnings = true; | 461 | m->warnings = true; |
| 470 | } | 462 | } |
| @@ -2374,6 +2366,13 @@ added_page(QPDF& pdf, QPDFPageObjectHelper page) | @@ -2374,6 +2366,13 @@ added_page(QPDF& pdf, QPDFPageObjectHelper page) | ||
| 2374 | return added_page(pdf, page.getObjectHandle()); | 2366 | return added_page(pdf, page.getObjectHandle()); |
| 2375 | } | 2367 | } |
| 2376 | 2368 | ||
| 2369 | +void | ||
| 2370 | +QPDFJob::new_selection( | ||
| 2371 | + std::string const& filename, std::string const& password, std::string const& range) | ||
| 2372 | +{ | ||
| 2373 | + m->selections.emplace_back(filename, password, range); | ||
| 2374 | +} | ||
| 2375 | + | ||
| 2377 | // Handle all page specifications. Return true if it succeeded without warnings. | 2376 | // Handle all page specifications. Return true if it succeeded without warnings. |
| 2378 | bool | 2377 | bool |
| 2379 | QPDFJob::handlePageSpecs(QPDF& pdf) | 2378 | QPDFJob::handlePageSpecs(QPDF& pdf) |
| @@ -2383,12 +2382,12 @@ QPDFJob::handlePageSpecs(QPDF& pdf) | @@ -2383,12 +2382,12 @@ QPDFJob::handlePageSpecs(QPDF& pdf) | ||
| 2383 | // Parse all page specifications and translate them into lists of actual pages. | 2382 | // Parse all page specifications and translate them into lists of actual pages. |
| 2384 | 2383 | ||
| 2385 | // Handle "." as a shortcut for the input file | 2384 | // Handle "." as a shortcut for the input file |
| 2386 | - for (auto& page_spec: m->page_specs) { | ||
| 2387 | - if (page_spec.filename == ".") { | ||
| 2388 | - page_spec.filename = m->infilename; | 2385 | + for (auto& selection: m->selections) { |
| 2386 | + if (selection.filename == ".") { | ||
| 2387 | + selection.filename = m->infilename; | ||
| 2389 | } | 2388 | } |
| 2390 | - if (page_spec.range.empty()) { | ||
| 2391 | - page_spec.range = "1-z"; | 2389 | + if (selection.range.empty()) { |
| 2390 | + selection.range = "1-z"; | ||
| 2392 | } | 2391 | } |
| 2393 | } | 2392 | } |
| 2394 | 2393 | ||
| @@ -2397,8 +2396,8 @@ QPDFJob::handlePageSpecs(QPDF& pdf) | @@ -2397,8 +2396,8 @@ QPDFJob::handlePageSpecs(QPDF& pdf) | ||
| 2397 | // Rather than trying to code some portable heuristic based on OS limits, just hard-code | 2396 | // Rather than trying to code some portable heuristic based on OS limits, just hard-code |
| 2398 | // this at a given number and allow users to override. | 2397 | // this at a given number and allow users to override. |
| 2399 | std::set<std::string> filenames; | 2398 | std::set<std::string> filenames; |
| 2400 | - for (auto& page_spec: m->page_specs) { | ||
| 2401 | - filenames.insert(page_spec.filename); | 2399 | + for (auto& selection: m->selections) { |
| 2400 | + filenames.insert(selection.filename); | ||
| 2402 | } | 2401 | } |
| 2403 | m->keep_files_open = (filenames.size() <= m->keep_files_open_threshold); | 2402 | m->keep_files_open = (filenames.size() <= m->keep_files_open_threshold); |
| 2404 | QTC::TC("qpdf", "QPDFJob automatically set keep files open", m->keep_files_open ? 0 : 1); | 2403 | QTC::TC("qpdf", "QPDFJob automatically set keep files open", m->keep_files_open ? 0 : 1); |
| @@ -2414,8 +2413,8 @@ QPDFJob::handlePageSpecs(QPDF& pdf) | @@ -2414,8 +2413,8 @@ QPDFJob::handlePageSpecs(QPDF& pdf) | ||
| 2414 | page_spec_qpdfs[m->infilename] = &pdf; | 2413 | page_spec_qpdfs[m->infilename] = &pdf; |
| 2415 | std::vector<QPDFPageData> parsed_specs; | 2414 | std::vector<QPDFPageData> parsed_specs; |
| 2416 | std::map<unsigned long long, std::set<QPDFObjGen>> copied_pages; | 2415 | std::map<unsigned long long, std::set<QPDFObjGen>> copied_pages; |
| 2417 | - for (auto& page_spec: m->page_specs) { | ||
| 2418 | - if (!page_spec_qpdfs.contains(page_spec.filename)) { | 2416 | + for (auto& selection: m->selections) { |
| 2417 | + if (!page_spec_qpdfs.contains(selection.filename)) { | ||
| 2419 | // Open the PDF file and store the QPDF object. Throw a std::shared_ptr to the qpdf into | 2418 | // Open the PDF file and store the QPDF object. Throw a std::shared_ptr to the qpdf into |
| 2420 | // a heap so that it survives through copying to the output but gets cleaned up | 2419 | // a heap so that it survives through copying to the output but gets cleaned up |
| 2421 | // automatically at the end. Do not canonicalize the file name. Using two different | 2420 | // automatically at the end. Do not canonicalize the file name. Using two different |
| @@ -2423,41 +2422,41 @@ QPDFJob::handlePageSpecs(QPDF& pdf) | @@ -2423,41 +2422,41 @@ QPDFJob::handlePageSpecs(QPDF& pdf) | ||
| 2423 | // you are using this an example of how to do this with the API, you can just create two | 2422 | // you are using this an example of how to do this with the API, you can just create two |
| 2424 | // different QPDF objects to the same underlying file with the same path to achieve the | 2423 | // different QPDF objects to the same underlying file with the same path to achieve the |
| 2425 | // same effect. | 2424 | // same effect. |
| 2426 | - auto password = page_spec.password; | 2425 | + auto password = selection.password; |
| 2427 | if (!m->encryption_file.empty() && password.empty() && | 2426 | if (!m->encryption_file.empty() && password.empty() && |
| 2428 | - page_spec.filename == m->encryption_file) { | 2427 | + selection.filename == m->encryption_file) { |
| 2429 | QTC::TC("qpdf", "QPDFJob pages encryption password"); | 2428 | QTC::TC("qpdf", "QPDFJob pages encryption password"); |
| 2430 | password = m->encryption_file_password; | 2429 | password = m->encryption_file_password; |
| 2431 | } | 2430 | } |
| 2432 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { | 2431 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
| 2433 | - v << prefix << ": processing " << page_spec.filename << "\n"; | 2432 | + v << prefix << ": processing " << selection.filename << "\n"; |
| 2434 | }); | 2433 | }); |
| 2435 | std::shared_ptr<InputSource> is; | 2434 | std::shared_ptr<InputSource> is; |
| 2436 | ClosedFileInputSource* cis = nullptr; | 2435 | ClosedFileInputSource* cis = nullptr; |
| 2437 | if (!m->keep_files_open) { | 2436 | if (!m->keep_files_open) { |
| 2438 | QTC::TC("qpdf", "QPDFJob keep files open n"); | 2437 | QTC::TC("qpdf", "QPDFJob keep files open n"); |
| 2439 | - cis = new ClosedFileInputSource(page_spec.filename.c_str()); | 2438 | + cis = new ClosedFileInputSource(selection.filename.c_str()); |
| 2440 | is = std::shared_ptr<InputSource>(cis); | 2439 | is = std::shared_ptr<InputSource>(cis); |
| 2441 | cis->stayOpen(true); | 2440 | cis->stayOpen(true); |
| 2442 | } else { | 2441 | } else { |
| 2443 | QTC::TC("qpdf", "QPDFJob keep files open y"); | 2442 | QTC::TC("qpdf", "QPDFJob keep files open y"); |
| 2444 | - FileInputSource* fis = new FileInputSource(page_spec.filename.c_str()); | 2443 | + FileInputSource* fis = new FileInputSource(selection.filename.c_str()); |
| 2445 | is = std::shared_ptr<InputSource>(fis); | 2444 | is = std::shared_ptr<InputSource>(fis); |
| 2446 | } | 2445 | } |
| 2447 | std::unique_ptr<QPDF> qpdf_sp; | 2446 | std::unique_ptr<QPDF> qpdf_sp; |
| 2448 | processInputSource(qpdf_sp, is, password.data(), true); | 2447 | processInputSource(qpdf_sp, is, password.data(), true); |
| 2449 | - page_spec_qpdfs[page_spec.filename] = qpdf_sp.get(); | 2448 | + page_spec_qpdfs[selection.filename] = qpdf_sp.get(); |
| 2450 | page_heap.push_back(std::move(qpdf_sp)); | 2449 | page_heap.push_back(std::move(qpdf_sp)); |
| 2451 | if (cis) { | 2450 | if (cis) { |
| 2452 | cis->stayOpen(false); | 2451 | cis->stayOpen(false); |
| 2453 | - page_spec_cfis[page_spec.filename] = cis; | 2452 | + page_spec_cfis[selection.filename] = cis; |
| 2454 | } | 2453 | } |
| 2455 | } | 2454 | } |
| 2456 | 2455 | ||
| 2457 | // Read original pages from the PDF, and parse the page range associated with this | 2456 | // Read original pages from the PDF, and parse the page range associated with this |
| 2458 | // occurrence of the file. | 2457 | // occurrence of the file. |
| 2459 | parsed_specs.emplace_back( | 2458 | parsed_specs.emplace_back( |
| 2460 | - page_spec.filename, page_spec_qpdfs[page_spec.filename], page_spec.range); | 2459 | + selection.filename, page_spec_qpdfs[selection.filename], selection.range); |
| 2461 | } | 2460 | } |
| 2462 | 2461 | ||
| 2463 | std::map<unsigned long long, bool> remove_unreferenced; | 2462 | std::map<unsigned long long, bool> remove_unreferenced; |
libqpdf/QPDFJob_config.cc
| @@ -978,7 +978,7 @@ QPDFJob::PagesConfig::PagesConfig(Config* c) : | @@ -978,7 +978,7 @@ QPDFJob::PagesConfig::PagesConfig(Config* c) : | ||
| 978 | std::shared_ptr<QPDFJob::PagesConfig> | 978 | std::shared_ptr<QPDFJob::PagesConfig> |
| 979 | QPDFJob::Config::pages() | 979 | QPDFJob::Config::pages() |
| 980 | { | 980 | { |
| 981 | - if (!o.m->page_specs.empty()) { | 981 | + if (!o.m->selections.empty()) { |
| 982 | usage("--pages may only be specified one time"); | 982 | usage("--pages may only be specified one time"); |
| 983 | } | 983 | } |
| 984 | return std::shared_ptr<PagesConfig>(new PagesConfig(this)); | 984 | return std::shared_ptr<PagesConfig>(new PagesConfig(this)); |
| @@ -987,7 +987,7 @@ QPDFJob::Config::pages() | @@ -987,7 +987,7 @@ QPDFJob::Config::pages() | ||
| 987 | QPDFJob::Config* | 987 | QPDFJob::Config* |
| 988 | QPDFJob::PagesConfig::endPages() | 988 | QPDFJob::PagesConfig::endPages() |
| 989 | { | 989 | { |
| 990 | - auto n_specs = config->o.m->page_specs.size(); | 990 | + auto n_specs = config->o.m->selections.size(); |
| 991 | if (n_specs == 0) { | 991 | if (n_specs == 0) { |
| 992 | usage("--pages: no page specifications given"); | 992 | usage("--pages: no page specifications given"); |
| 993 | } | 993 | } |
| @@ -998,27 +998,25 @@ QPDFJob::PagesConfig* | @@ -998,27 +998,25 @@ QPDFJob::PagesConfig* | ||
| 998 | QPDFJob::PagesConfig::pageSpec( | 998 | QPDFJob::PagesConfig::pageSpec( |
| 999 | std::string const& filename, std::string const& range, char const* password) | 999 | std::string const& filename, std::string const& range, char const* password) |
| 1000 | { | 1000 | { |
| 1001 | - config->o.m->page_specs.emplace_back(filename, password, range); | 1001 | + config->o.new_selection(filename, {password ? password : ""}, range); |
| 1002 | return this; | 1002 | return this; |
| 1003 | } | 1003 | } |
| 1004 | 1004 | ||
| 1005 | QPDFJob::PagesConfig* | 1005 | QPDFJob::PagesConfig* |
| 1006 | QPDFJob::PagesConfig::file(std::string const& arg) | 1006 | QPDFJob::PagesConfig::file(std::string const& arg) |
| 1007 | { | 1007 | { |
| 1008 | - config->o.m->page_specs.emplace_back(arg, "", ""); | 1008 | + config->o.new_selection(arg, {}, {}); |
| 1009 | return this; | 1009 | return this; |
| 1010 | } | 1010 | } |
| 1011 | 1011 | ||
| 1012 | QPDFJob::PagesConfig* | 1012 | QPDFJob::PagesConfig* |
| 1013 | QPDFJob::PagesConfig::range(std::string const& arg) | 1013 | QPDFJob::PagesConfig::range(std::string const& arg) |
| 1014 | { | 1014 | { |
| 1015 | - if (config->o.m->page_specs.empty()) { | ||
| 1016 | - QTC::TC("qpdf", "QPDFJob misplaced page range"); | 1015 | + if (config->o.m->selections.empty()) { |
| 1017 | usage("in --range must follow a file name"); | 1016 | usage("in --range must follow a file name"); |
| 1018 | } | 1017 | } |
| 1019 | - auto& last = config->o.m->page_specs.back(); | 1018 | + auto& last = config->o.m->selections.back(); |
| 1020 | if (!last.range.empty()) { | 1019 | if (!last.range.empty()) { |
| 1021 | - QTC::TC("qpdf", "QPDFJob duplicated range"); | ||
| 1022 | usage("--range already specified for this file"); | 1020 | usage("--range already specified for this file"); |
| 1023 | } | 1021 | } |
| 1024 | last.range = arg; | 1022 | last.range = arg; |
| @@ -1028,13 +1026,11 @@ QPDFJob::PagesConfig::range(std::string const& arg) | @@ -1028,13 +1026,11 @@ QPDFJob::PagesConfig::range(std::string const& arg) | ||
| 1028 | QPDFJob::PagesConfig* | 1026 | QPDFJob::PagesConfig* |
| 1029 | QPDFJob::PagesConfig::password(std::string const& arg) | 1027 | QPDFJob::PagesConfig::password(std::string const& arg) |
| 1030 | { | 1028 | { |
| 1031 | - if (config->o.m->page_specs.empty()) { | ||
| 1032 | - QTC::TC("qpdf", "QPDFJob misplaced pages password"); | 1029 | + if (config->o.m->selections.empty()) { |
| 1033 | usage("in --pages, --password must follow a file name"); | 1030 | usage("in --pages, --password must follow a file name"); |
| 1034 | } | 1031 | } |
| 1035 | - auto& last = config->o.m->page_specs.back(); | 1032 | + auto& last = config->o.m->selections.back(); |
| 1036 | if (!last.password.empty()) { | 1033 | if (!last.password.empty()) { |
| 1037 | - QTC::TC("qpdf", "QPDFJob duplicated pages password"); | ||
| 1038 | usage("--password already specified for this file"); | 1034 | usage("--password already specified for this file"); |
| 1039 | } | 1035 | } |
| 1040 | last.password = arg; | 1036 | last.password = arg; |
libqpdf/qpdf/QPDFJob_private.hh
| @@ -6,6 +6,22 @@ | @@ -6,6 +6,22 @@ | ||
| 6 | #include <qpdf/ClosedFileInputSource.hh> | 6 | #include <qpdf/ClosedFileInputSource.hh> |
| 7 | #include <qpdf/QPDFLogger.hh> | 7 | #include <qpdf/QPDFLogger.hh> |
| 8 | 8 | ||
| 9 | +// A selection of pages from a single input PDF to be included in the output. This corresponds to a | ||
| 10 | +// single clause in the --pages option. | ||
| 11 | +struct QPDFJob::Selection | ||
| 12 | +{ | ||
| 13 | + Selection(std::string const& filename, std::string const& password, std::string const& range) : | ||
| 14 | + filename(filename), | ||
| 15 | + password(password), | ||
| 16 | + range(range) | ||
| 17 | + { | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + std::string filename; | ||
| 21 | + std::string password; | ||
| 22 | + std::string range; | ||
| 23 | +}; | ||
| 24 | + | ||
| 9 | struct QPDFJob::RotationSpec | 25 | struct QPDFJob::RotationSpec |
| 10 | { | 26 | { |
| 11 | RotationSpec(int angle = 0, bool relative = false) : | 27 | RotationSpec(int angle = 0, bool relative = false) : |
| @@ -195,7 +211,7 @@ class QPDFJob::Members | @@ -195,7 +211,7 @@ class QPDFJob::Members | ||
| 195 | std::vector<UnderOverlay> underlay; | 211 | std::vector<UnderOverlay> underlay; |
| 196 | std::vector<UnderOverlay> overlay; | 212 | std::vector<UnderOverlay> overlay; |
| 197 | UnderOverlay* under_overlay{nullptr}; | 213 | UnderOverlay* under_overlay{nullptr}; |
| 198 | - std::vector<PageSpec> page_specs; | 214 | + std::vector<Selection> selections; |
| 199 | std::map<std::string, RotationSpec> rotations; | 215 | std::map<std::string, RotationSpec> rotations; |
| 200 | bool require_outfile{true}; | 216 | bool require_outfile{true}; |
| 201 | bool replace_input{false}; | 217 | bool replace_input{false}; |
qpdf/qpdf.testcov
| @@ -491,8 +491,6 @@ qpdf-c called qpdf_oh_get_binary_string_value 0 | @@ -491,8 +491,6 @@ qpdf-c called qpdf_oh_get_binary_string_value 0 | ||
| 491 | qpdf-c called qpdf_oh_get_binary_utf8_value 0 | 491 | qpdf-c called qpdf_oh_get_binary_utf8_value 0 |
| 492 | qpdf-c called qpdf_oh_new_binary_string 0 | 492 | qpdf-c called qpdf_oh_new_binary_string 0 |
| 493 | qpdf-c called qpdf_oh_new_binary_unicode_string 0 | 493 | qpdf-c called qpdf_oh_new_binary_unicode_string 0 |
| 494 | -QPDFJob duplicated pages password 0 | ||
| 495 | -QPDFJob misplaced pages password 0 | ||
| 496 | QPDFJob check encrypted encrypted 0 | 494 | QPDFJob check encrypted encrypted 0 |
| 497 | QPDFJob check encrypted not encrypted 0 | 495 | QPDFJob check encrypted not encrypted 0 |
| 498 | QPDFJob check password password incorrect 0 | 496 | QPDFJob check password password incorrect 0 |
| @@ -545,8 +543,6 @@ QPDFPageObjectHelper used fallback without copying 0 | @@ -545,8 +543,6 @@ QPDFPageObjectHelper used fallback without copying 0 | ||
| 545 | QPDF skipping cache for known unchecked object 0 | 543 | QPDF skipping cache for known unchecked object 0 |
| 546 | QPDF fix dangling triggered xref reconstruction 0 | 544 | QPDF fix dangling triggered xref reconstruction 0 |
| 547 | QPDF recover xref stream 0 | 545 | QPDF recover xref stream 0 |
| 548 | -QPDFJob misplaced page range 0 | ||
| 549 | -QPDFJob duplicated range 0 | ||
| 550 | QPDFJob json over/under no file 0 | 546 | QPDFJob json over/under no file 0 |
| 551 | QPDF_Array copy 1 | 547 | QPDF_Array copy 1 |
| 552 | QPDF_json stream data not string 0 | 548 | QPDF_json stream data not string 0 |