Commit adccedc02fea78dd5a924605d254ea709d63473b

Authored by Jay Berkenbilt
1 parent 88bacb64

Allow numeric range to be omitted qpdf --pages

Detect a missing page range and assume 1-z.
ChangeLog
  1 +2013-07-07 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * qpdf: allow omission of range in --pages. If range is omitted
  4 + such that an argument that is supposed to be a range is an invalid
  5 + range and a valid file name, the range of 1-z is assumed. This
  6 + makes it possible to merge a bunch of files with something like
  7 + qpdf --empty out.pdf --pages *.pdf --
  8 +
1 2013-06-15 Jay Berkenbilt <ejb@ql.org> 9 2013-06-15 Jay Berkenbilt <ejb@ql.org>
2 10
3 * Handle some additional broken files with missing /ID in trailer 11 * Handle some additional broken files with missing /ID in trailer
manual/qpdf-manual.xml
@@ -614,7 +614,7 @@ make @@ -614,7 +614,7 @@ make
614 file is given as the primary input file is used as the starting 614 file is given as the primary input file is used as the starting
615 point, but its pages are replaced with pages as specified. 615 point, but its pages are replaced with pages as specified.
616 616
617 - <programlisting><option>--pages <replaceable>input-file</replaceable> [ <replaceable>--password=password</replaceable> ] <replaceable>page-range</replaceable> [ ... ] --</option> 617 + <programlisting><option>--pages <replaceable>input-file</replaceable> [ <replaceable>--password=password</replaceable> ] [ <replaceable>page-range</replaceable> ] [ ... ] --</option>
618 </programlisting> 618 </programlisting>
619 Multiple input files may be specified. Each one is given as the 619 Multiple input files may be specified. Each one is given as the
620 name of the input file, an optional password (if required to open 620 name of the input file, an optional password (if required to open
@@ -636,6 +636,15 @@ make @@ -636,6 +636,15 @@ make
636 primary input. 636 primary input.
637 </para> 637 </para>
638 <para> 638 <para>
  639 + Starting with qpdf 4.2.0, it is possible to omit the page range.
  640 + If qpdf sees a value in the place where it expects a page range
  641 + and that value is not a valid range but is a valid file name, qpdf
  642 + will implicitly use the range <literal>1-z</literal>, meaning that
  643 + it will include all pages in the file. This makes it possible to
  644 + easily combine all pages in a set of files with a command like
  645 + <command>qpdf --empty out.pdf --pages *.pdf --</command>.
  646 + </para>
  647 + <para>
639 It is not presently possible to specify the same page from the 648 It is not presently possible to specify the same page from the
640 same file directly more than once, but you can make this work by 649 same file directly more than once, but you can make this work by
641 specifying two different paths to the same file (such as by 650 specifying two different paths to the same file (such as by
qpdf/qpdf.cc
@@ -163,7 +163,7 @@ These options allow pages to be selected from one or more PDF files.\n\ @@ -163,7 +163,7 @@ These options allow pages to be selected from one or more PDF files.\n\
163 Whatever file is given as the primary input file is used as the\n\ 163 Whatever file is given as the primary input file is used as the\n\
164 starting point, but its pages are replaced with pages as specified.\n\ 164 starting point, but its pages are replaced with pages as specified.\n\
165 \n\ 165 \n\
166 ---pages file [ --password=password ] page-range ... --\n\ 166 +--pages file [ --password=password ] [ page-range ] ... --\n\
167 \n\ 167 \n\
168 For each file that pages should be taken from, specify the file, a\n\ 168 For each file that pages should be taken from, specify the file, a\n\
169 password needed to open the file (if any), and a page range. The\n\ 169 password needed to open the file (if any), and a page range. The\n\
@@ -183,6 +183,10 @@ pages to appear in reverse. Repeating a number will cause an error, but\n\ @@ -183,6 +183,10 @@ pages to appear in reverse. Repeating a number will cause an error, but\n\
183 the manual discusses a workaround should you really want to include the\n\ 183 the manual discusses a workaround should you really want to include the\n\
184 same page twice.\n\ 184 same page twice.\n\
185 \n\ 185 \n\
  186 +If the page range is omitted, the range of 1-z is assumed. qpdf decides\n\
  187 +that the page range is omitted if the range argument is either -- or a\n\
  188 +valid file name and not a valid range.\n\
  189 +\n\
186 See the manual for examples and a discussion of additional subtleties.\n\ 190 See the manual for examples and a discussion of additional subtleties.\n\
187 \n\ 191 \n\
188 \n\ 192 \n\
@@ -354,7 +358,8 @@ static void show_encryption(QPDF&amp; pdf) @@ -354,7 +358,8 @@ static void show_encryption(QPDF&amp; pdf)
354 } 358 }
355 } 359 }
356 360
357 -static std::vector<int> parse_numrange(char const* range, int max) 361 +static std::vector<int> parse_numrange(char const* range, int max,
  362 + bool throw_error = false)
