mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-02 22:50:20 +00:00
Allow numeric range to be omitted qpdf --pages
Detect a missing page range and assume 1-z.
This commit is contained in:
parent
88bacb6449
commit
adccedc02f
@ -1,3 +1,11 @@
|
||||
2013-07-07 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* qpdf: allow omission of range in --pages. If range is omitted
|
||||
such that an argument that is supposed to be a range is an invalid
|
||||
range and a valid file name, the range of 1-z is assumed. This
|
||||
makes it possible to merge a bunch of files with something like
|
||||
qpdf --empty out.pdf --pages *.pdf --
|
||||
|
||||
2013-06-15 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Handle some additional broken files with missing /ID in trailer
|
||||
|
@ -614,7 +614,7 @@ make
|
||||
file is given as the primary input file is used as the starting
|
||||
point, but its pages are replaced with pages as specified.
|
||||
|
||||
<programlisting><option>--pages <replaceable>input-file</replaceable> [ <replaceable>--password=password</replaceable> ] <replaceable>page-range</replaceable> [ ... ] --</option>
|
||||
<programlisting><option>--pages <replaceable>input-file</replaceable> [ <replaceable>--password=password</replaceable> ] [ <replaceable>page-range</replaceable> ] [ ... ] --</option>
|
||||
</programlisting>
|
||||
Multiple input files may be specified. Each one is given as the
|
||||
name of the input file, an optional password (if required to open
|
||||
@ -635,6 +635,15 @@ make
|
||||
input file. To discard these, use <option>--empty</option> as the
|
||||
primary input.
|
||||
</para>
|
||||
<para>
|
||||
Starting with qpdf 4.2.0, it is possible to omit the page range.
|
||||
If qpdf sees a value in the place where it expects a page range
|
||||
and that value is not a valid range but is a valid file name, qpdf
|
||||
will implicitly use the range <literal>1-z</literal>, meaning that
|
||||
it will include all pages in the file. This makes it possible to
|
||||
easily combine all pages in a set of files with a command like
|
||||
<command>qpdf --empty out.pdf --pages *.pdf --</command>.
|
||||
</para>
|
||||
<para>
|
||||
It is not presently possible to specify the same page from the
|
||||
same file directly more than once, but you can make this work by
|
||||
|
61
qpdf/qpdf.cc
61
qpdf/qpdf.cc
@ -163,7 +163,7 @@ These options allow pages to be selected from one or more PDF files.\n\
|
||||
Whatever file is given as the primary input file is used as the\n\
|
||||
starting point, but its pages are replaced with pages as specified.\n\
|
||||
\n\
|
||||
--pages file [ --password=password ] page-range ... --\n\
|
||||
--pages file [ --password=password ] [ page-range ] ... --\n\
|
||||
\n\
|
||||
For each file that pages should be taken from, specify the file, a\n\
|
||||
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\
|
||||
the manual discusses a workaround should you really want to include the\n\
|
||||
same page twice.\n\
|
||||
\n\
|
||||
If the page range is omitted, the range of 1-z is assumed. qpdf decides\n\
|
||||
that the page range is omitted if the range argument is either -- or a\n\
|
||||
valid file name and not a valid range.\n\
|
||||
\n\
|
||||
See the manual for examples and a discussion of additional subtleties.\n\
|
||||
\n\
|
||||
\n\
|
||||
@ -354,7 +358,8 @@ static void show_encryption(QPDF& pdf)
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<int> parse_numrange(char const* range, int max)
|
||||
static std::vector<int> parse_numrange(char const* range, int max,
|
||||
bool throw_error = false)
|
||||
{
|
||||
std::vector<int> result;
|
||||
char const* p = range;
|
||||
@ -436,7 +441,9 @@ static std::vector<int> parse_numrange(char const* range, int max)
|
||||
for (size_t i = 0; i < work.size(); i += 2)
|
||||
{
|
||||
int num = work[i];
|
||||
if ((num < 1) || (num > max))
|
||||
// max == 0 means we don't know the max and are just
|
||||
// testing for valid syntax.
|
||||
if ((max > 0) && ((num < 1) || (num > max)))
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"number " + QUtil::int_to_string(num) + " out of range");
|
||||
@ -480,6 +487,10 @@ static std::vector<int> parse_numrange(char const* range, int max)
|
||||
}
|
||||
catch (std::runtime_error e)
|
||||
{
|
||||
if (throw_error)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
usage("error at * in numeric range " +
|
||||
@ -839,9 +850,9 @@ parse_pages_options(
|
||||
{
|
||||
usage("insufficient arguments to --pages");
|
||||
}
|
||||
char* file = argv[cur_arg++];
|
||||
char* password = 0;
|
||||
char* range = argv[cur_arg++];
|
||||
char const* file = argv[cur_arg++];
|
||||
char const* password = 0;
|
||||
char const* range = argv[cur_arg++];
|
||||
if (strncmp(range, "--password=", 11) == 0)
|
||||
{
|
||||
// Oh, that's the password, not the range
|
||||
@ -853,6 +864,44 @@ parse_pages_options(
|
||||
range = argv[cur_arg++];
|
||||
}
|
||||
|
||||
// See if the user omitted the range entirely, in which case
|
||||
// we assume "1-z".
|
||||
bool range_omitted = false;
|
||||
if (strcmp(range, "--") == 0)
|
||||
{
|
||||
// The filename or password was the last argument
|
||||
QTC::TC("qpdf", "qpdf pages range omitted at end");
|
||||
range_omitted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
parse_numrange(range, 0, true);
|
||||
}
|
||||
catch (std::runtime_error& e1)
|
||||
{
|
||||
// The range is invalid. Let's see if it's a file.
|
||||
try
|
||||
{
|
||||
fclose(QUtil::safe_fopen(range, "rb"));
|
||||
// Yup, it's a file.
|
||||
QTC::TC("qpdf", "qpdf pages range omitted in middle");
|
||||
range_omitted = true;
|
||||
}
|
||||
catch (std::runtime_error& e2)
|
||||
{
|
||||
// Ignore. The range is invalid and not a file.
|
||||
// We'll get an error message later.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (range_omitted)
|
||||
{
|
||||
--cur_arg;
|
||||
range = "1-z";
|
||||
}
|
||||
|
||||
result.push_back(PageSpec(file, password, range));
|
||||
}
|
||||
return result;
|
||||
|
@ -265,3 +265,5 @@ QPDF not caching overridden objstm object 0
|
||||
QPDFWriter original obj non-zero gen 0
|
||||
QPDF_optimization indirect outlines 0
|
||||
QPDF xref space 2
|
||||
qpdf pages range omitted at end 0
|
||||
qpdf pages range omitted in middle 0
|
||||
|
@ -567,7 +567,7 @@ foreach my $d (@nrange_tests)
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Merging and Splitting ---");
|
||||
$n_tests += 6;
|
||||
$n_tests += 8;
|
||||
|
||||
# Select pages from the same file multiple times including selecting
|
||||
# twice from an encrypted file and specifying the password only the
|
||||
@ -612,6 +612,16 @@ $td->runtest("avoid respecification of password",
|
||||
$td->runtest("check output",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "pages-copy-encryption.pdf"});
|
||||
$td->runtest("merge with implicit ranges",
|
||||
{$td->COMMAND =>
|
||||
"qpdf --empty a.pdf" .
|
||||
" --pages minimal.pdf 20-pages.pdf --password=user" .
|
||||
" page-labels-and-outlines.pdf --" .
|
||||
" --static-id"},
|
||||
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||
$td->runtest("check output",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "merge-implicit-ranges.pdf"});
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- PDF From Scratch ---");
|
||||
|
628
qpdf/qtest/qpdf/merge-implicit-ranges.pdf
Normal file
628
qpdf/qtest/qpdf/merge-implicit-ranges.pdf
Normal file
@ -0,0 +1,628 @@
|
||||
%PDF-1.3
|
||||
%¿÷¢þ
|
||||
1 0 obj
|
||||
<< /Pages 2 0 R /Type /Catalog >>
|
||||
endobj
|
||||
2 0 obj
|
||||
<< /Count 51 /Kids [ 3 0 R 4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R 37 0 R 38 0 R 39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R 45 0 R 46 0 R 47 0 R 48 0 R 49 0 R 50 0 R 51 0 R 52 0 R 53 0 R ] /Type /Pages >>
|
||||
endobj
|
||||
3 0 obj
|
||||
<< /Contents 54 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 55 0 R >> /ProcSet 56 0 R >> /Type /Page >>
|
||||
endobj
|
||||
4 0 obj
|
||||
<< /Contents 57 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
5 0 obj
|
||||
<< /Contents 59 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
6 0 obj
|
||||
<< /Contents 60 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
7 0 obj
|
||||
<< /Contents 61 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
8 0 obj
|
||||
<< /Contents 62 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
9 0 obj
|
||||
<< /Contents 63 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
10 0 obj
|
||||
<< /Contents 64 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
11 0 obj
|
||||
<< /Contents 65 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
12 0 obj
|
||||
<< /Contents 66 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
13 0 obj
|
||||
<< /Contents 67 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
14 0 obj
|
||||
<< /Contents 68 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
15 0 obj
|
||||
<< /Contents 69 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
16 0 obj
|
||||
<< /Contents 70 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
17 0 obj
|
||||
<< /Contents 71 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
18 0 obj
|
||||
<< /Contents 72 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
19 0 obj
|
||||
<< /Contents 73 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
20 0 obj
|
||||
<< /Contents 74 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
21 0 obj
|
||||
<< /Contents 75 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
22 0 obj
|
||||
<< /Contents 76 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
23 0 obj
|
||||
<< /Contents 77 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 58 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
|
||||
endobj
|
||||
24 0 obj
|
||||
<< /Contents 78 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
25 0 obj
|
||||
<< /Contents 81 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
26 0 obj
|
||||
<< /Contents 82 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
27 0 obj
|
||||
<< /Contents 83 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
28 0 obj
|
||||
<< /Contents 84 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
29 0 obj
|
||||
<< /Contents 85 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
30 0 obj
|
||||
<< /Contents 86 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
31 0 obj
|
||||
<< /Contents 87 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
32 0 obj
|
||||
<< /Contents 88 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
33 0 obj
|
||||
<< /Contents 89 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
34 0 obj
|
||||
<< /Contents 90 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
35 0 obj
|
||||
<< /Contents 91 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
36 0 obj
|
||||
<< /Contents 92 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
37 0 obj
|
||||
<< /Contents 93 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
38 0 obj
|
||||
<< /Contents 94 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
39 0 obj
|
||||
<< /Contents 95 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
40 0 obj
|
||||
<< /Contents 96 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
41 0 obj
|
||||
<< /Contents 97 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
42 0 obj
|
||||
<< /Contents 98 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
43 0 obj
|
||||
<< /Contents 99 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
44 0 obj
|
||||
<< /Contents 100 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
45 0 obj
|
||||
<< /Contents 101 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
46 0 obj
|
||||
<< /Contents 102 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
47 0 obj
|
||||
<< /Contents 103 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
48 0 obj
|
||||
<< /Contents 104 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
49 0 obj
|
||||
<< /Contents 105 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
50 0 obj
|
||||
<< /Contents 106 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
51 0 obj
|
||||
<< /Contents 107 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
52 0 obj
|
||||
<< /Contents 108 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
53 0 obj
|
||||
<< /Contents 109 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 79 0 R >> /ProcSet 80 0 R >> /Type /Page >>
|
||||
endobj
|
||||
54 0 obj
|
||||
<< /Length 48 /Filter /FlateDecode >>
|
||||
stream
|
||||
xœs
|
||||
áRPÐw3T02QI²Í<C2B2>€È@!$ÈÖÈ/I,É×TÉâr
á |