2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-05-31 09:20:52 +00:00

Support "r" in page ranges (fixes #155)

This commit is contained in:
Jay Berkenbilt 2018-03-04 06:59:12 -05:00
parent e577dfc87e
commit 666f794393
4 changed files with 75 additions and 15 deletions

View File

@ -1,3 +1,10 @@
2018-03-04 Jay Berkenbilt <ejb@ql.org>
* On the command line when specifying page ranges, support
preceding a page number by "r" to indicate that it should be
counted from the end. For example, the range r3-r1 would indicate
the last three pages of a document.
2018-03-03 Jay Berkenbilt <ejb@ql.org> 2018-03-03 Jay Berkenbilt <ejb@ql.org>
* Ignore zlib data check errors while uncompressing streams. This * Ignore zlib data check errors while uncompressing streams. This

View File

@ -818,13 +818,15 @@ make
</para> </para>
<para> <para>
The page range is a set of numbers separated by commas, ranges of The page range is a set of numbers separated by commas, ranges of
numbers separated dashes, or combinations of those. The character numbers separated dashes, or combinations of those. The character
&ldquo;z&rdquo; represents the last page. Pages can appear in any &ldquo;z&rdquo; represents the last page. A number preceded by an
order. Ranges can appear with a high number followed by a low &ldquo;r&rdquo; indicates to count from the end, so
number, which causes the pages to appear in reverse. Repeating a &ldquo;r3-r1&rdquo; would be the last three pages of the document.
number will cause an error, but you can use the workaround Pages can appear in any order. Ranges can appear with a high
discussed above should you really want to include the same page number followed by a low number, which causes the pages to appear
twice. in reverse. Repeating a number will cause an error, but you can
use the workaround discussed above should you really want to
include the same page twice.
</para> </para>
<para> <para>
Example page ranges: Example page ranges:
@ -840,6 +842,17 @@ make
<literal>z-1</literal>: all pages in the document in reverse <literal>z-1</literal>: all pages in the document in reverse
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<literal>r3-r1</literal>: the last three pages of the document
</para>
</listitem>
<listitem>
<para>
<literal>r1-r3</literal>: the last three pages of the document
in reverse order
</para>
</listitem>
</itemizedlist> </itemizedlist>
</para> </para>
<para> <para>

View File

@ -358,11 +358,12 @@ input.\n\
\n\ \n\
The page range is a set of numbers separated by commas, ranges of\n\ The page range is a set of numbers separated by commas, ranges of\n\
numbers separated dashes, or combinations of those. The character\n\ numbers separated dashes, or combinations of those. The character\n\
\"z\" represents the last page. Pages can appear in any order. Ranges\n\ \"z\" represents the last page. A number preceded by an \"r\" indicates\n\
can appear with a high number followed by a low number, which causes the\n\ to count from the end, so \"r3-r1\" would be the last three pages of the\n\
pages to appear in reverse. Repeating a number will cause an error, but\n\ document. Pages can appear in any order. Ranges can appear with a\n\
the manual discusses a workaround should you really want to include the\n\ high number followed by a low number, which causes the pages to appear in\n\
same page twice.\n\ reverse. Repeating a number will cause an error, but the manual discusses\n\
a workaround should you really want to include the same page twice.\n\
\n\ \n\
If the page range is omitted, the range of 1-z is assumed. qpdf decides\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\ that the page range is omitted if the range argument is either -- or a\n\
@ -577,6 +578,22 @@ static void show_encryption(QPDF& pdf, Options& o)
} }
} }
static int maybe_from_end(int num, bool from_end, int max)
{
if (from_end)
{
if (num > max)
{
num = 0;
}
else
{
num = max + 1 - num;
}
}
return num;
}
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) bool throw_error = false)
{ {
@ -593,6 +610,7 @@ static std::vector<int> parse_numrange(char const* range, int max,
st_after_number } state = st_top; st_after_number } state = st_top;
bool last_separator_was_dash = false; bool last_separator_was_dash = false;
int cur_number = 0; int cur_number = 0;
bool from_end = false;
while (*p) while (*p)
{ {
char ch = *p; char ch = *p;
@ -616,14 +634,25 @@ static std::vector<int> parse_numrange(char const* range, int max,
state = st_after_number; state = st_after_number;
cur_number = max; cur_number = max;
} }
else if (ch == 'r')
{
if (! (state == st_top))
{
throw std::runtime_error("r not expected");
}
state = st_in_number;
from_end = true;
}
else if ((ch == ',') || (ch == '-')) else if ((ch == ',') || (ch == '-'))
{ {
if (! ((state == st_in_number) || (state == st_after_number))) if (! ((state == st_in_number) || (state == st_after_number)))
{ {
throw std::runtime_error("unexpected separator"); throw std::runtime_error("unexpected separator");
} }
cur_number = maybe_from_end(cur_number, from_end, max);
work.push_back(cur_number); work.push_back(cur_number);
cur_number = 0; cur_number = 0;
from_end = false;
if (ch == ',') if (ch == ',')
{ {
state = st_top; state = st_top;
@ -649,6 +678,7 @@ static std::vector<int> parse_numrange(char const* range, int max,
} }
if ((state == st_in_number) || (state == st_after_number)) if ((state == st_in_number) || (state == st_after_number))
{ {
cur_number = maybe_from_end(cur_number, from_end, max);
work.push_back(cur_number); work.push_back(cur_number);
} }
else else

View File

@ -1092,9 +1092,19 @@ my @nrange_tests = (
["1-10,1234,5", ["1-10,1234,5",
"qpdf: error in numeric range 1-10,1234,5: number 1234 out of range", "qpdf: error in numeric range 1-10,1234,5: number 1234 out of range",
2], 2],
["1,3,5-10,z-13,13,9,z,2", ["1,r,3",
"numeric range 1,3,5-10,z-13,13,9,z,2" . "qpdf: error in numeric range 1,r,3: number 16 out of range",
" -> 1 3 5 6 7 8 9 10 15 14 13 13 9 15 2", 2],
["1,r16,3",
"qpdf: error in numeric range 1,r16,3: number 0 out of range",
2],
["1,3,5-10,z-13,13,9,z,2,r2-r4",
"numeric range 1,3,5-10,z-13,13,9,z,2,r2-r4" .
" -> 1 3 5 6 7 8 9 10 15 14 13 13 9 15 2 14 13 12",
0],
["r1-r15", # r\d+ at end
"numeric range r1-r15" .
" -> 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1",
0], 0],
); );
$n_tests += scalar(@nrange_tests); $n_tests += scalar(@nrange_tests);