Commit 2f232e8efbb9d8eaa4bd34d0b4de2ad557d3200c
Committed by
GitHub
Merge pull request #1204 from m-holger/assemble
Refactor QPDFJob::handlePageSpecs
Showing
13 changed files
with
744 additions
and
645 deletions
include/qpdf/QPDFJob.hh
| ... | ... | @@ -156,16 +156,6 @@ class QPDFJob |
| 156 | 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 | 159 | public: |
| 170 | 160 | // CONFIGURATION |
| 171 | 161 | |
| ... | ... | @@ -425,58 +415,16 @@ class QPDFJob |
| 425 | 415 | [[deprecated("use job_json_schema(version)")]] static std::string QPDF_DLL job_json_schema_v1(); |
| 426 | 416 | |
| 427 | 417 | private: |
| 428 | - struct RotationSpec | |
| 429 | - { | |
| 430 | - RotationSpec(int angle = 0, bool relative = false) : | |
| 431 | - angle(angle), | |
| 432 | - relative(relative) | |
| 433 | - { | |
| 434 | - } | |
| 435 | - | |
| 436 | - int angle; | |
| 437 | - bool relative; | |
| 438 | - }; | |
| 418 | + struct PageNo; | |
| 419 | + struct Selection; | |
| 420 | + struct Input; | |
| 421 | + struct Inputs; | |
| 422 | + struct RotationSpec; | |
| 423 | + struct UnderOverlay; | |
| 424 | + struct PageLabelSpec; | |
| 439 | 425 | |
| 440 | 426 | enum password_mode_e { pm_bytes, pm_hex_bytes, pm_unicode, pm_auto }; |
| 441 | 427 | |
| 442 | - struct UnderOverlay | |
| 443 | - { | |
| 444 | - UnderOverlay(char const* which) : | |
| 445 | - which(which), | |
| 446 | - to_nr("1-z"), | |
| 447 | - from_nr("1-z"), | |
| 448 | - repeat_nr("") | |
| 449 | - { | |
| 450 | - } | |
| 451 | - | |
| 452 | - std::string which; | |
| 453 | - std::string filename; | |
| 454 | - std::string password; | |
| 455 | - std::string to_nr; | |
| 456 | - std::string from_nr; | |
| 457 | - std::string repeat_nr; | |
| 458 | - std::unique_ptr<QPDF> pdf; | |
| 459 | - std::vector<int> to_pagenos; | |
| 460 | - std::vector<int> from_pagenos; | |
| 461 | - std::vector<int> repeat_pagenos; | |
| 462 | - }; | |
| 463 | - | |
| 464 | - struct PageLabelSpec | |
| 465 | - { | |
| 466 | - PageLabelSpec( | |
| 467 | - int first_page, qpdf_page_label_e label_type, int start_num, std::string_view prefix) : | |
| 468 | - first_page(first_page), | |
| 469 | - label_type(label_type), | |
| 470 | - start_num(start_num), | |
| 471 | - prefix(prefix) | |
| 472 | - { | |
| 473 | - } | |
| 474 | - int first_page; | |
| 475 | - qpdf_page_label_e label_type; | |
| 476 | - int start_num{1}; | |
| 477 | - std::string prefix; | |
| 478 | - }; | |
| 479 | - | |
| 480 | 428 | // Helper functions |
| 481 | 429 | static void usage(std::string const& msg); |
| 482 | 430 | static JSON json_schema(int json_version, std::set<std::string>* keys = nullptr); |
| ... | ... | @@ -513,20 +461,19 @@ class QPDFJob |
| 513 | 461 | |
| 514 | 462 | // Transformations |
| 515 | 463 | void setQPDFOptions(QPDF& pdf); |
| 516 | - void handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_heap); | |
| 464 | + void handlePageSpecs(QPDF& pdf); | |
| 517 | 465 | bool shouldRemoveUnreferencedResources(QPDF& pdf); |
| 518 | 466 | void handleRotations(QPDF& pdf); |
| 519 | 467 | void getUOPagenos( |
| 520 | - std::vector<UnderOverlay>& uo, std::map<int, std::map<size_t, std::vector<int>>>& pagenos); | |
| 468 | + std::vector<UnderOverlay>& uo, std::vector<std::map<size_t, std::vector<int>>>& pagenos); | |
| 521 | 469 | void handleUnderOverlay(QPDF& pdf); |
| 522 | 470 | std::string doUnderOverlayForPage( |
| 523 | 471 | QPDF& pdf, |
| 524 | 472 | UnderOverlay& uo, |
| 525 | - std::map<int, std::map<size_t, std::vector<int>>>& pagenos, | |
| 526 | - size_t page_idx, | |
| 473 | + std::vector<std::map<size_t, std::vector<int>>>& pagenos, | |
| 474 | + PageNo const& page_idx, | |
| 527 | 475 | size_t uo_idx, |
| 528 | 476 | std::map<int, std::map<size_t, QPDFObjectHandle>>& fo, |
| 529 | - std::vector<QPDFPageObjectHelper>& pages, | |
| 530 | 477 | QPDFPageObjectHelper& dest_page); |
| 531 | 478 | void validateUnderOverlay(QPDF& pdf, UnderOverlay* uo); |
| 532 | 479 | void handleTransformations(QPDF& pdf); |
| ... | ... | @@ -568,159 +515,8 @@ class QPDFJob |
| 568 | 515 | |
| 569 | 516 | enum remove_unref_e { re_auto, re_yes, re_no }; |
| 570 | 517 | |
| 571 | - class Members | |
| 572 | - { | |
| 573 | - friend class QPDFJob; | |
| 574 | - | |
| 575 | - public: | |
| 576 | - QPDF_DLL | |
| 577 | - ~Members() = default; | |
| 518 | + class Members; | |
| 578 | 519 | |
| 579 | - private: | |
| 580 | - // These default values are duplicated in help and docs. | |
| 581 | - static int constexpr DEFAULT_KEEP_FILES_OPEN_THRESHOLD = 200; | |
| 582 | - static int constexpr DEFAULT_OI_MIN_WIDTH = 128; | |
| 583 | - static int constexpr DEFAULT_OI_MIN_HEIGHT = 128; | |
| 584 | - static int constexpr DEFAULT_OI_MIN_AREA = 16384; | |
| 585 | - static int constexpr DEFAULT_II_MIN_BYTES = 1024; | |
| 586 | - | |
| 587 | - Members(); | |
| 588 | - Members(Members const&) = delete; | |
| 589 | - | |
| 590 | - std::shared_ptr<QPDFLogger> log; | |
| 591 | - std::string message_prefix{"qpdf"}; | |
| 592 | - bool warnings{false}; | |
| 593 | - unsigned long encryption_status{0}; | |
| 594 | - bool verbose{false}; | |
| 595 | - std::string password; | |
| 596 | - bool linearize{false}; | |
| 597 | - bool decrypt{false}; | |
| 598 | - bool remove_restrictions{false}; | |
| 599 | - int split_pages{0}; | |
| 600 | - bool progress{false}; | |
| 601 | - std::function<void(int)> progress_handler{nullptr}; | |
| 602 | - bool suppress_warnings{false}; | |
| 603 | - bool warnings_exit_zero{false}; | |
| 604 | - bool copy_encryption{false}; | |
| 605 | - std::string encryption_file; | |
| 606 | - std::string encryption_file_password; | |
| 607 | - bool encrypt{false}; | |
| 608 | - bool password_is_hex_key{false}; | |
| 609 | - bool suppress_password_recovery{false}; | |
| 610 | - password_mode_e password_mode{pm_auto}; | |
| 611 | - bool allow_insecure{false}; | |
| 612 | - bool allow_weak_crypto{false}; | |
| 613 | - std::string user_password; | |
| 614 | - std::string owner_password; | |
| 615 | - int keylen{0}; | |
| 616 | - bool r2_print{true}; | |
| 617 | - bool r2_modify{true}; | |
| 618 | - bool r2_extract{true}; | |
| 619 | - bool r2_annotate{true}; | |
| 620 | - bool r3_accessibility{true}; | |
| 621 | - bool r3_extract{true}; | |
| 622 | - bool r3_assemble{true}; | |
| 623 | - bool r3_annotate_and_form{true}; | |
| 624 | - bool r3_form_filling{true}; | |
| 625 | - bool r3_modify_other{true}; | |
| 626 | - qpdf_r3_print_e r3_print{qpdf_r3p_full}; | |
| 627 | - bool force_V4{false}; | |
| 628 | - bool force_R5{false}; | |
| 629 | - bool cleartext_metadata{false}; | |
| 630 | - bool use_aes{false}; | |
| 631 | - bool stream_data_set{false}; | |
| 632 | - qpdf_stream_data_e stream_data_mode{qpdf_s_compress}; | |
| 633 | - bool compress_streams{true}; | |
| 634 | - bool compress_streams_set{false}; | |
| 635 | - bool recompress_flate{false}; | |
| 636 | - bool recompress_flate_set{false}; | |
| 637 | - int compression_level{-1}; | |
| 638 | - int jpeg_quality{-1}; | |
| 639 | - qpdf_stream_decode_level_e decode_level{qpdf_dl_generalized}; | |
| 640 | - bool decode_level_set{false}; | |
| 641 | - bool normalize_set{false}; | |
| 642 | - bool normalize{false}; | |
| 643 | - bool suppress_recovery{false}; | |
| 644 | - bool object_stream_set{false}; | |
| 645 | - qpdf_object_stream_e object_stream_mode{qpdf_o_preserve}; | |
| 646 | - bool ignore_xref_streams{false}; | |
| 647 | - bool qdf_mode{false}; | |
| 648 | - bool preserve_unreferenced_objects{false}; | |
| 649 | - remove_unref_e remove_unreferenced_page_resources{re_auto}; | |
| 650 | - bool keep_files_open{true}; | |
| 651 | - bool keep_files_open_set{false}; | |
| 652 | - size_t keep_files_open_threshold{DEFAULT_KEEP_FILES_OPEN_THRESHOLD}; | |
| 653 | - bool newline_before_endstream{false}; | |
| 654 | - std::string linearize_pass1; | |
| 655 | - bool coalesce_contents{false}; | |
| 656 | - bool flatten_annotations{false}; | |
| 657 | - int flatten_annotations_required{0}; | |
| 658 | - int flatten_annotations_forbidden{an_invisible | an_hidden}; | |
| 659 | - bool generate_appearances{false}; | |
| 660 | - PDFVersion max_input_version; | |
| 661 | - std::string min_version; | |
| 662 | - std::string force_version; | |
| 663 | - bool show_npages{false}; | |
| 664 | - bool deterministic_id{false}; | |
| 665 | - bool static_id{false}; | |
| 666 | - bool static_aes_iv{false}; | |
| 667 | - bool suppress_original_object_id{false}; | |
| 668 | - bool show_encryption{false}; | |
| 669 | - bool show_encryption_key{false}; | |
| 670 | - bool check_linearization{false}; | |
| 671 | - bool show_linearization{false}; | |
| 672 | - bool show_xref{false}; | |
| 673 | - bool show_trailer{false}; | |
| 674 | - int show_obj{0}; | |
| 675 | - int show_gen{0}; | |
| 676 | - bool show_raw_stream_data{false}; | |
| 677 | - bool show_filtered_stream_data{false}; | |
| 678 | - bool show_pages{false}; | |
| 679 | - bool show_page_images{false}; | |
| 680 | - std::vector<size_t> collate; | |
| 681 | - bool flatten_rotation{false}; | |
| 682 | - bool list_attachments{false}; | |
| 683 | - std::string attachment_to_show; | |
| 684 | - std::list<std::string> attachments_to_remove; | |
| 685 | - std::list<AddAttachment> attachments_to_add; | |
| 686 | - std::list<CopyAttachmentFrom> attachments_to_copy; | |
| 687 | - int json_version{0}; | |
| 688 | - std::set<std::string> json_keys; | |
| 689 | - std::set<std::string> json_objects; | |
| 690 | - qpdf_json_stream_data_e json_stream_data{qpdf_sj_none}; | |
| 691 | - bool json_stream_data_set{false}; | |
| 692 | - std::string json_stream_prefix; | |
| 693 | - bool test_json_schema{false}; | |
| 694 | - bool check{false}; | |
| 695 | - bool optimize_images{false}; | |
| 696 | - bool externalize_inline_images{false}; | |
| 697 | - bool keep_inline_images{false}; | |
| 698 | - bool remove_info{false}; | |
| 699 | - bool remove_metadata{false}; | |
| 700 | - bool remove_page_labels{false}; | |
| 701 | - bool remove_structure{false}; | |
| 702 | - size_t oi_min_width{DEFAULT_OI_MIN_WIDTH}; | |
| 703 | - size_t oi_min_height{DEFAULT_OI_MIN_HEIGHT}; | |
| 704 | - size_t oi_min_area{DEFAULT_OI_MIN_AREA}; | |
| 705 | - size_t ii_min_bytes{DEFAULT_II_MIN_BYTES}; | |
| 706 | - std::vector<UnderOverlay> underlay; | |
| 707 | - std::vector<UnderOverlay> overlay; | |
| 708 | - UnderOverlay* under_overlay{nullptr}; | |
| 709 | - std::vector<PageSpec> page_specs; | |
| 710 | - std::map<std::string, RotationSpec> rotations; | |
| 711 | - bool require_outfile{true}; | |
| 712 | - bool replace_input{false}; | |
| 713 | - bool check_is_encrypted{false}; | |
| 714 | - bool check_requires_password{false}; | |
| 715 | - std::string infilename; | |
| 716 | - bool empty_input{false}; | |
| 717 | - std::string outfilename; | |
| 718 | - bool json_input{false}; | |
| 719 | - bool json_output{false}; | |
| 720 | - std::string update_from_json; | |
| 721 | - bool report_mem_usage{false}; | |
| 722 | - std::vector<PageLabelSpec> page_label_specs; | |
| 723 | - }; | |
| 724 | 520 | std::shared_ptr<Members> m; |
| 725 | 521 | }; |
| 726 | 522 | ... | ... |
libqpdf/QPDFJob.cc
| 1 | -#include <qpdf/QPDFJob.hh> | |
| 1 | +#include <qpdf/QPDFJob_private.hh> | |
| 2 | 2 | |
| 3 | 3 | #include <cstring> |
| 4 | 4 | #include <iostream> |
| ... | ... | @@ -58,18 +58,7 @@ namespace |
| 58 | 58 | std::shared_ptr<Pl_DCT::CompressConfig> config; |
| 59 | 59 | }; |
| 60 | 60 | |
| 61 | - struct QPDFPageData | |
| 62 | - { | |
| 63 | - QPDFPageData(std::string const& filename, QPDF* qpdf, std::string const& range); | |
| 64 | - QPDFPageData(QPDFPageData const& other, int page); | |
| 65 | - | |
| 66 | - std::string filename; | |
| 67 | - QPDF* qpdf; | |
| 68 | - std::vector<QPDFObjectHandle> orig_pages; | |
| 69 | - std::vector<int> selected_pages; | |
| 70 | - }; | |
| 71 | - | |
| 72 | - class ProgressReporter: public QPDFWriter::ProgressReporter | |
| 61 | + class ProgressReporter final: public QPDFWriter::ProgressReporter | |
| 73 | 62 | { |
| 74 | 63 | public: |
| 75 | 64 | ProgressReporter(Pipeline& p, std::string const& prefix, char const* filename) : |
| ... | ... | @@ -78,8 +67,12 @@ namespace |
| 78 | 67 | filename(filename) |
| 79 | 68 | { |
| 80 | 69 | } |
| 81 | - ~ProgressReporter() override = default; | |
| 82 | - void reportProgress(int) override; | |
| 70 | + ~ProgressReporter() final = default; | |
| 71 | + void | |
| 72 | + reportProgress(int percentage) final | |
| 73 | + { | |
| 74 | + p << prefix << ": " << filename << ": write progress: " << percentage << "%\n"; | |
| 75 | + } | |
| 83 | 76 | |
| 84 | 77 | private: |
| 85 | 78 | Pipeline& p; |
| ... | ... | @@ -239,47 +232,32 @@ ImageOptimizer::provideStreamData(QPDFObjGen const&, Pipeline* pipeline) |
| 239 | 232 | image.pipeStreamData(p.get(), 0, decode_level, false, false); |
| 240 | 233 | } |
| 241 | 234 | |
| 242 | -QPDFJob::PageSpec::PageSpec( | |
| 243 | - std::string const& filename, std::string const& password, std::string const& range) : | |
| 244 | - filename(filename), | |
| 245 | - password(password.empty() ? "" : password), | |
| 246 | - range(range) | |
| 235 | +// Page number (1 based) and index (0 based). Defaults to page number 1 / index 0. | |
| 236 | +struct QPDFJob::PageNo | |
| 247 | 237 | { |
| 248 | -} | |
| 238 | + PageNo() = default; | |
| 239 | + PageNo(PageNo const&) = default; | |
| 249 | 240 | |
| 250 | -QPDFPageData::QPDFPageData(std::string const& filename, QPDF* qpdf, std::string const& range) : | |
| 251 | - filename(filename), | |
| 252 | - qpdf(qpdf), | |
| 253 | - orig_pages(qpdf->getAllPages()) | |
| 254 | -{ | |
| 255 | - try { | |
| 256 | - selected_pages = QUtil::parse_numrange(range.c_str(), QIntC::to_int(orig_pages.size())); | |
| 257 | - } catch (std::runtime_error& e) { | |
| 258 | - throw std::runtime_error("parsing numeric range for " + filename + ": " + e.what()); | |
| 241 | + PageNo(int no) : | |
| 242 | + idx{QIntC::to_size(no - 1)}, | |
| 243 | + no{no} | |
| 244 | + { | |
| 259 | 245 | } |
| 260 | -} | |
| 261 | 246 | |
| 262 | -QPDFPageData::QPDFPageData(QPDFPageData const& other, int page) : | |
| 263 | - filename(other.filename), | |
| 264 | - qpdf(other.qpdf), | |
| 265 | - orig_pages(other.orig_pages) | |
| 266 | -{ | |
| 267 | - selected_pages.push_back(page); | |
| 268 | -} | |
| 269 | - | |
| 270 | -void | |
| 271 | -ProgressReporter::reportProgress(int percentage) | |
| 272 | -{ | |
| 273 | - p << prefix << ": " << filename << ": write progress: " << percentage << "%\n"; | |
| 274 | -} | |
| 247 | + PageNo& | |
| 248 | + operator++() | |
| 249 | + { | |
| 250 | + ++idx; | |
| 251 | + ++no; | |
| 252 | + return *this; | |
| 253 | + } | |
| 275 | 254 | |
| 276 | -QPDFJob::Members::Members() : | |
| 277 | - log(QPDFLogger::defaultLogger()) | |
| 278 | -{ | |
| 279 | -} | |
| 255 | + size_t idx{0}; | |
| 256 | + int no{1}; | |
| 257 | +}; | |
| 280 | 258 | |
| 281 | 259 | QPDFJob::QPDFJob() : |
| 282 | - m(new Members()) | |
| 260 | + m(std::make_shared<Members>(*this)) | |
| 283 | 261 | { |
| 284 | 262 | } |
| 285 | 263 | |
| ... | ... | @@ -416,7 +394,7 @@ QPDFJob::createQPDF() |
| 416 | 394 | checkConfiguration(); |
| 417 | 395 | std::unique_ptr<QPDF> pdf_sp; |
| 418 | 396 | try { |
| 419 | - processFile(pdf_sp, m->infilename.data(), m->password.data(), true, true); | |
| 397 | + processFile(pdf_sp, m->infile_nm(), m->password.data(), true, true); | |
| 420 | 398 | } catch (QPDFExc& e) { |
| 421 | 399 | if (e.getErrorCode() == qpdf_e_password) { |
| 422 | 400 | // Allow certain operations to work when an incorrect password is supplied. |
| ... | ... | @@ -447,40 +425,34 @@ QPDFJob::createQPDF() |
| 447 | 425 | pdf.updateFromJSON(m->update_from_json); |
| 448 | 426 | } |
| 449 | 427 | |
| 450 | - std::vector<std::unique_ptr<QPDF>> page_heap; | |
| 451 | - if (!m->page_specs.empty()) { | |
| 452 | - handlePageSpecs(pdf, page_heap); | |
| 453 | - } | |
| 428 | + handlePageSpecs(pdf); | |
| 454 | 429 | if (!m->rotations.empty()) { |
| 455 | 430 | handleRotations(pdf); |
| 456 | 431 | } |
| 457 | 432 | handleUnderOverlay(pdf); |
| 458 | 433 | handleTransformations(pdf); |
| 434 | + m->warnings |= m->inputs.clear(); | |
| 435 | + | |
| 436 | + auto root = pdf.getRoot(); | |
| 459 | 437 | if (m->remove_info) { |
| 460 | 438 | auto trailer = pdf.getTrailer(); |
| 461 | - auto mod_date = trailer.getKey("/Info").getKeyIfDict("/ModDate"); | |
| 439 | + auto mod_date = trailer["/Info"]["/ModDate"]; | |
| 462 | 440 | if (mod_date.null()) { |
| 463 | - trailer.removeKey("/Info"); | |
| 441 | + trailer.erase("/Info"); | |
| 464 | 442 | } else { |
| 465 | - auto info = trailer.replaceKeyAndGetNew( | |
| 466 | - "/Info", pdf.makeIndirectObject(QPDFObjectHandle::newDictionary())); | |
| 467 | - info.replaceKey("/ModDate", mod_date); | |
| 443 | + trailer.replaceKey( | |
| 444 | + "/Info", pdf.makeIndirectObject(Dictionary({{"/ModDate", mod_date}}))); | |
| 468 | 445 | } |
| 469 | - pdf.getRoot().removeKey("/Metadata"); | |
| 446 | + root.erase("/Metadata"); | |
| 470 | 447 | } |
| 471 | 448 | if (m->remove_metadata) { |
| 472 | - pdf.getRoot().removeKey("/Metadata"); | |
| 449 | + root.erase("/Metadata"); | |
| 473 | 450 | } |
| 474 | 451 | if (m->remove_structure) { |
| 475 | - pdf.getRoot().removeKey("/StructTreeRoot"); | |
| 476 | - pdf.getRoot().removeKey("/MarkInfo"); | |
| 452 | + root.erase("/StructTreeRoot"); | |
| 453 | + root.erase("/MarkInfo"); | |
| 477 | 454 | } |
| 478 | 455 | |
| 479 | - for (auto& foreign: page_heap) { | |
| 480 | - if (foreign->anyWarnings()) { | |
| 481 | - m->warnings = true; | |
| 482 | - } | |
| 483 | - } | |
| 484 | 456 | return pdf_sp; |
| 485 | 457 | } |
| 486 | 458 | |
| ... | ... | @@ -595,10 +567,10 @@ QPDFJob::checkConfiguration() |
| 595 | 567 | // standard output. |
| 596 | 568 | m->outfilename = "-"; |
| 597 | 569 | } |
| 598 | - if (m->infilename.empty() && !m->empty_input) { | |
| 570 | + if (m->infile_name().empty() && !m->empty_input) { | |
| 599 | 571 | usage("an input file name is required"); |
| 600 | 572 | } |
| 601 | - if (m->replace_input && m->infilename.empty()) { | |
| 573 | + if (m->replace_input && m->infile_name().empty()) { | |
| 602 | 574 | usage("--replace-input may not be used with --empty"); |
| 603 | 575 | } |
| 604 | 576 | if (m->require_outfile && m->outfilename.empty() && !m->replace_input) { |
| ... | ... | @@ -637,8 +609,7 @@ QPDFJob::checkConfiguration() |
| 637 | 609 | if (save_to_stdout) { |
| 638 | 610 | m->log->saveToStandardOutput(true); |
| 639 | 611 | } |
| 640 | - if (!m->split_pages && QUtil::same_file(m->infilename.data(), m->outfilename.data())) { | |
| 641 | - QTC::TC("qpdf", "QPDFJob same file error"); | |
| 612 | + if (!m->split_pages && QUtil::same_file(m->infile_nm(), m->outfilename.data())) { | |
| 642 | 613 | usage( |
| 643 | 614 | "input file and output file are the same; use --replace-input to intentionally " |
| 644 | 615 | "overwrite the input file"); |
| ... | ... | @@ -762,7 +733,7 @@ QPDFJob::doCheck(QPDF& pdf) |
| 762 | 733 | // may continue to perform additional checks after finding errors. |
| 763 | 734 | bool okay = true; |
| 764 | 735 | auto& cout = *m->log->getInfo(); |
| 765 | - cout << "checking " << m->infilename << "\n"; | |
| 736 | + cout << "checking " << m->infile_name() << "\n"; | |
| 766 | 737 | QPDF::JobSetter::setCheckMode(pdf, true); |
| 767 | 738 | try { |
| 768 | 739 | int extension_level = pdf.getExtensionLevel(); |
| ... | ... | @@ -933,7 +904,7 @@ QPDFJob::doListAttachments(QPDF& pdf) |
| 933 | 904 | }); |
| 934 | 905 | } |
| 935 | 906 | } else { |
| 936 | - *m->log->getInfo() << m->infilename << " has no embedded files\n"; | |
| 907 | + *m->log->getInfo() << m->infile_name() << " has no embedded files\n"; | |
| 937 | 908 | } |
| 938 | 909 | } |
| 939 | 910 | |
| ... | ... | @@ -1699,7 +1670,6 @@ QPDFJob::doInspection(QPDF& pdf) |
| 1699 | 1670 | doCheck(pdf); |
| 1700 | 1671 | } |
| 1701 | 1672 | if (m->show_npages) { |
| 1702 | - QTC::TC("qpdf", "QPDFJob npages"); | |
| 1703 | 1673 | cout << pdf.getRoot().getKey("/Pages").getKey("/Count").getIntValue() << "\n"; |
| 1704 | 1674 | } |
| 1705 | 1675 | if (m->show_encryption) { |
| ... | ... | @@ -1707,9 +1677,9 @@ QPDFJob::doInspection(QPDF& pdf) |
| 1707 | 1677 | } |
| 1708 | 1678 | if (m->check_linearization) { |
| 1709 | 1679 | if (!pdf.isLinearized()) { |
| 1710 | - cout << m->infilename << " is not linearized\n"; | |
| 1680 | + cout << m->infile_name() << " is not linearized\n"; | |
| 1711 | 1681 | } else if (pdf.checkLinearization()) { |
| 1712 | - cout << m->infilename << ": no linearization errors\n"; | |
| 1682 | + cout << m->infile_name() << ": no linearization errors\n"; | |
| 1713 | 1683 | } else { |
| 1714 | 1684 | m->warnings = true; |
| 1715 | 1685 | } |
| ... | ... | @@ -1718,7 +1688,7 @@ QPDFJob::doInspection(QPDF& pdf) |
| 1718 | 1688 | if (pdf.isLinearized()) { |
| 1719 | 1689 | pdf.showLinearizationData(); |
| 1720 | 1690 | } else { |
| 1721 | - cout << m->infilename << " is not linearized\n"; | |
| 1691 | + cout << m->infile_name() << " is not linearized\n"; | |
| 1722 | 1692 | } |
| 1723 | 1693 | } |
| 1724 | 1694 | if (m->show_xref) { |
| ... | ... | @@ -1755,7 +1725,7 @@ QPDFJob::doProcessOnce( |
| 1755 | 1725 | if (empty) { |
| 1756 | 1726 | pdf->emptyPDF(); |
| 1757 | 1727 | } else if (main_input && m->json_input) { |
| 1758 | - pdf->createFromJSON(m->infilename); | |
| 1728 | + pdf->createFromJSON(m->infile_name()); | |
| 1759 | 1729 | } else { |
| 1760 | 1730 | fn(pdf.get(), password); |
| 1761 | 1731 | } |
| ... | ... | @@ -1867,25 +1837,22 @@ QPDFJob::processInputSource( |
| 1867 | 1837 | void |
| 1868 | 1838 | QPDFJob::validateUnderOverlay(QPDF& pdf, UnderOverlay* uo) |
| 1869 | 1839 | { |
| 1870 | - auto& main_pdh = pdf.pages(); | |
| 1871 | - int main_npages = QIntC::to_int(main_pdh.getAllPages().size()); | |
| 1872 | 1840 | processFile(uo->pdf, uo->filename.data(), uo->password.data(), true, false); |
| 1873 | - QPDFPageDocumentHelper uo_pdh(*(uo->pdf)); | |
| 1874 | - int uo_npages = QIntC::to_int(uo_pdh.getAllPages().size()); | |
| 1875 | 1841 | try { |
| 1876 | - uo->to_pagenos = QUtil::parse_numrange(uo->to_nr.c_str(), main_npages); | |
| 1842 | + uo->to_pagenos = | |
| 1843 | + QUtil::parse_numrange(uo->to_nr.data(), static_cast<int>(pdf.getAllPages().size())); | |
| 1877 | 1844 | } catch (std::runtime_error& e) { |
| 1878 | 1845 | throw std::runtime_error( |
| 1879 | 1846 | "parsing numeric range for " + uo->which + " \"to\" pages: " + e.what()); |
| 1880 | 1847 | } |
| 1881 | 1848 | try { |
| 1882 | 1849 | if (uo->from_nr.empty()) { |
| 1883 | - QTC::TC("qpdf", "QPDFJob from_nr from repeat_nr"); | |
| 1884 | 1850 | uo->from_nr = uo->repeat_nr; |
| 1885 | 1851 | } |
| 1886 | - uo->from_pagenos = QUtil::parse_numrange(uo->from_nr.c_str(), uo_npages); | |
| 1852 | + int uo_npages = static_cast<int>(uo->pdf->getAllPages().size()); | |
| 1853 | + uo->from_pagenos = QUtil::parse_numrange(uo->from_nr.data(), uo_npages); | |
| 1887 | 1854 | if (!uo->repeat_nr.empty()) { |
| 1888 | - uo->repeat_pagenos = QUtil::parse_numrange(uo->repeat_nr.c_str(), uo_npages); | |
| 1855 | + uo->repeat_pagenos = QUtil::parse_numrange(uo->repeat_nr.data(), uo_npages); | |
| 1889 | 1856 | } |
| 1890 | 1857 | } catch (std::runtime_error& e) { |
| 1891 | 1858 | throw std::runtime_error( |
| ... | ... | @@ -1897,29 +1864,28 @@ std::string |
| 1897 | 1864 | QPDFJob::doUnderOverlayForPage( |
| 1898 | 1865 | QPDF& pdf, |
| 1899 | 1866 | UnderOverlay& uo, |
| 1900 | - std::map<int, std::map<size_t, std::vector<int>>>& pagenos, | |
| 1901 | - size_t page_idx, | |
| 1867 | + std::vector<std::map<size_t, std::vector<int>>>& pagenos, | |
| 1868 | + PageNo const& pageno, | |
| 1902 | 1869 | size_t uo_idx, |
| 1903 | 1870 | std::map<int, std::map<size_t, QPDFObjectHandle>>& fo, |
| 1904 | - std::vector<QPDFPageObjectHelper>& pages, | |
| 1905 | 1871 | QPDFPageObjectHelper& dest_page) |
| 1906 | 1872 | { |
| 1907 | - int pageno = 1 + QIntC::to_int(page_idx); | |
| 1908 | - if (!(pagenos.contains(pageno) && pagenos[pageno].contains(uo_idx))) { | |
| 1873 | + if (!(uo.pdf && pagenos[pageno.idx].contains(uo_idx))) { | |
| 1909 | 1874 | return ""; |
| 1910 | 1875 | } |
| 1911 | 1876 | auto& dest_afdh = dest_page.qpdf()->acroform(); |
| 1912 | 1877 | |
| 1878 | + auto const& pages = uo.pdf->getAllPages(); | |
| 1913 | 1879 | std::string content; |
| 1914 | 1880 | int min_suffix = 1; |
| 1915 | 1881 | QPDFObjectHandle resources = dest_page.getAttribute("/Resources", true); |
| 1916 | - for (int from_pageno: pagenos[pageno][uo_idx]) { | |
| 1882 | + for (PageNo from_no: pagenos[pageno.idx][uo_idx]) { | |
| 1917 | 1883 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
| 1918 | - v << " " << uo.filename << " " << uo.which << " " << from_pageno << "\n"; | |
| 1884 | + v << " " << uo.filename << " " << uo.which << " " << from_no.no << "\n"; | |
| 1919 | 1885 | }); |
| 1920 | - auto from_page = pages.at(QIntC::to_size(from_pageno - 1)); | |
| 1921 | - if (!fo[from_pageno].contains(uo_idx)) { | |
| 1922 | - fo[from_pageno][uo_idx] = pdf.copyForeignObject(from_page.getFormXObjectForPage()); | |
| 1886 | + QPDFPageObjectHelper from_page = pages.at(from_no.idx); | |
| 1887 | + if (!fo[from_no.no].contains(uo_idx)) { | |
| 1888 | + fo[from_no.no][uo_idx] = pdf.copyForeignObject(from_page.getFormXObjectForPage()); | |
| 1923 | 1889 | } |
| 1924 | 1890 | |
| 1925 | 1891 | // If the same page is overlaid or underlaid multiple times, we'll generate multiple names |
| ... | ... | @@ -1927,13 +1893,13 @@ QPDFJob::doUnderOverlayForPage( |
| 1927 | 1893 | std::string name = resources.getUniqueResourceName("/Fx", min_suffix); |
| 1928 | 1894 | QPDFMatrix cm; |
| 1929 | 1895 | std::string new_content = dest_page.placeFormXObject( |
| 1930 | - fo[from_pageno][uo_idx], name, dest_page.getTrimBox().getArrayAsRectangle(), cm); | |
| 1896 | + fo[from_no.no][uo_idx], name, dest_page.getTrimBox().getArrayAsRectangle(), cm); | |
| 1931 | 1897 | dest_page.copyAnnotations(from_page, cm, &dest_afdh, &from_page.qpdf()->acroform()); |
| 1932 | 1898 | if (!new_content.empty()) { |
| 1933 | 1899 | resources.mergeResources("<< /XObject << >> >>"_qpdf); |
| 1934 | 1900 | auto xobject = resources.getKey("/XObject"); |
| 1935 | 1901 | if (xobject.isDictionary()) { |
| 1936 | - xobject.replaceKey(name, fo[from_pageno][uo_idx]); | |
| 1902 | + xobject.replaceKey(name, fo[from_no.no][uo_idx]); | |
| 1937 | 1903 | } |
| 1938 | 1904 | ++min_suffix; |
| 1939 | 1905 | content += new_content; |
| ... | ... | @@ -1945,14 +1911,15 @@ QPDFJob::doUnderOverlayForPage( |
| 1945 | 1911 | void |
| 1946 | 1912 | QPDFJob::getUOPagenos( |
| 1947 | 1913 | std::vector<QPDFJob::UnderOverlay>& uos, |
| 1948 | - std::map<int, std::map<size_t, std::vector<int>>>& pagenos) | |
| 1914 | + std::vector<std::map<size_t, std::vector<int>>>& pagenos) | |
| 1949 | 1915 | { |
| 1950 | 1916 | size_t uo_idx = 0; |
| 1951 | 1917 | for (auto const& uo: uos) { |
| 1952 | 1918 | size_t page_idx = 0; |
| 1953 | 1919 | size_t from_size = uo.from_pagenos.size(); |
| 1954 | 1920 | size_t repeat_size = uo.repeat_pagenos.size(); |
| 1955 | - for (int to_pageno: uo.to_pagenos) { | |
| 1921 | + for (int to_pageno_i: uo.to_pagenos) { | |
| 1922 | + size_t to_pageno = static_cast<size_t>(to_pageno_i - 1); | |
| 1956 | 1923 | if (page_idx < from_size) { |
| 1957 | 1924 | pagenos[to_pageno][uo_idx].push_back(uo.from_pagenos.at(page_idx)); |
| 1958 | 1925 | } else if (repeat_size) { |
| ... | ... | @@ -1978,67 +1945,47 @@ QPDFJob::handleUnderOverlay(QPDF& pdf) |
| 1978 | 1945 | validateUnderOverlay(pdf, &uo); |
| 1979 | 1946 | } |
| 1980 | 1947 | |
| 1981 | - // First map key is 1-based page number. Second is index into the overlay/underlay vector. Watch | |
| 1982 | - // out to not reverse the keys or be off by one. | |
| 1983 | - std::map<int, std::map<size_t, std::vector<int>>> underlay_pagenos; | |
| 1984 | - std::map<int, std::map<size_t, std::vector<int>>> overlay_pagenos; | |
| 1948 | + auto const& dest_pages = pdf.getAllPages(); | |
| 1949 | + | |
| 1950 | + // First vector key is 0-based page number. Second is index into the overlay/underlay vector. | |
| 1951 | + // Watch out to not reverse the keys or be off by one. | |
| 1952 | + std::vector<std::map<size_t, std::vector<int>>> underlay_pagenos(dest_pages.size()); | |
| 1953 | + std::vector<std::map<size_t, std::vector<int>>> overlay_pagenos(dest_pages.size()); | |
| 1985 | 1954 | getUOPagenos(m->underlay, underlay_pagenos); |
| 1986 | 1955 | getUOPagenos(m->overlay, overlay_pagenos); |
| 1987 | 1956 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
| 1988 | 1957 | v << prefix << ": processing underlay/overlay\n"; |
| 1989 | 1958 | }); |
| 1990 | 1959 | |
| 1991 | - auto get_pages = [](std::vector<UnderOverlay>& v, | |
| 1992 | - std::vector<std::vector<QPDFPageObjectHelper>>& v_out) { | |
| 1993 | - for (auto const& uo: v) { | |
| 1994 | - if (uo.pdf) { | |
| 1995 | - v_out.push_back(QPDFPageDocumentHelper(*(uo.pdf)).getAllPages()); | |
| 1996 | - } | |
| 1997 | - } | |
| 1998 | - }; | |
| 1999 | - std::vector<std::vector<QPDFPageObjectHelper>> upages; | |
| 2000 | - get_pages(m->underlay, upages); | |
| 2001 | - std::vector<std::vector<QPDFPageObjectHelper>> opages; | |
| 2002 | - get_pages(m->overlay, opages); | |
| 2003 | - | |
| 2004 | 1960 | std::map<int, std::map<size_t, QPDFObjectHandle>> underlay_fo; |
| 2005 | 1961 | std::map<int, std::map<size_t, QPDFObjectHandle>> overlay_fo; |
| 2006 | - QPDFPageDocumentHelper main_pdh(pdf); | |
| 2007 | - auto main_pages = main_pdh.getAllPages(); | |
| 2008 | - size_t main_npages = main_pages.size(); | |
| 2009 | - for (size_t page_idx = 0; page_idx < main_npages; ++page_idx) { | |
| 2010 | - auto pageno = QIntC::to_int(page_idx) + 1; | |
| 2011 | - doIfVerbose( | |
| 2012 | - [&](Pipeline& v, std::string const& prefix) { v << " page " << pageno << "\n"; }); | |
| 2013 | - if (underlay_pagenos[pageno].empty() && overlay_pagenos[pageno].empty()) { | |
| 1962 | + PageNo dest_page_no; | |
| 1963 | + for (QPDFPageObjectHelper dest_page: dest_pages) { | |
| 1964 | + doIfVerbose([&](Pipeline& v, std::string const& prefix) { | |
| 1965 | + v << " page " << dest_page_no.no << "\n"; | |
| 1966 | + }); | |
| 1967 | + if (underlay_pagenos[dest_page_no.idx].empty() && | |
| 1968 | + overlay_pagenos[dest_page_no.idx].empty()) { | |
| 1969 | + ++dest_page_no; | |
| 2014 | 1970 | continue; |
| 2015 | 1971 | } |
| 2016 | 1972 | // This code converts the original page, any underlays, and any overlays to form XObjects. |
| 2017 | 1973 | // Then it concatenates display of all underlays, the original page, and all overlays. Prior |
| 2018 | 1974 | // to 11.3.0, the original page contents were wrapped in q/Q, but this didn't work if the |
| 2019 | 1975 | // original page had unbalanced q/Q operators. See GitHub issue #904. |
| 2020 | - auto& dest_page = main_pages.at(page_idx); | |
| 2021 | - auto dest_page_oh = dest_page.getObjectHandle(); | |
| 2022 | 1976 | auto this_page_fo = dest_page.getFormXObjectForPage(); |
| 2023 | 1977 | // The resulting form xobject lazily reads the content from the original page, which we are |
| 2024 | 1978 | // going to replace. Therefore, we have to explicitly copy it. |
| 2025 | 1979 | auto content_data = this_page_fo.getRawStreamData(); |
| 2026 | 1980 | this_page_fo.replaceStreamData(content_data, QPDFObjectHandle(), QPDFObjectHandle()); |
| 2027 | - auto resources = | |
| 2028 | - dest_page_oh.replaceKeyAndGetNew("/Resources", "<< /XObject << >> >>"_qpdf); | |
| 2029 | - resources.getKey("/XObject").replaceKeyAndGetNew("/Fx0", this_page_fo); | |
| 1981 | + auto resources = dest_page.getObjectHandle().replaceKeyAndGetNew( | |
| 1982 | + "/Resources", Dictionary({{"/XObject", Dictionary({{"/Fx0", this_page_fo}})}})); | |
| 1983 | + | |
| 2030 | 1984 | size_t uo_idx{0}; |
| 2031 | 1985 | std::string content; |
| 2032 | 1986 | for (auto& underlay: m->underlay) { |
| 2033 | 1987 | content += doUnderOverlayForPage( |
| 2034 | - pdf, | |
| 2035 | - underlay, | |
| 2036 | - underlay_pagenos, | |
| 2037 | - page_idx, | |
| 2038 | - uo_idx, | |
| 2039 | - underlay_fo, | |
| 2040 | - upages[uo_idx], | |
| 2041 | - dest_page); | |
| 1988 | + pdf, underlay, underlay_pagenos, dest_page_no, uo_idx, underlay_fo, dest_page); | |
| 2042 | 1989 | ++uo_idx; |
| 2043 | 1990 | } |
| 2044 | 1991 | content += dest_page.placeFormXObject( |
| ... | ... | @@ -2051,17 +1998,11 @@ QPDFJob::handleUnderOverlay(QPDF& pdf) |
| 2051 | 1998 | uo_idx = 0; |
| 2052 | 1999 | for (auto& overlay: m->overlay) { |
| 2053 | 2000 | content += doUnderOverlayForPage( |
| 2054 | - pdf, | |
| 2055 | - overlay, | |
| 2056 | - overlay_pagenos, | |
| 2057 | - page_idx, | |
| 2058 | - uo_idx, | |
| 2059 | - overlay_fo, | |
| 2060 | - opages[uo_idx], | |
| 2061 | - dest_page); | |
| 2001 | + pdf, overlay, overlay_pagenos, dest_page_no, uo_idx, overlay_fo, dest_page); | |
| 2062 | 2002 | ++uo_idx; |
| 2063 | 2003 | } |
| 2064 | - dest_page_oh.replaceKey("/Contents", pdf.newStream(content)); | |
| 2004 | + dest_page.getObjectHandle().replaceKey("/Contents", pdf.newStream(content)); | |
| 2005 | + ++dest_page_no; | |
| 2065 | 2006 | } |
| 2066 | 2007 | } |
| 2067 | 2008 | |
| ... | ... | @@ -2389,108 +2330,200 @@ added_page(QPDF& pdf, QPDFPageObjectHelper page) |
| 2389 | 2330 | return added_page(pdf, page.getObjectHandle()); |
| 2390 | 2331 | } |
| 2391 | 2332 | |
| 2333 | +// Initialize all members that depend on the QPDF object. If both qpdf and qpdf_p are null do | |
| 2334 | +// nothing. | |
| 2392 | 2335 | void |
| 2393 | -QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_heap) | |
| 2336 | +QPDFJob::Input::initialize(Inputs& in, QPDF* a_qpdf) | |
| 2394 | 2337 | { |
| 2395 | - // Parse all page specifications and translate them into lists of actual pages. | |
| 2338 | + qpdf = a_qpdf ? a_qpdf : qpdf_p.get(); | |
| 2339 | + if (qpdf) { | |
| 2340 | + orig_pages = qpdf->getAllPages(); | |
| 2341 | + n_pages = static_cast<int>(orig_pages.size()); | |
| 2342 | + copied_pages = std::vector<bool>(orig_pages.size(), false); | |
| 2396 | 2343 | |
| 2397 | - // Handle "." as a shortcut for the input file | |
| 2398 | - for (auto& page_spec: m->page_specs) { | |
| 2399 | - if (page_spec.filename == ".") { | |
| 2400 | - page_spec.filename = m->infilename; | |
| 2344 | + if (in.job.m->remove_unreferenced_page_resources != QPDFJob::re_no) { | |
| 2345 | + remove_unreferenced = in.job.shouldRemoveUnreferencedResources(*qpdf); | |
| 2346 | + } | |
| 2347 | + if (qpdf->page_labels().hasPageLabels()) { | |
| 2348 | + in.any_page_labels = true; | |
| 2401 | 2349 | } |
| 2402 | - if (page_spec.range.empty()) { | |
| 2403 | - page_spec.range = "1-z"; | |
| 2350 | + } | |
| 2351 | +} | |
| 2352 | + | |
| 2353 | +void | |
| 2354 | +QPDFJob::Inputs::infile_name(std::string const& name) | |
| 2355 | +{ | |
| 2356 | + if (!infile_name_.empty()) { | |
| 2357 | + usage("input file has already been given"); | |
| 2358 | + } | |
| 2359 | + infile_name_ = name; | |
| 2360 | + | |
| 2361 | + auto& in_entry = *files.insert({name, Input()}).first; | |
| 2362 | + auto it = files.find(""); | |
| 2363 | + if (it != files.end()) { | |
| 2364 | + // We allready have selection entries for the main input file. We need to fix them to point | |
| 2365 | + // to the correct files entry. | |
| 2366 | + for (auto& selection: selections) { | |
| 2367 | + if (selection.in_entry == &*it) { | |
| 2368 | + selection.in_entry = &in_entry; | |
| 2369 | + } | |
| 2404 | 2370 | } |
| 2371 | + files.erase(it); | |
| 2372 | + } | |
| 2373 | +} | |
| 2374 | + | |
| 2375 | +void | |
| 2376 | +QPDFJob::Inputs::process(std::string const& filename, QPDFJob::Input& input) | |
| 2377 | +{ | |
| 2378 | + // Open the PDF file and store the QPDF object. Do not canonicalize the file name. Using two | |
| 2379 | + // different paths to refer to the same file is a documented workaround for duplicating a page. | |
| 2380 | + // If you are using this an example of how to do this with the API, you can just create two | |
| 2381 | + // different QPDF objects to the same underlying file with the same path to achieve the | |
| 2382 | + // same effect. | |
| 2383 | + auto password = input.password; | |
| 2384 | + if (!encryption_file.empty() && password.empty() && filename == encryption_file) { | |
| 2385 | + password = encryption_file_password; | |
| 2386 | + } | |
| 2387 | + job.doIfVerbose([&](Pipeline& v, std::string const& prefix) { | |
| 2388 | + v << prefix << ": processing " << filename << "\n"; | |
| 2389 | + }); | |
| 2390 | + if (!keep_files_open) { | |
| 2391 | + auto cis = std::make_shared<ClosedFileInputSource>(filename.data()); | |
| 2392 | + input.cfis = cis.get(); | |
| 2393 | + input.cfis->stayOpen(true); | |
| 2394 | + job.processInputSource(input.qpdf_p, cis, password.data(), true); | |
| 2395 | + } else { | |
| 2396 | + job.processInputSource( | |
| 2397 | + input.qpdf_p, | |
| 2398 | + std::make_shared<FileInputSource>(filename.data()), | |
| 2399 | + password.data(), | |
| 2400 | + true); | |
| 2405 | 2401 | } |
| 2402 | + input.initialize(*this); | |
| 2406 | 2403 | |
| 2407 | - if (!m->keep_files_open_set) { | |
| 2404 | + if (input.cfis) { | |
| 2405 | + input.cfis->stayOpen(false); | |
| 2406 | + } | |
| 2407 | +} | |
| 2408 | + | |
| 2409 | +void | |
| 2410 | +QPDFJob::Inputs::process_all() | |
| 2411 | +{ | |
| 2412 | + if (!infile_name().empty()) { | |
| 2413 | + files.erase(""); | |
| 2414 | + } | |
| 2415 | + if (!keep_files_open_set) { | |
| 2408 | 2416 | // Count the number of distinct files to determine whether we should keep files open or not. |
| 2409 | 2417 | // Rather than trying to code some portable heuristic based on OS limits, just hard-code |
| 2410 | 2418 | // this at a given number and allow users to override. |
| 2411 | - std::set<std::string> filenames; | |
| 2412 | - for (auto& page_spec: m->page_specs) { | |
| 2413 | - filenames.insert(page_spec.filename); | |
| 2414 | - } | |
| 2415 | - m->keep_files_open = (filenames.size() <= m->keep_files_open_threshold); | |
| 2416 | - QTC::TC("qpdf", "QPDFJob automatically set keep files open", m->keep_files_open ? 0 : 1); | |
| 2417 | - doIfVerbose([&](Pipeline& v, std::string const& prefix) { | |
| 2418 | - v << prefix << ": selecting --keep-open-files=" << (m->keep_files_open ? "y" : "n") | |
| 2419 | + keep_files_open = files.size() <= keep_files_open_threshold; | |
| 2420 | + QTC::TC("qpdf", "QPDFJob automatically set keep files open", keep_files_open ? 0 : 1); | |
| 2421 | + job.doIfVerbose([&](Pipeline& v, std::string const& prefix) { | |
| 2422 | + v << prefix << ": selecting --keep-open-files=" << (keep_files_open ? "y" : "n") | |
| 2419 | 2423 | << "\n"; |
| 2420 | 2424 | }); |
| 2421 | 2425 | } |
| 2422 | 2426 | |
| 2423 | - // Create a QPDF object for each file that we may take pages from. | |
| 2424 | - std::map<std::string, QPDF*> page_spec_qpdfs; | |
| 2425 | - std::map<std::string, ClosedFileInputSource*> page_spec_cfis; | |
| 2426 | - page_spec_qpdfs[m->infilename] = &pdf; | |
| 2427 | - std::vector<QPDFPageData> parsed_specs; | |
| 2428 | - std::map<unsigned long long, std::set<QPDFObjGen>> copied_pages; | |
| 2429 | - for (auto& page_spec: m->page_specs) { | |
| 2430 | - if (!page_spec_qpdfs.contains(page_spec.filename)) { | |
| 2431 | - // Open the PDF file and store the QPDF object. Throw a std::shared_ptr to the qpdf into | |
| 2432 | - // a heap so that it survives through copying to the output but gets cleaned up | |
| 2433 | - // automatically at the end. Do not canonicalize the file name. Using two different | |
| 2434 | - // paths to refer to the same file is a documented workaround for duplicating a page. If | |
| 2435 | - // you are using this an example of how to do this with the API, you can just create two | |
| 2436 | - // different QPDF objects to the same underlying file with the same path to achieve the | |
| 2437 | - // same effect. | |
| 2438 | - auto password = page_spec.password; | |
| 2439 | - if (!m->encryption_file.empty() && password.empty() && | |
| 2440 | - page_spec.filename == m->encryption_file) { | |
| 2441 | - QTC::TC("qpdf", "QPDFJob pages encryption password"); | |
| 2442 | - password = m->encryption_file_password; | |
| 2427 | + for (auto& [filename, input]: files) { | |
| 2428 | + if (!input.qpdf) { | |
| 2429 | + process(filename, input); | |
| 2430 | + } | |
| 2431 | + | |
| 2432 | + for (auto& selection: selections) { | |
| 2433 | + if (&selection.input() != &input) { | |
| 2434 | + continue; | |
| 2443 | 2435 | } |
| 2444 | - doIfVerbose([&](Pipeline& v, std::string const& prefix) { | |
| 2445 | - v << prefix << ": processing " << page_spec.filename << "\n"; | |
| 2446 | - }); | |
| 2447 | - std::shared_ptr<InputSource> is; | |
| 2448 | - ClosedFileInputSource* cis = nullptr; | |
| 2449 | - if (!m->keep_files_open) { | |
| 2450 | - QTC::TC("qpdf", "QPDFJob keep files open n"); | |
| 2451 | - cis = new ClosedFileInputSource(page_spec.filename.c_str()); | |
| 2452 | - is = std::shared_ptr<InputSource>(cis); | |
| 2453 | - cis->stayOpen(true); | |
| 2454 | - } else { | |
| 2455 | - QTC::TC("qpdf", "QPDFJob keep files open y"); | |
| 2456 | - FileInputSource* fis = new FileInputSource(page_spec.filename.c_str()); | |
| 2457 | - is = std::shared_ptr<InputSource>(fis); | |
| 2436 | + // Read original pages from the PDF, and parse the page range associated with this | |
| 2437 | + // occurrence of the file. | |
| 2438 | + if (selection.range.empty()) { | |
| 2439 | + selection.selected_pages.reserve(static_cast<size_t>(input.n_pages)); | |
| 2440 | + for (int i = 1; i <= input.n_pages; ++i) { | |
| 2441 | + selection.selected_pages.push_back(i); | |
| 2442 | + } | |
| 2443 | + continue; | |
| 2458 | 2444 | } |
| 2459 | - std::unique_ptr<QPDF> qpdf_sp; | |
| 2460 | - processInputSource(qpdf_sp, is, password.data(), true); | |
| 2461 | - page_spec_qpdfs[page_spec.filename] = qpdf_sp.get(); | |
| 2462 | - page_heap.push_back(std::move(qpdf_sp)); | |
| 2463 | - if (cis) { | |
| 2464 | - cis->stayOpen(false); | |
| 2465 | - page_spec_cfis[page_spec.filename] = cis; | |
| 2445 | + try { | |
| 2446 | + selection.selected_pages = | |
| 2447 | + QUtil::parse_numrange(selection.range.data(), selection.input().n_pages); | |
| 2448 | + } catch (std::runtime_error& e) { | |
| 2449 | + throw std::runtime_error( | |
| 2450 | + "parsing numeric range for " + selection.filename() + ": " + e.what()); | |
| 2466 | 2451 | } |
| 2467 | 2452 | } |
| 2468 | - | |
| 2469 | - // Read original pages from the PDF, and parse the page range associated with this | |
| 2470 | - // occurrence of the file. | |
| 2471 | - parsed_specs.emplace_back( | |
| 2472 | - page_spec.filename, page_spec_qpdfs[page_spec.filename], page_spec.range); | |
| 2473 | 2453 | } |
| 2454 | +} | |
| 2474 | 2455 | |
| 2475 | - std::map<unsigned long long, bool> remove_unreferenced; | |
| 2476 | - if (m->remove_unreferenced_page_resources != QPDFJob::re_no) { | |
| 2477 | - for (auto const& iter: page_spec_qpdfs) { | |
| 2478 | - std::string const& filename = iter.first; | |
| 2479 | - ClosedFileInputSource* cis = nullptr; | |
| 2480 | - if (page_spec_cfis.contains(filename)) { | |
| 2481 | - cis = page_spec_cfis[filename]; | |
| 2482 | - cis->stayOpen(true); | |
| 2483 | - } | |
| 2484 | - QPDF& other(*(iter.second)); | |
| 2485 | - auto other_uuid = other.getUniqueId(); | |
| 2486 | - if (!remove_unreferenced.contains(other_uuid)) { | |
| 2487 | - remove_unreferenced[other_uuid] = shouldRemoveUnreferencedResources(other); | |
| 2488 | - } | |
| 2489 | - if (cis) { | |
| 2490 | - cis->stayOpen(false); | |
| 2491 | - } | |
| 2456 | +bool | |
| 2457 | +QPDFJob::Inputs::clear() | |
| 2458 | +{ | |
| 2459 | + bool any_warnings = false; | |
| 2460 | + for (auto& [filename, file_spec]: files) { | |
| 2461 | + if (auto& pdf = file_spec.qpdf_p) { | |
| 2462 | + any_warnings |= pdf->anyWarnings(); | |
| 2463 | + pdf = nullptr; | |
| 2492 | 2464 | } |
| 2493 | 2465 | } |
| 2466 | + return any_warnings; | |
| 2467 | +} | |
| 2468 | + | |
| 2469 | +QPDFJob::Selection& | |
| 2470 | +QPDFJob::Inputs::new_selection(std::string const& filename) | |
| 2471 | +{ | |
| 2472 | + // Handle "." as a shortcut for the input file. Note that infile_name may not be known yet, in | |
| 2473 | + // which case we are wrongly entering an empty name. This will be corrected in the infile_name | |
| 2474 | + // setter. | |
| 2475 | + return selections.emplace_back( | |
| 2476 | + *files.insert({(filename == "." ? infile_name() : filename), Input()}).first); | |
| 2477 | +} | |
| 2478 | + | |
| 2479 | +void | |
| 2480 | +QPDFJob::Inputs::new_selection( | |
| 2481 | + std::string const& filename, std::string const& password, std::string const& range) | |
| 2482 | +{ | |
| 2483 | + auto& selection = new_selection(filename); | |
| 2484 | + selection.password(password); | |
| 2485 | + selection.range = range; | |
| 2486 | +} | |
| 2487 | + | |
| 2488 | +QPDFJob::Selection::Selection(std::pair<const std::string, QPDFJob::Input>& entry) : | |
| 2489 | + in_entry(&entry) | |
| 2490 | +{ | |
| 2491 | +} | |
| 2492 | + | |
| 2493 | +QPDFJob::Input& | |
| 2494 | +QPDFJob::Selection::input() | |
| 2495 | +{ | |
| 2496 | + return in_entry->second; | |
| 2497 | +} | |
| 2498 | + | |
| 2499 | +std::string const& | |
| 2500 | +QPDFJob::Selection::filename() | |
| 2501 | +{ | |
| 2502 | + return in_entry->first; | |
| 2503 | +} | |
| 2504 | + | |
| 2505 | +void | |
| 2506 | +QPDFJob::Selection::password(std::string password) | |
| 2507 | +{ | |
| 2508 | + auto& in = input(); | |
| 2509 | + if (!in.password.empty()) { | |
| 2510 | + usage("--password already specified for this file"); | |
| 2511 | + } | |
| 2512 | + in.password = password; | |
| 2513 | +} | |
| 2514 | + | |
| 2515 | +// Handle all page specifications. | |
| 2516 | +void | |
| 2517 | +QPDFJob::handlePageSpecs(QPDF& pdf) | |
| 2518 | +{ | |
| 2519 | + if (m->inputs.selections.empty()) { | |
| 2520 | + return; | |
| 2521 | + } | |
| 2522 | + auto& main_input = m->inputs.files[m->infile_name()]; | |
| 2523 | + main_input.initialize(m->inputs, &pdf); | |
| 2524 | + | |
| 2525 | + // Parse all section and translate them into lists of actual pages. | |
| 2526 | + m->inputs.process_all(); | |
| 2494 | 2527 | |
| 2495 | 2528 | // Clear all pages out of the primary QPDF's pages tree but leave the objects in place in the |
| 2496 | 2529 | // file so they can be re-added without changing their object numbers. This enables other things |
| ... | ... | @@ -2498,23 +2531,22 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea |
| 2498 | 2531 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
| 2499 | 2532 | v << prefix << ": removing unreferenced pages from primary input\n"; |
| 2500 | 2533 | }); |
| 2501 | - QPDFPageDocumentHelper dh(pdf); | |
| 2502 | - std::vector<QPDFPageObjectHelper> orig_pages = dh.getAllPages(); | |
| 2503 | - for (auto const& page: orig_pages) { | |
| 2504 | - dh.removePage(page); | |
| 2534 | + for (auto const& page: main_input.orig_pages) { | |
| 2535 | + pdf.removePage(page); | |
| 2505 | 2536 | } |
| 2506 | 2537 | |
| 2507 | 2538 | auto n_collate = m->collate.size(); |
| 2508 | - auto n_specs = parsed_specs.size(); | |
| 2539 | + auto n_specs = m->inputs.selections.size(); | |
| 2509 | 2540 | if (!(n_collate == 0 || n_collate == 1 || n_collate == n_specs)) { |
| 2510 | 2541 | usage( |
| 2511 | 2542 | "--pages: if --collate has more than one value, it must have one value per page " |
| 2512 | 2543 | "specification"); |
| 2513 | 2544 | } |
| 2545 | + | |
| 2546 | + std::vector<Selection> new_specs; | |
| 2514 | 2547 | if (n_collate > 0 && n_specs > 1) { |
| 2515 | 2548 | // Collate the pages by selecting one page from each spec in order. When a spec runs out of |
| 2516 | 2549 | // pages, stop selecting from it. |
| 2517 | - std::vector<QPDFPageData> new_parsed_specs; | |
| 2518 | 2550 | // Make sure we have a collate value for each spec. We have already checked that a non-empty |
| 2519 | 2551 | // collate has either one value or one value per spec. |
| 2520 | 2552 | for (auto i = n_collate; i < n_specs; ++i) { |
| ... | ... | @@ -2525,70 +2557,55 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea |
| 2525 | 2557 | while (got_pages) { |
| 2526 | 2558 | got_pages = false; |
| 2527 | 2559 | for (size_t i = 0; i < n_specs; ++i) { |
| 2528 | - QPDFPageData& page_data = parsed_specs.at(i); | |
| 2560 | + auto& page_data = m->inputs.selections.at(i); | |
| 2529 | 2561 | for (size_t j = 0; j < m->collate.at(i); ++j) { |
| 2530 | 2562 | if (cur_page.at(i) + j < page_data.selected_pages.size()) { |
| 2531 | 2563 | got_pages = true; |
| 2532 | - new_parsed_specs.emplace_back( | |
| 2564 | + new_specs.emplace_back( | |
| 2533 | 2565 | page_data, page_data.selected_pages.at(cur_page.at(i) + j)); |
| 2534 | 2566 | } |
| 2535 | 2567 | } |
| 2536 | 2568 | cur_page.at(i) += m->collate.at(i); |
| 2537 | 2569 | } |
| 2538 | 2570 | } |
| 2539 | - parsed_specs = new_parsed_specs; | |
| 2540 | 2571 | } |
| 2541 | 2572 | |
| 2542 | 2573 | // Add all the pages from all the files in the order specified. Keep track of any pages from the |
| 2543 | 2574 | // original file that we are selecting. |
| 2544 | - std::set<int> selected_from_orig; | |
| 2545 | 2575 | std::vector<QPDFObjectHandle> new_labels; |
| 2546 | - bool any_page_labels = false; | |
| 2547 | 2576 | int out_pageno = 0; |
| 2548 | 2577 | auto& this_afdh = pdf.acroform(); |
| 2549 | 2578 | std::set<QPDFObjGen> referenced_fields; |
| 2550 | - for (auto& page_data: parsed_specs) { | |
| 2551 | - ClosedFileInputSource* cis = nullptr; | |
| 2552 | - if (page_spec_cfis.contains(page_data.filename)) { | |
| 2553 | - cis = page_spec_cfis[page_data.filename]; | |
| 2554 | - cis->stayOpen(true); | |
| 2555 | - } | |
| 2556 | - auto& pldh = page_data.qpdf->page_labels(); | |
| 2557 | - auto& other_afdh = page_data.qpdf->acroform(); | |
| 2558 | - if (pldh.hasPageLabels()) { | |
| 2559 | - any_page_labels = true; | |
| 2579 | + for (auto& selection: new_specs.empty() ? m->inputs.selections : new_specs) { | |
| 2580 | + auto& input = selection.input(); | |
| 2581 | + if (input.cfis) { | |
| 2582 | + input.cfis->stayOpen(true); | |
| 2560 | 2583 | } |
| 2584 | + auto* pldh = m->inputs.any_page_labels ? &input.qpdf->page_labels() : nullptr; | |
| 2585 | + auto& other_afdh = input.qpdf->acroform(); | |
| 2561 | 2586 | doIfVerbose([&](Pipeline& v, std::string const& prefix) { |
| 2562 | - v << prefix << ": adding pages from " << page_data.filename << "\n"; | |
| 2587 | + v << prefix << ": adding pages from " << selection.filename() << "\n"; | |
| 2563 | 2588 | }); |
| 2564 | - for (auto pageno_iter: page_data.selected_pages) { | |
| 2589 | + const bool this_file = input.qpdf == &pdf; | |
| 2590 | + for (PageNo page: selection.selected_pages) { | |
| 2591 | + bool first_copy_from_orig = this_file && !main_input.copied_pages[page.idx]; | |
| 2592 | + | |
| 2565 | 2593 | // Pages are specified from 1 but numbered from 0 in the vector |
| 2566 | - int pageno = pageno_iter - 1; | |
| 2567 | - pldh.getLabelsForPageRange(pageno, pageno, out_pageno++, new_labels); | |
| 2568 | - QPDFPageObjectHelper to_copy = page_data.orig_pages.at(QIntC::to_size(pageno)); | |
| 2569 | - QPDFObjGen to_copy_og = to_copy.getObjectHandle().getObjGen(); | |
| 2570 | - unsigned long long from_uuid = page_data.qpdf->getUniqueId(); | |
| 2571 | - if (copied_pages[from_uuid].contains(to_copy_og)) { | |
| 2572 | - QTC::TC( | |
| 2573 | - "qpdf", | |
| 2574 | - "QPDFJob copy same page more than once", | |
| 2575 | - (page_data.qpdf == &pdf) ? 0 : 1); | |
| 2594 | + int pageno = page.no - 1; | |
| 2595 | + if (pldh) { | |
| 2596 | + pldh->getLabelsForPageRange(pageno, pageno, out_pageno++, new_labels); | |
| 2597 | + } | |
| 2598 | + QPDFPageObjectHelper to_copy = input.orig_pages.at(page.idx); | |
| 2599 | + if (input.copied_pages[page.idx]) { | |
| 2600 | + QTC::TC("qpdf", "QPDFJob copy same page more than once", this_file ? 0 : 1); | |
| 2576 | 2601 | to_copy = to_copy.shallowCopyPage(); |
| 2577 | 2602 | } else { |
| 2578 | - copied_pages[from_uuid].insert(to_copy_og); | |
| 2579 | - if (remove_unreferenced[from_uuid]) { | |
| 2603 | + input.copied_pages[page.idx] = true; | |
| 2604 | + if (input.remove_unreferenced) { | |
| 2580 | 2605 | to_copy.removeUnreferencedResources(); |
| 2581 | 2606 | } |
| 2582 | 2607 | } |
| 2583 | - dh.addPage(to_copy, false); | |
| 2584 | - bool first_copy_from_orig = false; | |
| 2585 | - bool this_file = (page_data.qpdf == &pdf); | |
| 2586 | - if (this_file) { | |
| 2587 | - // This is a page from the original file. Keep track of the fact that we are using | |
| 2588 | - // it. | |
| 2589 | - first_copy_from_orig = (!selected_from_orig.contains(pageno)); | |
| 2590 | - selected_from_orig.insert(pageno); | |
| 2591 | - } | |
| 2608 | + pdf.addPage(to_copy, false); | |
| 2592 | 2609 | auto new_page = added_page(pdf, to_copy); |
| 2593 | 2610 | // Try to avoid gratuitously renaming fields. In the case of where we're just extracting |
| 2594 | 2611 | // a bunch of pages from the original file and not copying any page more than once, |
| ... | ... | @@ -2616,48 +2633,46 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea |
| 2616 | 2633 | } |
| 2617 | 2634 | } |
| 2618 | 2635 | } |
| 2619 | - if (cis) { | |
| 2620 | - cis->stayOpen(false); | |
| 2636 | + if (input.cfis) { | |
| 2637 | + input.cfis->stayOpen(false); | |
| 2621 | 2638 | } |
| 2622 | 2639 | } |
| 2623 | - if (any_page_labels) { | |
| 2624 | - QPDFObjectHandle page_labels = QPDFObjectHandle::newDictionary(); | |
| 2625 | - page_labels.replaceKey("/Nums", QPDFObjectHandle::newArray(new_labels)); | |
| 2626 | - pdf.getRoot().replaceKey("/PageLabels", page_labels); | |
| 2640 | + if (m->inputs.any_page_labels) { | |
| 2641 | + pdf.getRoot().replaceKey("/PageLabels", Dictionary({{"/Nums", Array(new_labels)}})); | |
| 2627 | 2642 | } |
| 2628 | 2643 | |
| 2629 | 2644 | // Delete page objects for unused page in primary. This prevents those objects from being |
| 2630 | 2645 | // preserved by being referred to from other places, such as the outlines dictionary. Also make |
| 2631 | 2646 | // sure we keep form fields from pages we preserved. |
| 2632 | - for (size_t pageno = 0; pageno < orig_pages.size(); ++pageno) { | |
| 2633 | - auto page = orig_pages.at(pageno); | |
| 2634 | - if (selected_from_orig.contains(QIntC::to_int(pageno))) { | |
| 2647 | + size_t page_idx = 0; | |
| 2648 | + for (auto const& page: main_input.orig_pages) { | |
| 2649 | + if (main_input.copied_pages[page_idx]) { | |
| 2635 | 2650 | for (auto field: this_afdh.getFormFieldsForPage(page)) { |
| 2636 | - QTC::TC("qpdf", "QPDFJob pages keeping field from original"); | |
| 2637 | - referenced_fields.insert(field.getObjectHandle().getObjGen()); | |
| 2651 | + referenced_fields.insert(field); | |
| 2638 | 2652 | } |
| 2639 | 2653 | } else { |
| 2640 | - pdf.replaceObject(page.getObjectHandle().getObjGen(), QPDFObjectHandle::newNull()); | |
| 2654 | + pdf.replaceObject(page, QPDFObjectHandle::newNull()); | |
| 2641 | 2655 | } |
| 2656 | + ++page_idx; | |
| 2642 | 2657 | } |
| 2643 | 2658 | // Remove unreferenced form fields |
| 2644 | 2659 | if (this_afdh.hasAcroForm()) { |
| 2645 | - auto acroform = pdf.getRoot().getKey("/AcroForm"); | |
| 2646 | - auto fields = acroform.getKey("/Fields"); | |
| 2647 | - if (fields.isArray()) { | |
| 2648 | - auto new_fields = QPDFObjectHandle::newArray(); | |
| 2649 | - if (fields.isIndirect()) { | |
| 2650 | - new_fields = pdf.makeIndirectObject(new_fields); | |
| 2651 | - } | |
| 2652 | - for (auto const& field: fields.aitems()) { | |
| 2660 | + auto acroform = pdf.getRoot()["/AcroForm"]; | |
| 2661 | + if (Array fields = acroform["/Fields"]) { | |
| 2662 | + std::vector<QPDFObjectHandle> new_fields; | |
| 2663 | + new_fields.reserve(referenced_fields.size()); | |
| 2664 | + for (auto const& field: fields) { | |
| 2653 | 2665 | if (referenced_fields.contains(field.getObjGen())) { |
| 2654 | - new_fields.appendItem(field); | |
| 2666 | + new_fields.emplace_back(field); | |
| 2655 | 2667 | } |
| 2656 | 2668 | } |
| 2657 | 2669 | if (new_fields.empty()) { |
| 2658 | - pdf.getRoot().removeKey("/AcroForm"); | |
| 2670 | + pdf.getRoot().erase("/AcroForm"); | |
| 2659 | 2671 | } else { |
| 2660 | - acroform.replaceKey("/Fields", new_fields); | |
| 2672 | + acroform.replaceKey( | |
| 2673 | + "/Fields", | |
| 2674 | + fields.indirect() ? pdf.makeIndirectObject(Array(new_fields)) | |
| 2675 | + : QPDFObjectHandle(Array(new_fields))); | |
| 2661 | 2676 | } |
| 2662 | 2677 | } |
| 2663 | 2678 | } |
| ... | ... | @@ -2924,8 +2939,8 @@ QPDFJob::setWriterOptions(QPDFWriter& w) |
| 2924 | 2939 | std::unique_ptr<QPDF> encryption_pdf; |
| 2925 | 2940 | processFile( |
| 2926 | 2941 | encryption_pdf, |
| 2927 | - m->encryption_file.data(), | |
| 2928 | - m->encryption_file_password.data(), | |
| 2942 | + m->inputs.encryption_file.data(), | |
| 2943 | + m->inputs.encryption_file_password.data(), | |
| 2929 | 2944 | false, |
| 2930 | 2945 | false); |
| 2931 | 2946 | w.copyEncryptionParameters(*encryption_pdf); |
| ... | ... | @@ -3045,7 +3060,7 @@ QPDFJob::doSplitPages(QPDF& pdf) |
| 3045 | 3060 | page_range += "-" + QUtil::uint_to_string(last, QIntC::to_int(pageno_len)); |
| 3046 | 3061 | } |
| 3047 | 3062 | std::string outfile = before + page_range + after; |
| 3048 | - if (QUtil::same_file(m->infilename.data(), outfile.data())) { | |
| 3063 | + if (QUtil::same_file(m->infile_nm(), outfile.data())) { | |
| 3049 | 3064 | throw std::runtime_error("split pages would overwrite input file with " + outfile); |
| 3050 | 3065 | } |
| 3051 | 3066 | QPDFWriter w(outpdf, outfile.c_str()); |
| ... | ... | @@ -3064,7 +3079,7 @@ QPDFJob::writeOutfile(QPDF& pdf) |
| 3064 | 3079 | if (m->replace_input) { |
| 3065 | 3080 | // Append but don't prepend to the path to generate a temporary name. This saves us from |
| 3066 | 3081 | // having to split the path by directory and non-directory. |
| 3067 | - temp_out = m->infilename + ".~qpdf-temp#"; | |
| 3082 | + temp_out = m->infile_name() + ".~qpdf-temp#"; | |
| 3068 | 3083 | // m->outfilename will be restored to 0 before temp_out goes out of scope. |
| 3069 | 3084 | m->outfilename = temp_out; |
| 3070 | 3085 | } else if (m->outfilename == "-") { |
| ... | ... | @@ -3098,13 +3113,13 @@ QPDFJob::writeOutfile(QPDF& pdf) |
| 3098 | 3113 | if (m->replace_input) { |
| 3099 | 3114 | // We must close the input before we can rename files |
| 3100 | 3115 | pdf.closeInputSource(); |
| 3101 | - std::string backup = m->infilename + ".~qpdf-orig"; | |
| 3116 | + std::string backup = m->infile_name() + ".~qpdf-orig"; | |
| 3102 | 3117 | bool warnings = pdf.anyWarnings(); |
| 3103 | 3118 | if (!warnings) { |
| 3104 | 3119 | backup.append(1, '#'); |
| 3105 | 3120 | } |
| 3106 | - QUtil::rename_file(m->infilename.data(), backup.data()); | |
| 3107 | - QUtil::rename_file(temp_out.data(), m->infilename.data()); | |
| 3121 | + QUtil::rename_file(m->infile_nm(), backup.data()); | |
| 3122 | + QUtil::rename_file(temp_out.data(), m->infile_nm()); | |
| 3108 | 3123 | if (warnings) { |
| 3109 | 3124 | *m->log->getError() << m->message_prefix |
| 3110 | 3125 | << ": there are warnings; original file kept in " << backup << "\n"; | ... | ... |
libqpdf/QPDFJob_config.cc
| 1 | -#include <qpdf/QPDFJob.hh> | |
| 1 | +#include <qpdf/QPDFJob_private.hh> | |
| 2 | 2 | |
| 3 | 3 | #include <regex> |
| 4 | 4 | |
| ... | ... | @@ -15,18 +15,14 @@ QPDFJob::Config::checkConfiguration() |
| 15 | 15 | QPDFJob::Config* |
| 16 | 16 | QPDFJob::Config::inputFile(std::string const& filename) |
| 17 | 17 | { |
| 18 | - if (o.m->infilename.empty()) { | |
| 19 | - o.m->infilename = filename; | |
| 20 | - } else { | |
| 21 | - usage("input file has already been given"); | |
| 22 | - } | |
| 18 | + o.m->inputs.infile_name(filename); | |
| 23 | 19 | return this; |
| 24 | 20 | } |
| 25 | 21 | |
| 26 | 22 | QPDFJob::Config* |
| 27 | 23 | QPDFJob::Config::emptyInput() |
| 28 | 24 | { |
| 29 | - if (o.m->infilename.empty()) { | |
| 25 | + if (o.m->infile_name().empty()) { | |
| 30 | 26 | // Various places in QPDFJob.cc used to know that the empty string for infile means empty. |
| 31 | 27 | // This approach meant that passing "" as the argument to inputFile in job JSON, or |
| 32 | 28 | // equivalently using "" as a positional command-line argument would be the same as |
| ... | ... | @@ -152,7 +148,7 @@ QPDFJob::Config::copyEncryption(std::string const& parameter) |
| 152 | 148 | if (o.m->deterministic_id) { |
| 153 | 149 | usage("the deterministic-id option is incompatible with encrypted output files"); |
| 154 | 150 | } |
| 155 | - o.m->encryption_file = parameter; | |
| 151 | + o.m->inputs.encryption_file = parameter; | |
| 156 | 152 | o.m->copy_encryption = true; |
| 157 | 153 | o.m->encrypt = false; |
| 158 | 154 | o.m->decrypt = false; |
| ... | ... | @@ -181,7 +177,7 @@ QPDFJob::Config::deterministicId() |
| 181 | 177 | QPDFJob::Config* |
| 182 | 178 | QPDFJob::Config::encryptionFilePassword(std::string const& parameter) |
| 183 | 179 | { |
| 184 | - o.m->encryption_file_password = parameter; | |
| 180 | + o.m->inputs.encryption_file_password = parameter; | |
| 185 | 181 | return this; |
| 186 | 182 | } |
| 187 | 183 | |
| ... | ... | @@ -354,15 +350,15 @@ QPDFJob::Config::testJsonSchema() |
| 354 | 350 | QPDFJob::Config* |
| 355 | 351 | QPDFJob::Config::keepFilesOpen(std::string const& parameter) |
| 356 | 352 | { |
| 357 | - o.m->keep_files_open_set = true; | |
| 358 | - o.m->keep_files_open = (parameter == "y"); | |
| 353 | + o.m->inputs.keep_files_open_set = true; | |
| 354 | + o.m->inputs.keep_files_open = (parameter == "y"); | |
| 359 | 355 | return this; |
| 360 | 356 | } |
| 361 | 357 | |
| 362 | 358 | QPDFJob::Config* |
| 363 | 359 | QPDFJob::Config::keepFilesOpenThreshold(std::string const& parameter) |
| 364 | 360 | { |
| 365 | - o.m->keep_files_open_threshold = QUtil::string_to_uint(parameter.c_str()); | |
| 361 | + o.m->inputs.keep_files_open_threshold = QUtil::string_to_uint(parameter.c_str()); | |
| 366 | 362 | return this; |
| 367 | 363 | } |
| 368 | 364 | |
| ... | ... | @@ -978,7 +974,7 @@ QPDFJob::PagesConfig::PagesConfig(Config* c) : |
| 978 | 974 | std::shared_ptr<QPDFJob::PagesConfig> |
| 979 | 975 | QPDFJob::Config::pages() |
| 980 | 976 | { |
| 981 | - if (!o.m->page_specs.empty()) { | |
| 977 | + if (!o.m->inputs.selections.empty()) { | |
| 982 | 978 | usage("--pages may only be specified one time"); |
| 983 | 979 | } |
| 984 | 980 | return std::shared_ptr<PagesConfig>(new PagesConfig(this)); |
| ... | ... | @@ -987,7 +983,7 @@ QPDFJob::Config::pages() |
| 987 | 983 | QPDFJob::Config* |
| 988 | 984 | QPDFJob::PagesConfig::endPages() |
| 989 | 985 | { |
| 990 | - auto n_specs = config->o.m->page_specs.size(); | |
| 986 | + auto n_specs = config->o.m->inputs.selections.size(); | |
| 991 | 987 | if (n_specs == 0) { |
| 992 | 988 | usage("--pages: no page specifications given"); |
| 993 | 989 | } |
| ... | ... | @@ -998,27 +994,25 @@ QPDFJob::PagesConfig* |
| 998 | 994 | QPDFJob::PagesConfig::pageSpec( |
| 999 | 995 | std::string const& filename, std::string const& range, char const* password) |
| 1000 | 996 | { |
| 1001 | - config->o.m->page_specs.emplace_back(filename, password, range); | |
| 997 | + config->o.m->inputs.new_selection(filename, {password ? password : ""}, range); | |
| 1002 | 998 | return this; |
| 1003 | 999 | } |
| 1004 | 1000 | |
| 1005 | 1001 | QPDFJob::PagesConfig* |
| 1006 | 1002 | QPDFJob::PagesConfig::file(std::string const& arg) |
| 1007 | 1003 | { |
| 1008 | - config->o.m->page_specs.emplace_back(arg, "", ""); | |
| 1004 | + (void)config->o.m->inputs.new_selection(arg); | |
| 1009 | 1005 | return this; |
| 1010 | 1006 | } |
| 1011 | 1007 | |
| 1012 | 1008 | QPDFJob::PagesConfig* |
| 1013 | 1009 | QPDFJob::PagesConfig::range(std::string const& arg) |
| 1014 | 1010 | { |
| 1015 | - if (config->o.m->page_specs.empty()) { | |
| 1016 | - QTC::TC("qpdf", "QPDFJob misplaced page range"); | |
| 1011 | + if (config->o.m->inputs.selections.empty()) { | |
| 1017 | 1012 | usage("in --range must follow a file name"); |
| 1018 | 1013 | } |
| 1019 | - auto& last = config->o.m->page_specs.back(); | |
| 1014 | + auto& last = config->o.m->inputs.selections.back(); | |
| 1020 | 1015 | if (!last.range.empty()) { |
| 1021 | - QTC::TC("qpdf", "QPDFJob duplicated range"); | |
| 1022 | 1016 | usage("--range already specified for this file"); |
| 1023 | 1017 | } |
| 1024 | 1018 | last.range = arg; |
| ... | ... | @@ -1028,16 +1022,10 @@ QPDFJob::PagesConfig::range(std::string const& arg) |
| 1028 | 1022 | QPDFJob::PagesConfig* |
| 1029 | 1023 | QPDFJob::PagesConfig::password(std::string const& arg) |
| 1030 | 1024 | { |
| 1031 | - if (config->o.m->page_specs.empty()) { | |
| 1032 | - QTC::TC("qpdf", "QPDFJob misplaced pages password"); | |
| 1025 | + if (config->o.m->inputs.selections.empty()) { | |
| 1033 | 1026 | usage("in --pages, --password must follow a file name"); |
| 1034 | 1027 | } |
| 1035 | - auto& last = config->o.m->page_specs.back(); | |
| 1036 | - if (!last.password.empty()) { | |
| 1037 | - QTC::TC("qpdf", "QPDFJob duplicated pages password"); | |
| 1038 | - usage("--password already specified for this file"); | |
| 1039 | - } | |
| 1040 | - last.password = arg; | |
| 1028 | + config->o.m->inputs.selections.back().password(arg); | |
| 1041 | 1029 | return this; |
| 1042 | 1030 | } |
| 1043 | 1031 | ... | ... |
libqpdf/QPDFJob_json.cc
libqpdf/qpdf/QPDFJob_private.hh
0 โ 100644
| 1 | +#ifndef QPDFJOB_PRIVATE_HH | |
| 2 | +#define QPDFJOB_PRIVATE_HH | |
| 3 | + | |
| 4 | +#include <qpdf/QPDFJob.hh> | |
| 5 | + | |
| 6 | +#include <qpdf/ClosedFileInputSource.hh> | |
| 7 | +#include <qpdf/QPDFLogger.hh> | |
| 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() = delete; | |
| 14 | + | |
| 15 | + Selection(std::pair<const std::string, Input>& entry); | |
| 16 | + | |
| 17 | + Selection(Selection const& other, int page) : | |
| 18 | + in_entry(other.in_entry), | |
| 19 | + selected_pages({page}) | |
| 20 | + { | |
| 21 | + } | |
| 22 | + | |
| 23 | + QPDFJob::Input& input(); | |
| 24 | + std::string const& filename(); | |
| 25 | + void password(std::string password); | |
| 26 | + | |
| 27 | + std::pair<const std::string, QPDFJob::Input>* in_entry{nullptr}; | |
| 28 | + std::string range; // An empty range means all pages. | |
| 29 | + std::vector<int> selected_pages; | |
| 30 | +}; | |
| 31 | + | |
| 32 | +// A single input PDF. | |
| 33 | +// | |
| 34 | +// N.B. A single input PDF may be represented by multiple Input instances using variations of the | |
| 35 | +// filename. This is a documented work-around. | |
| 36 | +struct QPDFJob::Input | |
| 37 | +{ | |
| 38 | + void initialize(Inputs& in, QPDF* qpdf = nullptr); | |
| 39 | + | |
| 40 | + std::string password; | |
| 41 | + std::unique_ptr<QPDF> qpdf_p; | |
| 42 | + QPDF* qpdf; | |
| 43 | + ClosedFileInputSource* cfis{}; | |
| 44 | + std::vector<QPDFObjectHandle> orig_pages; | |
| 45 | + int n_pages; | |
| 46 | + std::vector<bool> copied_pages; | |
| 47 | + bool remove_unreferenced{false}; | |
| 48 | +}; | |
| 49 | + | |
| 50 | +// All PDF input files for a job. | |
| 51 | +struct QPDFJob::Inputs | |
| 52 | +{ | |
| 53 | + friend struct Input; | |
| 54 | + // These default values are duplicated in help and docs. | |
| 55 | + static int constexpr DEFAULT_KEEP_FILES_OPEN_THRESHOLD = 200; | |
| 56 | + | |
| 57 | + Inputs(QPDFJob& job) : | |
| 58 | + job(job) | |
| 59 | + { | |
| 60 | + } | |
| 61 | + void process(std::string const& filename, QPDFJob::Input& file_spec); | |
| 62 | + void process_all(); | |
| 63 | + | |
| 64 | + // Destroy all owned QPDF objects. Return false if any of the QPDF objects recorded warnings. | |
| 65 | + bool clear(); | |
| 66 | + | |
| 67 | + Selection& new_selection(std::string const& filename); | |
| 68 | + | |
| 69 | + void new_selection( | |
| 70 | + std::string const& filename, std::string const& password, std::string const& range); | |
| 71 | + | |
| 72 | + std::string const& | |
| 73 | + infile_name() const | |
| 74 | + { | |
| 75 | + return infile_name_; | |
| 76 | + } | |
| 77 | + | |
| 78 | + void infile_name(std::string const& name); | |
| 79 | + | |
| 80 | + std::string infile_name_; | |
| 81 | + std::string encryption_file; | |
| 82 | + std::string encryption_file_password; | |
| 83 | + bool keep_files_open{true}; | |
| 84 | + bool keep_files_open_set{false}; | |
| 85 | + size_t keep_files_open_threshold{DEFAULT_KEEP_FILES_OPEN_THRESHOLD}; | |
| 86 | + | |
| 87 | + std::map<std::string, Input> files; | |
| 88 | + std::vector<Selection> selections; | |
| 89 | + | |
| 90 | + bool any_page_labels{false}; | |
| 91 | + | |
| 92 | + private: | |
| 93 | + QPDFJob& job; | |
| 94 | +}; | |
| 95 | + | |
| 96 | +struct QPDFJob::RotationSpec | |
| 97 | +{ | |
| 98 | + RotationSpec(int angle = 0, bool relative = false) : | |
| 99 | + angle(angle), | |
| 100 | + relative(relative) | |
| 101 | + { | |
| 102 | + } | |
| 103 | + | |
| 104 | + int angle; | |
| 105 | + bool relative; | |
| 106 | +}; | |
| 107 | + | |
| 108 | +struct QPDFJob::UnderOverlay | |
| 109 | +{ | |
| 110 | + UnderOverlay(char const* which) : | |
| 111 | + which(which), | |
| 112 | + to_nr("1-z"), | |
| 113 | + from_nr("1-z"), | |
| 114 | + repeat_nr("") | |
| 115 | + { | |
| 116 | + } | |
| 117 | + | |
| 118 | + std::string which; | |
| 119 | + std::string filename; | |
| 120 | + std::string password; | |
| 121 | + std::string to_nr; | |
| 122 | + std::string from_nr; | |
| 123 | + std::string repeat_nr; | |
| 124 | + std::unique_ptr<QPDF> pdf; | |
| 125 | + std::vector<int> to_pagenos; | |
| 126 | + std::vector<int> from_pagenos; | |
| 127 | + std::vector<int> repeat_pagenos; | |
| 128 | +}; | |
| 129 | + | |
| 130 | +struct QPDFJob::PageLabelSpec | |
| 131 | +{ | |
| 132 | + PageLabelSpec( | |
| 133 | + int first_page, qpdf_page_label_e label_type, int start_num, std::string_view prefix) : | |
| 134 | + first_page(first_page), | |
| 135 | + label_type(label_type), | |
| 136 | + start_num(start_num), | |
| 137 | + prefix(prefix) | |
| 138 | + { | |
| 139 | + } | |
| 140 | + int first_page; | |
| 141 | + qpdf_page_label_e label_type; | |
| 142 | + int start_num{1}; | |
| 143 | + std::string prefix; | |
| 144 | +}; | |
| 145 | + | |
| 146 | +class QPDFJob::Members | |
| 147 | +{ | |
| 148 | + friend class QPDFJob; | |
| 149 | + | |
| 150 | + public: | |
| 151 | + Members(QPDFJob& job) : | |
| 152 | + log(QPDFLogger::defaultLogger()), | |
| 153 | + inputs(job) | |
| 154 | + { | |
| 155 | + } | |
| 156 | + Members(Members const&) = delete; | |
| 157 | + ~Members() = default; | |
| 158 | + | |
| 159 | + inline const char* infile_nm() const; | |
| 160 | + | |
| 161 | + inline std::string const& infile_name() const; | |
| 162 | + | |
| 163 | + private: | |
| 164 | + // These default values are duplicated in help and docs. | |
| 165 | + static int constexpr DEFAULT_OI_MIN_WIDTH = 128; | |
| 166 | + static int constexpr DEFAULT_OI_MIN_HEIGHT = 128; | |
| 167 | + static int constexpr DEFAULT_OI_MIN_AREA = 16384; | |
| 168 | + static int constexpr DEFAULT_II_MIN_BYTES = 1024; | |
| 169 | + | |
| 170 | + std::shared_ptr<QPDFLogger> log; | |
| 171 | + std::string message_prefix{"qpdf"}; | |
| 172 | + bool warnings{false}; | |
| 173 | + unsigned long encryption_status{0}; | |
| 174 | + bool verbose{false}; | |
| 175 | + std::string password; | |
| 176 | + bool linearize{false}; | |
| 177 | + bool decrypt{false}; | |
| 178 | + bool remove_restrictions{false}; | |
| 179 | + int split_pages{0}; | |
| 180 | + bool progress{false}; | |
| 181 | + std::function<void(int)> progress_handler{nullptr}; | |
| 182 | + bool suppress_warnings{false}; | |
| 183 | + bool warnings_exit_zero{false}; | |
| 184 | + bool copy_encryption{false}; | |
| 185 | + bool encrypt{false}; | |
| 186 | + bool password_is_hex_key{false}; | |
| 187 | + bool suppress_password_recovery{false}; | |
| 188 | + password_mode_e password_mode{pm_auto}; | |
| 189 | + bool allow_insecure{false}; | |
| 190 | + bool allow_weak_crypto{false}; | |
| 191 | + std::string user_password; | |
| 192 | + std::string owner_password; | |
| 193 | + int keylen{0}; | |
| 194 | + bool r2_print{true}; | |
| 195 | + bool r2_modify{true}; | |
| 196 | + bool r2_extract{true}; | |
| 197 | + bool r2_annotate{true}; | |
| 198 | + bool r3_accessibility{true}; | |
| 199 | + bool r3_extract{true}; | |
| 200 | + bool r3_assemble{true}; | |
| 201 | + bool r3_annotate_and_form{true}; | |
| 202 | + bool r3_form_filling{true}; | |
| 203 | + bool r3_modify_other{true}; | |
| 204 | + qpdf_r3_print_e r3_print{qpdf_r3p_full}; | |
| 205 | + bool force_V4{false}; | |
| 206 | + bool force_R5{false}; | |
| 207 | + bool cleartext_metadata{false}; | |
| 208 | + bool use_aes{false}; | |
| 209 | + bool stream_data_set{false}; | |
| 210 | + qpdf_stream_data_e stream_data_mode{qpdf_s_compress}; | |
| 211 | + bool compress_streams{true}; | |
| 212 | + bool compress_streams_set{false}; | |
| 213 | + bool recompress_flate{false}; | |
| 214 | + bool recompress_flate_set{false}; | |
| 215 | + int compression_level{-1}; | |
| 216 | + int jpeg_quality{-1}; | |
| 217 | + qpdf_stream_decode_level_e decode_level{qpdf_dl_generalized}; | |
| 218 | + bool decode_level_set{false}; | |
| 219 | + bool normalize_set{false}; | |
| 220 | + bool normalize{false}; | |
| 221 | + bool suppress_recovery{false}; | |
| 222 | + bool object_stream_set{false}; | |
| 223 | + qpdf_object_stream_e object_stream_mode{qpdf_o_preserve}; | |
| 224 | + bool ignore_xref_streams{false}; | |
| 225 | + bool qdf_mode{false}; | |
| 226 | + bool preserve_unreferenced_objects{false}; | |
| 227 | + remove_unref_e remove_unreferenced_page_resources{re_auto}; | |
| 228 | + bool newline_before_endstream{false}; | |
| 229 | + std::string linearize_pass1; | |
| 230 | + bool coalesce_contents{false}; | |
| 231 | + bool flatten_annotations{false}; | |
| 232 | + int flatten_annotations_required{0}; | |
| 233 | + int flatten_annotations_forbidden{an_invisible | an_hidden}; | |
| 234 | + bool generate_appearances{false}; | |
| 235 | + PDFVersion max_input_version; | |
| 236 | + std::string min_version; | |
| 237 | + std::string force_version; | |
| 238 | + bool show_npages{false}; | |
| 239 | + bool deterministic_id{false}; | |
| 240 | + bool static_id{false}; | |
| 241 | + bool static_aes_iv{false}; | |
| 242 | + bool suppress_original_object_id{false}; | |
| 243 | + bool show_encryption{false}; | |
| 244 | + bool show_encryption_key{false}; | |
| 245 | + bool check_linearization{false}; | |
| 246 | + bool show_linearization{false}; | |
| 247 | + bool show_xref{false}; | |
| 248 | + bool show_trailer{false}; | |
| 249 | + int show_obj{0}; | |
| 250 | + int show_gen{0}; | |
| 251 | + bool show_raw_stream_data{false}; | |
| 252 | + bool show_filtered_stream_data{false}; | |
| 253 | + bool show_pages{false}; | |
| 254 | + bool show_page_images{false}; | |
| 255 | + std::vector<size_t> collate; | |
| 256 | + bool flatten_rotation{false}; | |
| 257 | + bool list_attachments{false}; | |
| 258 | + std::string attachment_to_show; | |
| 259 | + std::list<std::string> attachments_to_remove; | |
| 260 | + std::list<AddAttachment> attachments_to_add; | |
| 261 | + std::list<CopyAttachmentFrom> attachments_to_copy; | |
| 262 | + int json_version{0}; | |
| 263 | + std::set<std::string> json_keys; | |
| 264 | + std::set<std::string> json_objects; | |
| 265 | + qpdf_json_stream_data_e json_stream_data{qpdf_sj_none}; | |
| 266 | + bool json_stream_data_set{false}; | |
| 267 | + std::string json_stream_prefix; | |
| 268 | + bool test_json_schema{false}; | |
| 269 | + bool check{false}; | |
| 270 | + bool optimize_images{false}; | |
| 271 | + bool externalize_inline_images{false}; | |
| 272 | + bool keep_inline_images{false}; | |
| 273 | + bool remove_info{false}; | |
| 274 | + bool remove_metadata{false}; | |
| 275 | + bool remove_page_labels{false}; | |
| 276 | + bool remove_structure{false}; | |
| 277 | + size_t oi_min_width{DEFAULT_OI_MIN_WIDTH}; | |
| 278 | + size_t oi_min_height{DEFAULT_OI_MIN_HEIGHT}; | |
| 279 | + size_t oi_min_area{DEFAULT_OI_MIN_AREA}; | |
| 280 | + size_t ii_min_bytes{DEFAULT_II_MIN_BYTES}; | |
| 281 | + std::vector<UnderOverlay> underlay; | |
| 282 | + std::vector<UnderOverlay> overlay; | |
| 283 | + UnderOverlay* under_overlay{nullptr}; | |
| 284 | + Inputs inputs; | |
| 285 | + std::map<std::string, RotationSpec> rotations; | |
| 286 | + bool require_outfile{true}; | |
| 287 | + bool replace_input{false}; | |
| 288 | + bool check_is_encrypted{false}; | |
| 289 | + bool check_requires_password{false}; | |
| 290 | + bool empty_input{false}; | |
| 291 | + std::string outfilename; | |
| 292 | + bool json_input{false}; | |
| 293 | + bool json_output{false}; | |
| 294 | + std::string update_from_json; | |
| 295 | + bool report_mem_usage{false}; | |
| 296 | + std::vector<PageLabelSpec> page_label_specs; | |
| 297 | +}; | |
| 298 | + | |
| 299 | +inline const char* | |
| 300 | +QPDFJob::Members::infile_nm() const | |
| 301 | +{ | |
| 302 | + return inputs.infile_name().data(); | |
| 303 | +} | |
| 304 | + | |
| 305 | +inline std::string const& | |
| 306 | +QPDFJob::Members::infile_name() const | |
| 307 | +{ | |
| 308 | + return inputs.infile_name(); | |
| 309 | +} | |
| 310 | + | |
| 311 | +#endif // QPDFJOB_PRIVATE_HH | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -188,7 +188,6 @@ QPDF insert foreign page 0 |
| 188 | 188 | QPDFWriter copy use_aes 1 |
| 189 | 189 | QPDFParser indirect without context 0 |
| 190 | 190 | QPDFObjectHandle trailing data in parse 0 |
| 191 | -QPDFJob pages encryption password 0 | |
| 192 | 191 | QPDFTokenizer EOF reading token 0 |
| 193 | 192 | QPDFTokenizer EOF reading appendable token 0 |
| 194 | 193 | QPDFWriter extra header text no newline 0 |
| ... | ... | @@ -209,7 +208,6 @@ QPDF not caching overridden objstm object 0 |
| 209 | 208 | QPDF_optimization indirect outlines 0 |
| 210 | 209 | QPDF xref space 2 |
| 211 | 210 | QPDFJob pages range omitted in middle 0 |
| 212 | -QPDFJob npages 0 | |
| 213 | 211 | QPDFWriter standard deterministic ID 1 |
| 214 | 212 | QPDFWriter linearized deterministic ID 1 |
| 215 | 213 | qpdf-c called qpdf_set_deterministic_ID 0 |
| ... | ... | @@ -222,7 +220,6 @@ QPDFParser found fake 1 |
| 222 | 220 | QPDFParser no val for last key 0 |
| 223 | 221 | QPDF resolve failure to null 0 |
| 224 | 222 | QPDFObjectHandle errors in parsecontent 0 |
| 225 | -QPDFJob same file error 0 | |
| 226 | 223 | QPDFJob split-pages %d 0 |
| 227 | 224 | QPDFJob split-pages .pdf 0 |
| 228 | 225 | QPDFJob split-pages other 0 |
| ... | ... | @@ -286,8 +283,6 @@ QPDFAcroFormDocumentHelper non-dictionary field 0 |
| 286 | 283 | QPDFAcroFormDocumentHelper loop 0 |
| 287 | 284 | QPDFAcroFormDocumentHelper field found 1 |
| 288 | 285 | QPDFAcroFormDocumentHelper annotation found 1 |
| 289 | -QPDFJob keep files open n 0 | |
| 290 | -QPDFJob keep files open y 0 | |
| 291 | 286 | QPDFJob automatically set keep files open 1 |
| 292 | 287 | QPDFOutlineDocumentHelper string named dest 0 |
| 293 | 288 | QPDFObjectHandle merge top type mismatch 0 |
| ... | ... | @@ -338,7 +333,6 @@ QPDFPageDocumentHelper ignore annotation with no appearance 0 |
| 338 | 333 | QPDFFormFieldObjectHelper replaced BMC at EOF 0 |
| 339 | 334 | QPDFFormFieldObjectHelper fallback Tf 0 |
| 340 | 335 | QPDFPageObjectHelper copy shared attribute 1 |
| 341 | -QPDFJob from_nr from repeat_nr 0 | |
| 342 | 336 | QPDF resolve duplicated page object 0 |
| 343 | 337 | QPDF handle direct page object 0 |
| 344 | 338 | QPDF missing mediabox 0 |
| ... | ... | @@ -453,7 +447,6 @@ QPDFFileSpecObjectHelper empty compat_name 0 |
| 453 | 447 | QPDFFileSpecObjectHelper non-empty compat_name 0 |
| 454 | 448 | QPDFAcroFormDocumentHelper copy annotation 3 |
| 455 | 449 | QPDFAcroFormDocumentHelper field with parent 3 |
| 456 | -QPDFJob pages keeping field from original 0 | |
| 457 | 450 | QPDFObjectHandle merge reuse 0 |
| 458 | 451 | QPDFObjectHandle merge generate 0 |
| 459 | 452 | QPDFAcroFormDocumentHelper replaced DA token 0 |
| ... | ... | @@ -493,8 +486,6 @@ qpdf-c called qpdf_oh_get_binary_string_value 0 |
| 493 | 486 | qpdf-c called qpdf_oh_get_binary_utf8_value 0 |
| 494 | 487 | qpdf-c called qpdf_oh_new_binary_string 0 |
| 495 | 488 | qpdf-c called qpdf_oh_new_binary_unicode_string 0 |
| 496 | -QPDFJob duplicated pages password 0 | |
| 497 | -QPDFJob misplaced pages password 0 | |
| 498 | 489 | QPDFJob check encrypted encrypted 0 |
| 499 | 490 | QPDFJob check encrypted not encrypted 0 |
| 500 | 491 | QPDFJob check password password incorrect 0 |
| ... | ... | @@ -547,8 +538,6 @@ QPDFPageObjectHelper used fallback without copying 0 |
| 547 | 538 | QPDF skipping cache for known unchecked object 0 |
| 548 | 539 | QPDF fix dangling triggered xref reconstruction 0 |
| 549 | 540 | QPDF recover xref stream 0 |
| 550 | -QPDFJob misplaced page range 0 | |
| 551 | -QPDFJob duplicated range 0 | |
| 552 | 541 | QPDFJob json over/under no file 0 |
| 553 | 542 | QPDF_Array copy 1 |
| 554 | 543 | QPDF_json stream data not string 0 | ... | ... |
qpdf/qtest/qpdf/disable-kfo.out
| 1 | -qpdf: selecting --keep-open-files=n | |
| 2 | -qpdf: processing 001-kfo.pdf | |
| 3 | -qpdf: processing 002-kfo.pdf | |
| 4 | -qpdf: processing 003-kfo.pdf | |
| 5 | -qpdf: processing 004-kfo.pdf | |
| 6 | -qpdf: processing 005-kfo.pdf | |
| 7 | -qpdf: processing 006-kfo.pdf | |
| 8 | -qpdf: processing 007-kfo.pdf | |
| 9 | -qpdf: processing 008-kfo.pdf | |
| 10 | -qpdf: processing 009-kfo.pdf | |
| 11 | -qpdf: processing 010-kfo.pdf | |
| 12 | -qpdf: processing 011-kfo.pdf | |
| 13 | -qpdf: processing 012-kfo.pdf | |
| 14 | -qpdf: processing 013-kfo.pdf | |
| 15 | -qpdf: processing 014-kfo.pdf | |
| 16 | -qpdf: processing 015-kfo.pdf | |
| 17 | -qpdf: processing 016-kfo.pdf | |
| 18 | -qpdf: processing 017-kfo.pdf | |
| 19 | -qpdf: processing 018-kfo.pdf | |
| 20 | -qpdf: processing 019-kfo.pdf | |
| 21 | -qpdf: processing 020-kfo.pdf | |
| 22 | -qpdf: processing 021-kfo.pdf | |
| 23 | -qpdf: processing 022-kfo.pdf | |
| 24 | -qpdf: processing 023-kfo.pdf | |
| 25 | -qpdf: processing 024-kfo.pdf | |
| 26 | -qpdf: processing 025-kfo.pdf | |
| 27 | -qpdf: processing 026-kfo.pdf | |
| 28 | -qpdf: processing 027-kfo.pdf | |
| 29 | -qpdf: processing 028-kfo.pdf | |
| 30 | -qpdf: processing 029-kfo.pdf | |
| 31 | -qpdf: processing 030-kfo.pdf | |
| 32 | -qpdf: processing 031-kfo.pdf | |
| 33 | -qpdf: processing 032-kfo.pdf | |
| 34 | -qpdf: processing 033-kfo.pdf | |
| 35 | -qpdf: processing 034-kfo.pdf | |
| 36 | -qpdf: processing 035-kfo.pdf | |
| 37 | -qpdf: processing 036-kfo.pdf | |
| 38 | -qpdf: processing 037-kfo.pdf | |
| 39 | -qpdf: processing 038-kfo.pdf | |
| 40 | -qpdf: processing 039-kfo.pdf | |
| 41 | -qpdf: processing 040-kfo.pdf | |
| 42 | -qpdf: processing 041-kfo.pdf | |
| 43 | -qpdf: processing 042-kfo.pdf | |
| 44 | -qpdf: processing 043-kfo.pdf | |
| 45 | -qpdf: processing 044-kfo.pdf | |
| 46 | -qpdf: processing 045-kfo.pdf | |
| 47 | -qpdf: processing 046-kfo.pdf | |
| 48 | -qpdf: processing 047-kfo.pdf | |
| 49 | -qpdf: processing 048-kfo.pdf | |
| 50 | -qpdf: processing 049-kfo.pdf | |
| 51 | -qpdf: processing 050-kfo.pdf | |
| 52 | -qpdf: processing 051-kfo.pdf | |
| 53 | 1 | qpdf: empty PDF: checking for shared resources |
| 54 | 2 | qpdf: no shared resources found |
| 3 | +qpdf: selecting --keep-open-files=n | |
| 4 | +qpdf: processing 001-kfo.pdf | |
| 55 | 5 | qpdf: 001-kfo.pdf: checking for shared resources |
| 56 | 6 | qpdf: no shared resources found |
| 7 | +qpdf: processing 002-kfo.pdf | |
| 57 | 8 | qpdf: 002-kfo.pdf: checking for shared resources |
| 58 | 9 | qpdf: no shared resources found |
| 10 | +qpdf: processing 003-kfo.pdf | |
| 59 | 11 | qpdf: 003-kfo.pdf: checking for shared resources |
| 60 | 12 | qpdf: no shared resources found |
| 13 | +qpdf: processing 004-kfo.pdf | |
| 61 | 14 | qpdf: 004-kfo.pdf: checking for shared resources |
| 62 | 15 | qpdf: no shared resources found |
| 16 | +qpdf: processing 005-kfo.pdf | |
| 63 | 17 | qpdf: 005-kfo.pdf: checking for shared resources |
| 64 | 18 | qpdf: no shared resources found |
| 19 | +qpdf: processing 006-kfo.pdf | |
| 65 | 20 | qpdf: 006-kfo.pdf: checking for shared resources |
| 66 | 21 | qpdf: no shared resources found |
| 22 | +qpdf: processing 007-kfo.pdf | |
| 67 | 23 | qpdf: 007-kfo.pdf: checking for shared resources |
| 68 | 24 | qpdf: no shared resources found |
| 25 | +qpdf: processing 008-kfo.pdf | |
| 69 | 26 | qpdf: 008-kfo.pdf: checking for shared resources |
| 70 | 27 | qpdf: no shared resources found |
| 28 | +qpdf: processing 009-kfo.pdf | |
| 71 | 29 | qpdf: 009-kfo.pdf: checking for shared resources |
| 72 | 30 | qpdf: no shared resources found |
| 31 | +qpdf: processing 010-kfo.pdf | |
| 73 | 32 | qpdf: 010-kfo.pdf: checking for shared resources |
| 74 | 33 | qpdf: no shared resources found |
| 34 | +qpdf: processing 011-kfo.pdf | |
| 75 | 35 | qpdf: 011-kfo.pdf: checking for shared resources |
| 76 | 36 | qpdf: no shared resources found |
| 37 | +qpdf: processing 012-kfo.pdf | |
| 77 | 38 | qpdf: 012-kfo.pdf: checking for shared resources |
| 78 | 39 | qpdf: no shared resources found |
| 40 | +qpdf: processing 013-kfo.pdf | |
| 79 | 41 | qpdf: 013-kfo.pdf: checking for shared resources |
| 80 | 42 | qpdf: no shared resources found |
| 43 | +qpdf: processing 014-kfo.pdf | |
| 81 | 44 | qpdf: 014-kfo.pdf: checking for shared resources |
| 82 | 45 | qpdf: no shared resources found |
| 46 | +qpdf: processing 015-kfo.pdf | |
| 83 | 47 | qpdf: 015-kfo.pdf: checking for shared resources |
| 84 | 48 | qpdf: no shared resources found |
| 49 | +qpdf: processing 016-kfo.pdf | |
| 85 | 50 | qpdf: 016-kfo.pdf: checking for shared resources |
| 86 | 51 | qpdf: no shared resources found |
| 52 | +qpdf: processing 017-kfo.pdf | |
| 87 | 53 | qpdf: 017-kfo.pdf: checking for shared resources |
| 88 | 54 | qpdf: no shared resources found |
| 55 | +qpdf: processing 018-kfo.pdf | |
| 89 | 56 | qpdf: 018-kfo.pdf: checking for shared resources |
| 90 | 57 | qpdf: no shared resources found |
| 58 | +qpdf: processing 019-kfo.pdf | |
| 91 | 59 | qpdf: 019-kfo.pdf: checking for shared resources |
| 92 | 60 | qpdf: no shared resources found |
| 61 | +qpdf: processing 020-kfo.pdf | |
| 93 | 62 | qpdf: 020-kfo.pdf: checking for shared resources |
| 94 | 63 | qpdf: no shared resources found |
| 64 | +qpdf: processing 021-kfo.pdf | |
| 95 | 65 | qpdf: 021-kfo.pdf: checking for shared resources |
| 96 | 66 | qpdf: no shared resources found |
| 67 | +qpdf: processing 022-kfo.pdf | |
| 97 | 68 | qpdf: 022-kfo.pdf: checking for shared resources |
| 98 | 69 | qpdf: no shared resources found |
| 70 | +qpdf: processing 023-kfo.pdf | |
| 99 | 71 | qpdf: 023-kfo.pdf: checking for shared resources |
| 100 | 72 | qpdf: no shared resources found |
| 73 | +qpdf: processing 024-kfo.pdf | |
| 101 | 74 | qpdf: 024-kfo.pdf: checking for shared resources |
| 102 | 75 | qpdf: no shared resources found |
| 76 | +qpdf: processing 025-kfo.pdf | |
| 103 | 77 | qpdf: 025-kfo.pdf: checking for shared resources |
| 104 | 78 | qpdf: no shared resources found |
| 79 | +qpdf: processing 026-kfo.pdf | |
| 105 | 80 | qpdf: 026-kfo.pdf: checking for shared resources |
| 106 | 81 | qpdf: no shared resources found |
| 82 | +qpdf: processing 027-kfo.pdf | |
| 107 | 83 | qpdf: 027-kfo.pdf: checking for shared resources |
| 108 | 84 | qpdf: no shared resources found |
| 85 | +qpdf: processing 028-kfo.pdf | |
| 109 | 86 | qpdf: 028-kfo.pdf: checking for shared resources |
| 110 | 87 | qpdf: no shared resources found |
| 88 | +qpdf: processing 029-kfo.pdf | |
| 111 | 89 | qpdf: 029-kfo.pdf: checking for shared resources |
| 112 | 90 | qpdf: no shared resources found |
| 91 | +qpdf: processing 030-kfo.pdf | |
| 113 | 92 | qpdf: 030-kfo.pdf: checking for shared resources |
| 114 | 93 | qpdf: no shared resources found |
| 94 | +qpdf: processing 031-kfo.pdf | |
| 115 | 95 | qpdf: 031-kfo.pdf: checking for shared resources |
| 116 | 96 | qpdf: no shared resources found |
| 97 | +qpdf: processing 032-kfo.pdf | |
| 117 | 98 | qpdf: 032-kfo.pdf: checking for shared resources |
| 118 | 99 | qpdf: no shared resources found |
| 100 | +qpdf: processing 033-kfo.pdf | |
| 119 | 101 | qpdf: 033-kfo.pdf: checking for shared resources |
| 120 | 102 | qpdf: no shared resources found |
| 103 | +qpdf: processing 034-kfo.pdf | |
| 121 | 104 | qpdf: 034-kfo.pdf: checking for shared resources |
| 122 | 105 | qpdf: no shared resources found |
| 106 | +qpdf: processing 035-kfo.pdf | |
| 123 | 107 | qpdf: 035-kfo.pdf: checking for shared resources |
| 124 | 108 | qpdf: no shared resources found |
| 109 | +qpdf: processing 036-kfo.pdf | |
| 125 | 110 | qpdf: 036-kfo.pdf: checking for shared resources |
| 126 | 111 | qpdf: no shared resources found |
| 112 | +qpdf: processing 037-kfo.pdf | |
| 127 | 113 | qpdf: 037-kfo.pdf: checking for shared resources |
| 128 | 114 | qpdf: no shared resources found |
| 115 | +qpdf: processing 038-kfo.pdf | |
| 129 | 116 | qpdf: 038-kfo.pdf: checking for shared resources |
| 130 | 117 | qpdf: no shared resources found |
| 118 | +qpdf: processing 039-kfo.pdf | |
| 131 | 119 | qpdf: 039-kfo.pdf: checking for shared resources |
| 132 | 120 | qpdf: no shared resources found |
| 121 | +qpdf: processing 040-kfo.pdf | |
| 133 | 122 | qpdf: 040-kfo.pdf: checking for shared resources |
| 134 | 123 | qpdf: no shared resources found |
| 124 | +qpdf: processing 041-kfo.pdf | |
| 135 | 125 | qpdf: 041-kfo.pdf: checking for shared resources |
| 136 | 126 | qpdf: no shared resources found |
| 127 | +qpdf: processing 042-kfo.pdf | |
| 137 | 128 | qpdf: 042-kfo.pdf: checking for shared resources |
| 138 | 129 | qpdf: no shared resources found |
| 130 | +qpdf: processing 043-kfo.pdf | |
| 139 | 131 | qpdf: 043-kfo.pdf: checking for shared resources |
| 140 | 132 | qpdf: no shared resources found |
| 133 | +qpdf: processing 044-kfo.pdf | |
| 141 | 134 | qpdf: 044-kfo.pdf: checking for shared resources |
| 142 | 135 | qpdf: no shared resources found |
| 136 | +qpdf: processing 045-kfo.pdf | |
| 143 | 137 | qpdf: 045-kfo.pdf: checking for shared resources |
| 144 | 138 | qpdf: no shared resources found |
| 139 | +qpdf: processing 046-kfo.pdf | |
| 145 | 140 | qpdf: 046-kfo.pdf: checking for shared resources |
| 146 | 141 | qpdf: no shared resources found |
| 142 | +qpdf: processing 047-kfo.pdf | |
| 147 | 143 | qpdf: 047-kfo.pdf: checking for shared resources |
| 148 | 144 | qpdf: no shared resources found |
| 145 | +qpdf: processing 048-kfo.pdf | |
| 149 | 146 | qpdf: 048-kfo.pdf: checking for shared resources |
| 150 | 147 | qpdf: no shared resources found |
| 148 | +qpdf: processing 049-kfo.pdf | |
| 151 | 149 | qpdf: 049-kfo.pdf: checking for shared resources |
| 152 | 150 | qpdf: no shared resources found |
| 151 | +qpdf: processing 050-kfo.pdf | |
| 153 | 152 | qpdf: 050-kfo.pdf: checking for shared resources |
| 154 | 153 | qpdf: no shared resources found |
| 154 | +qpdf: processing 051-kfo.pdf | |
| 155 | 155 | qpdf: 051-kfo.pdf: checking for shared resources |
| 156 | 156 | qpdf: no shared resources found |
| 157 | 157 | qpdf: removing unreferenced pages from primary input | ... | ... |
qpdf/qtest/qpdf/enable-kfo.out
| 1 | -qpdf: selecting --keep-open-files=y | |
| 2 | -qpdf: processing 010-kfo.pdf | |
| 3 | -qpdf: processing 011-kfo.pdf | |
| 4 | -qpdf: processing 012-kfo.pdf | |
| 5 | -qpdf: processing 013-kfo.pdf | |
| 6 | -qpdf: processing 014-kfo.pdf | |
| 7 | -qpdf: processing 015-kfo.pdf | |
| 8 | -qpdf: processing 016-kfo.pdf | |
| 9 | -qpdf: processing 017-kfo.pdf | |
| 10 | -qpdf: processing 018-kfo.pdf | |
| 11 | -qpdf: processing 019-kfo.pdf | |
| 12 | 1 | qpdf: empty PDF: checking for shared resources |
| 13 | 2 | qpdf: no shared resources found |
| 3 | +qpdf: selecting --keep-open-files=y | |
| 4 | +qpdf: processing 010-kfo.pdf | |
| 14 | 5 | qpdf: 010-kfo.pdf: checking for shared resources |
| 15 | 6 | qpdf: no shared resources found |
| 7 | +qpdf: processing 011-kfo.pdf | |
| 16 | 8 | qpdf: 011-kfo.pdf: checking for shared resources |
| 17 | 9 | qpdf: no shared resources found |
| 10 | +qpdf: processing 012-kfo.pdf | |
| 18 | 11 | qpdf: 012-kfo.pdf: checking for shared resources |
| 19 | 12 | qpdf: no shared resources found |
| 13 | +qpdf: processing 013-kfo.pdf | |
| 20 | 14 | qpdf: 013-kfo.pdf: checking for shared resources |
| 21 | 15 | qpdf: no shared resources found |
| 16 | +qpdf: processing 014-kfo.pdf | |
| 22 | 17 | qpdf: 014-kfo.pdf: checking for shared resources |
| 23 | 18 | qpdf: no shared resources found |
| 19 | +qpdf: processing 015-kfo.pdf | |
| 24 | 20 | qpdf: 015-kfo.pdf: checking for shared resources |
| 25 | 21 | qpdf: no shared resources found |
| 22 | +qpdf: processing 016-kfo.pdf | |
| 26 | 23 | qpdf: 016-kfo.pdf: checking for shared resources |
| 27 | 24 | qpdf: no shared resources found |
| 25 | +qpdf: processing 017-kfo.pdf | |
| 28 | 26 | qpdf: 017-kfo.pdf: checking for shared resources |
| 29 | 27 | qpdf: no shared resources found |
| 28 | +qpdf: processing 018-kfo.pdf | |
| 30 | 29 | qpdf: 018-kfo.pdf: checking for shared resources |
| 31 | 30 | qpdf: no shared resources found |
| 31 | +qpdf: processing 019-kfo.pdf | |
| 32 | 32 | qpdf: 019-kfo.pdf: checking for shared resources |
| 33 | 33 | qpdf: no shared resources found |
| 34 | 34 | qpdf: removing unreferenced pages from primary input | ... | ... |
qpdf/qtest/qpdf/kfo-n.out
| 1 | -qpdf: processing 001-kfo.pdf | |
| 2 | -qpdf: processing 002-kfo.pdf | |
| 3 | -qpdf: processing 003-kfo.pdf | |
| 4 | -qpdf: processing 004-kfo.pdf | |
| 5 | -qpdf: processing 005-kfo.pdf | |
| 6 | -qpdf: processing 006-kfo.pdf | |
| 7 | -qpdf: processing 007-kfo.pdf | |
| 8 | -qpdf: processing 008-kfo.pdf | |
| 9 | -qpdf: processing 009-kfo.pdf | |
| 10 | 1 | qpdf: empty PDF: checking for shared resources |
| 11 | 2 | qpdf: no shared resources found |
| 3 | +qpdf: processing 001-kfo.pdf | |
| 12 | 4 | qpdf: 001-kfo.pdf: checking for shared resources |
| 13 | 5 | qpdf: no shared resources found |
| 6 | +qpdf: processing 002-kfo.pdf | |
| 14 | 7 | qpdf: 002-kfo.pdf: checking for shared resources |
| 15 | 8 | qpdf: no shared resources found |
| 9 | +qpdf: processing 003-kfo.pdf | |
| 16 | 10 | qpdf: 003-kfo.pdf: checking for shared resources |
| 17 | 11 | qpdf: no shared resources found |
| 12 | +qpdf: processing 004-kfo.pdf | |
| 18 | 13 | qpdf: 004-kfo.pdf: checking for shared resources |
| 19 | 14 | qpdf: no shared resources found |
| 15 | +qpdf: processing 005-kfo.pdf | |
| 20 | 16 | qpdf: 005-kfo.pdf: checking for shared resources |
| 21 | 17 | qpdf: no shared resources found |
| 18 | +qpdf: processing 006-kfo.pdf | |
| 22 | 19 | qpdf: 006-kfo.pdf: checking for shared resources |
| 23 | 20 | qpdf: no shared resources found |
| 21 | +qpdf: processing 007-kfo.pdf | |
| 24 | 22 | qpdf: 007-kfo.pdf: checking for shared resources |
| 25 | 23 | qpdf: no shared resources found |
| 24 | +qpdf: processing 008-kfo.pdf | |
| 26 | 25 | qpdf: 008-kfo.pdf: checking for shared resources |
| 27 | 26 | qpdf: no shared resources found |
| 27 | +qpdf: processing 009-kfo.pdf | |
| 28 | 28 | qpdf: 009-kfo.pdf: checking for shared resources |
| 29 | 29 | qpdf: no shared resources found |
| 30 | 30 | qpdf: removing unreferenced pages from primary input | ... | ... |
qpdf/qtest/qpdf/kfo-y.out
| 1 | -qpdf: processing 001-kfo.pdf | |
| 2 | -qpdf: processing 002-kfo.pdf | |
| 3 | -qpdf: processing 003-kfo.pdf | |
| 4 | -qpdf: processing 004-kfo.pdf | |
| 5 | -qpdf: processing 005-kfo.pdf | |
| 6 | -qpdf: processing 006-kfo.pdf | |
| 7 | -qpdf: processing 007-kfo.pdf | |
| 8 | -qpdf: processing 008-kfo.pdf | |
| 9 | -qpdf: processing 009-kfo.pdf | |
| 10 | 1 | qpdf: empty PDF: checking for shared resources |
| 11 | 2 | qpdf: no shared resources found |
| 3 | +qpdf: processing 001-kfo.pdf | |
| 12 | 4 | qpdf: 001-kfo.pdf: checking for shared resources |
| 13 | 5 | qpdf: no shared resources found |
| 6 | +qpdf: processing 002-kfo.pdf | |
| 14 | 7 | qpdf: 002-kfo.pdf: checking for shared resources |
| 15 | 8 | qpdf: no shared resources found |
| 9 | +qpdf: processing 003-kfo.pdf | |
| 16 | 10 | qpdf: 003-kfo.pdf: checking for shared resources |
| 17 | 11 | qpdf: no shared resources found |
| 12 | +qpdf: processing 004-kfo.pdf | |
| 18 | 13 | qpdf: 004-kfo.pdf: checking for shared resources |
| 19 | 14 | qpdf: no shared resources found |
| 15 | +qpdf: processing 005-kfo.pdf | |
| 20 | 16 | qpdf: 005-kfo.pdf: checking for shared resources |
| 21 | 17 | qpdf: no shared resources found |
| 18 | +qpdf: processing 006-kfo.pdf | |
| 22 | 19 | qpdf: 006-kfo.pdf: checking for shared resources |
| 23 | 20 | qpdf: no shared resources found |
| 21 | +qpdf: processing 007-kfo.pdf | |
| 24 | 22 | qpdf: 007-kfo.pdf: checking for shared resources |
| 25 | 23 | qpdf: no shared resources found |
| 24 | +qpdf: processing 008-kfo.pdf | |
| 26 | 25 | qpdf: 008-kfo.pdf: checking for shared resources |
| 27 | 26 | qpdf: no shared resources found |
| 27 | +qpdf: processing 009-kfo.pdf | |
| 28 | 28 | qpdf: 009-kfo.pdf: checking for shared resources |
| 29 | 29 | qpdf: no shared resources found |
| 30 | 30 | qpdf: removing unreferenced pages from primary input | ... | ... |
qpdf/qtest/qpdf/uo-6.out
| 1 | -qpdf: selecting --keep-open-files=y | |
| 2 | 1 | qpdf: fxo-red.pdf: checking for shared resources |
| 3 | 2 | qpdf: no shared resources found |
| 3 | +qpdf: selecting --keep-open-files=y | |
| 4 | 4 | qpdf: removing unreferenced pages from primary input |
| 5 | 5 | qpdf: adding pages from fxo-red.pdf |
| 6 | 6 | qpdf: processing underlay/overlay | ... | ... |
qpdf/qtest/qpdf/uo-8.out
| 1 | -qpdf: selecting --keep-open-files=y | |
| 2 | 1 | qpdf: fxo-red.pdf: checking for shared resources |
| 3 | 2 | qpdf: no shared resources found |
| 3 | +qpdf: selecting --keep-open-files=y | |
| 4 | 4 | qpdf: removing unreferenced pages from primary input |
| 5 | 5 | qpdf: adding pages from fxo-red.pdf |
| 6 | 6 | qpdf: processing underlay/overlay | ... | ... |
qpdf/qtest/qpdf/verbose-merge.out
| 1 | +qpdf: page-labels-and-outlines.pdf: checking for shared resources | |
| 2 | +qpdf: no shared resources found | |
| 1 | 3 | qpdf: selecting --keep-open-files=y |
| 2 | -qpdf: processing 20-pages.pdf | |
| 3 | 4 | qpdf: processing ./20-pages.pdf |
| 4 | -qpdf: processing minimal.pdf | |
| 5 | 5 | qpdf: ./20-pages.pdf: checking for shared resources |
| 6 | 6 | qpdf: no shared resources found |
| 7 | +qpdf: processing 20-pages.pdf | |
| 7 | 8 | qpdf: 20-pages.pdf: checking for shared resources |
| 8 | 9 | qpdf: no shared resources found |
| 10 | +qpdf: processing minimal.pdf | |
| 9 | 11 | qpdf: minimal.pdf: checking for shared resources |
| 10 | 12 | qpdf: no shared resources found |
| 11 | -qpdf: page-labels-and-outlines.pdf: checking for shared resources | |
| 12 | -qpdf: no shared resources found | |
| 13 | 13 | qpdf: removing unreferenced pages from primary input |
| 14 | 14 | qpdf: adding pages from page-labels-and-outlines.pdf |
| 15 | 15 | qpdf: adding pages from 20-pages.pdf | ... | ... |