aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2019-11-09 17:54:16 +0100
committerJay Berkenbilt <ejb@ql.org>2019-11-09 19:23:12 +0100
commitc4478e5249f935abe852b11275ffe48c29d8f997 (patch)
treee797874704e755a99eb170c2d6fe0b0ace03df52
parentc9cc83621bf383a135699e2c952713eb592ebcb7 (diff)
downloadqpdf-c4478e5249f935abe852b11275ffe48c29d8f997.tar.zst
Allow odd/even modifiers in numeric range (fixes #364)
-rw-r--r--libqpdf/QUtil.cc35
-rw-r--r--libtests/qtest/numrange.test18
-rw-r--r--manual/qpdf-manual.xml28
-rw-r--r--qpdf/qpdf.cc5
4 files changed, 81 insertions, 5 deletions
diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc
index db52bdb3..5fda01c9 100644
--- a/libqpdf/QUtil.cc
+++ b/libqpdf/QUtil.cc
@@ -1116,6 +1116,8 @@ QUtil::parse_numrange(char const* range, int max)
std::vector<int> work;
static int const comma = -1;
static int const dash = -2;
+ size_t start_idx = 0;
+ size_t skip = 1;
enum { st_top,
st_in_number,
@@ -1182,6 +1184,14 @@ QUtil::parse_numrange(char const* range, int max)
work.push_back(dash);
}
}
+ else if (ch == ':')
+ {
+ if (! ((state == st_in_number) || (state == st_after_number)))
+ {
+ throw std::runtime_error("unexpected colon");
+ }
+ break;
+ }
else
{
throw std::runtime_error("unexpected character");
@@ -1197,6 +1207,22 @@ QUtil::parse_numrange(char const* range, int max)
{
throw std::runtime_error("number expected");
}
+ if (*p == ':')
+ {
+ if (strcmp(p, ":odd") == 0)
+ {
+ skip = 2;
+ }
+ else if (strcmp(p, ":even") == 0)
+ {
+ skip = 2;
+ start_idx = 1;
+ }
+ else
+ {
+ throw std::runtime_error("unexpected even/odd modifier");
+ }
+ }
p = 0;
for (size_t i = 0; i < work.size(); i += 2)
@@ -1245,6 +1271,15 @@ QUtil::parse_numrange(char const* range, int max)
}
}
}
+ if ((start_idx > 0) || (skip != 1))
+ {
+ auto t = result;
+ result.clear();
+ for (size_t i = start_idx; i < t.size(); i += skip)
+ {
+ result.push_back(t.at(i));
+ }
+ }
}
catch (std::runtime_error const& e)
{
diff --git a/libtests/qtest/numrange.test b/libtests/qtest/numrange.test
index 9acf6ea4..896c44d2 100644
--- a/libtests/qtest/numrange.test
+++ b/libtests/qtest/numrange.test
@@ -49,6 +49,24 @@ my @nrange_tests = (
"numeric range r1-r15" .
" -> 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1",
0],
+ ["1-10:quack",
+ "error at * in numeric range 1-10*:quack: unexpected even/odd modifier",
+ 2],
+ ["1-10:",
+ "error at * in numeric range 1-10*:: unexpected even/odd modifier",
+ 2],
+ ["1-10,r:",
+ "error at * in numeric range 1-10,r*:: unexpected even/odd modifier",
+ 2],
+ ["1-10,:",
+ "error at * in numeric range 1-10,*:: unexpected colon",
+ 2],
+ ["1-6,8-12:odd",
+ "numeric range 1-6,8-12:odd -> 1 3 5 8 10 12",
+ 0],
+ ["1-6,8-12:even",
+ "numeric range 1-6,8-12:even -> 2 4 6 9 11",
+ 0],
);
foreach my $d (@nrange_tests)
{
diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml
index 58d21435..c332bedc 100644
--- a/manual/qpdf-manual.xml
+++ b/manual/qpdf-manual.xml
@@ -1391,9 +1391,12 @@ make
<literal>r3-r1</literal> would be the last three pages of the
document. Pages can appear in any order. Ranges can appear with a
high number followed by a low number, which causes the pages to
- appear 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.
+ appear in reverse. Numbers may be repeated in a page range. A page
+ range may be optionally appended with <literal>:even</literal> or
+ <literal>:odd</literal> to indicate only the even or odd pages in
+ the given range. Note that even and odd refer to the positions
+ within the specified, range, not whether the original number is
+ even or odd.
</para>
<para>
Example page ranges:
@@ -1420,6 +1423,18 @@ make
in reverse order
</para>
</listitem>
+ <listitem>
+ <para>
+ <literal>1-20:even</literal>: even pages from 2 to 20
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>5,7-9,12:odd</literal>: pages 5, 8, and, 12, which are
+ the pages in odd positions from among the original range, which
+ represents pages 5, 7, 8, 9, and 12.
+ </para>
+ </listitem>
</itemizedlist>
</para>
<para>
@@ -4663,6 +4678,13 @@ print "\n";
<xref linkend="ref.crypto"/>.
</para>
</listitem>
+ <listitem>
+ <para>
+ Allow <literal>:even</literal> or <literal>:odd</literal> to
+ be appended to numeric ranges for specification of the even
+ or odd pages from among the pages specified in the range.
+ </para>
+ </listitem>
</itemizedlist>
</listitem>
</itemizedlist>
diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc
index 2bd98ccb..0e634412 100644
--- a/qpdf/qpdf.cc
+++ b/qpdf/qpdf.cc
@@ -1286,8 +1286,9 @@ ArgParser::argHelp()
<< "to count from the end, so \"r3-r1\" would be the last three pages of the\n"
<< "document. Pages can appear in any order. Ranges can appear with a\n"
<< "high number followed by a low number, which causes the pages to appear in\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"
+ << "reverse. Numbers may be repeated. A page range may be appended with :odd\n"
+ << "to indicate odd pages in the selected range or :even to indicate even\n"
+ << "pages.\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"