358 { 363 {
359 std::vector<int> result; 364 std::vector<int> result;
360 char const* p = range; 365 char const* p = range;
@@ -436,7 +441,9 @@ static std::vector&lt;int&gt; parse_numrange(char const* range, int max) @@ -436,7 +441,9 @@ static std::vector&lt;int&gt; parse_numrange(char const* range, int max)
436 for (size_t i = 0; i < work.size(); i += 2) 441 for (size_t i = 0; i < work.size(); i += 2)
437 { 442 {
438 int num = work[i]; 443 int num = work[i];
439 - if ((num < 1) || (num > max)) 444 + // max == 0 means we don't know the max and are just
  445 + // testing for valid syntax.
  446 + if ((max > 0) && ((num < 1) || (num > max)))
440 { 447 {
441 throw std::runtime_error( 448 throw std::runtime_error(
442 "number " + QUtil::int_to_string(num) + " out of range"); 449 "number " + QUtil::int_to_string(num) + " out of range");
@@ -480,6 +487,10 @@ static std::vector&lt;int&gt; parse_numrange(char const* range, int max) @@ -480,6 +487,10 @@ static std::vector&lt;int&gt; parse_numrange(char const* range, int max)
480 } 487 }
481 catch (std::runtime_error e) 488 catch (std::runtime_error e)
482 { 489 {
  490 + if (throw_error)
  491 + {
  492 + throw e;
  493 + }
483 if (p) 494 if (p)
484 { 495 {
485 usage("error at * in numeric range " + 496 usage("error at * in numeric range " +
@@ -839,9 +850,9 @@ parse_pages_options( @@ -839,9 +850,9 @@ parse_pages_options(
839 { 850 {
840 usage("insufficient arguments to --pages"); 851 usage("insufficient arguments to --pages");
841 } 852 }
842 - char* file = argv[cur_arg++];  
843 - char* password = 0;  
844 - char* range = argv[cur_arg++]; 853 + char const* file = argv[cur_arg++];
  854 + char const* password = 0;
  855 + char const* range = argv[cur_arg++];
845 if (strncmp(range, "--password=", 11) == 0) 856 if (strncmp(range, "--password=", 11) == 0)
846 { 857 {
847 // Oh, that's the password, not the range 858 // Oh, that's the password, not the range
@@ -853,6 +864,44 @@ parse_pages_options( @@ -853,6 +864,44 @@ parse_pages_options(
853 range = argv[cur_arg++]; 864 range = argv[cur_arg++];
854 } 865 }
855 866
  867 + // See if the user omitted the range entirely, in which case
  868 + // we assume "1-z".
  869 + bool range_omitted = false;
  870 + if (strcmp(range, "--") == 0)
  871 + {
  872 + // The filename or password was the last argument
  873 + QTC::TC("qpdf", "qpdf pages range omitted at end");
  874 + range_omitted = true;
  875 + }
  876 + else
  877 + {
  878 + try
  879 + {
  880 + parse_numrange(range, 0, true);
  881 + }
  882 + catch (std::runtime_error& e1)
  883 + {
  884 + // The range is invalid. Let's see if it's a file.
  885 + try
  886 + {
  887 + fclose(QUtil::safe_fopen(range, "rb"));
  888 + // Yup, it's a file.
  889 + QTC::TC("qpdf", "qpdf pages range omitted in middle");
  890 + range_omitted = true;
  891 + }
  892 + catch (std::runtime_error& e2)
  893 + {
  894 + // Ignore. The range is invalid and not a file.
  895 + // We'll get an error message later.
  896 + }
  897 + }
  898 + }
  899 + if (range_omitted)
  900 + {
  901 + --cur_arg;
  902 + range = "1-z";
  903 + }
  904 +
856 result.push_back(PageSpec(file, password, range)); 905 result.push_back(PageSpec(file, password, range));
857 } 906 }
858 return result; 907 return result;
qpdf/qpdf.testcov
@@ -265,3 +265,5 @@ QPDF not caching overridden objstm object 0 @@ -265,3 +265,5 @@ QPDF not caching overridden objstm object 0
265 QPDFWriter original obj non-zero gen 0 265 QPDFWriter original obj non-zero gen 0
266 QPDF_optimization indirect outlines 0 266 QPDF_optimization indirect outlines 0
267 QPDF xref space 2 267 QPDF xref space 2
  268 +qpdf pages range omitted at end 0
  269 +qpdf pages range omitted in middle 0
qpdf/qtest/qpdf.test
@@ -567,7 +567,7 @@ foreach my $d (@nrange_tests) @@ -567,7 +567,7 @@ foreach my $d (@nrange_tests)
567 show_ntests(); 567 show_ntests();
568 # ---------- 568 # ----------
569 $td->notify("--- Merging and Splitting ---"); 569 $td->notify("--- Merging and Splitting ---");
570 -$n_tests += 6; 570 +$n_tests += 8;
571 571
572 # Select pages from the same file multiple times including selecting 572 # Select pages from the same file multiple times including selecting
573 # twice from an encrypted file and specifying the password only the 573 # twice from an encrypted file and specifying the password only the
@@ -612,6 +612,16 @@ $td-&gt;runtest(&quot;avoid respecification of password&quot;, @@ -612,6 +612,16 @@ $td-&gt;runtest(&quot;avoid respecification of password&quot;,
612 $td->runtest("check output", 612 $td->runtest("check output",
613 {$td->FILE => "a.pdf"}, 613 {$td->FILE => "a.pdf"},
614 {$td->FILE => "pages-copy-encryption.pdf"}); 614 {$td->FILE => "pages-copy-encryption.pdf"});
  615 +$td->runtest("merge with implicit ranges",
  616 + {$td->COMMAND =>
  617 + "qpdf --empty a.pdf" .
  618 + " --pages minimal.pdf 20-pages.pdf --password=user" .
  619 + " page-labels-and-outlines.pdf --" .
  620 + " --static-id"},
  621 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  622 +$td->runtest("check output",
  623 + {$td->FILE => "a.pdf"},
  624 + {$td->FILE => "merge-implicit-ranges.pdf"});
615 show_ntests(); 625 show_ntests();
616 # ---------- 626 # ----------
617 $td->notify("--- PDF From Scratch ---"); 627 $td->notify("--- PDF From Scratch ---");
qpdf/qtest/qpdf/merge-implicit-ranges.pdf 0 → 100644
No preview for this file type