Commit c4478e5249f935abe852b11275ffe48c29d8f997
1 parent
c9cc8362
Allow odd/even modifiers in numeric range (fixes #364)
Showing
4 changed files
with
81 additions
and
5 deletions
libqpdf/QUtil.cc
| ... | ... | @@ -1116,6 +1116,8 @@ QUtil::parse_numrange(char const* range, int max) |
| 1116 | 1116 | std::vector<int> work; |
| 1117 | 1117 | static int const comma = -1; |
| 1118 | 1118 | static int const dash = -2; |
| 1119 | + size_t start_idx = 0; | |
| 1120 | + size_t skip = 1; | |
| 1119 | 1121 | |
| 1120 | 1122 | enum { st_top, |
| 1121 | 1123 | st_in_number, |
| ... | ... | @@ -1182,6 +1184,14 @@ QUtil::parse_numrange(char const* range, int max) |
| 1182 | 1184 | work.push_back(dash); |
| 1183 | 1185 | } |
| 1184 | 1186 | } |
| 1187 | + else if (ch == ':') | |
| 1188 | + { | |
| 1189 | + if (! ((state == st_in_number) || (state == st_after_number))) | |
| 1190 | + { | |
| 1191 | + throw std::runtime_error("unexpected colon"); | |
| 1192 | + } | |
| 1193 | + break; | |
| 1194 | + } | |
| 1185 | 1195 | else |
| 1186 | 1196 | { |
| 1187 | 1197 | throw std::runtime_error("unexpected character"); |
| ... | ... | @@ -1197,6 +1207,22 @@ QUtil::parse_numrange(char const* range, int max) |
| 1197 | 1207 | { |
| 1198 | 1208 | throw std::runtime_error("number expected"); |
| 1199 | 1209 | } |
| 1210 | + if (*p == ':') | |
| 1211 | + { | |
| 1212 | + if (strcmp(p, ":odd") == 0) | |
| 1213 | + { | |
| 1214 | + skip = 2; | |
| 1215 | + } | |
| 1216 | + else if (strcmp(p, ":even") == 0) | |
| 1217 | + { | |
| 1218 | + skip = 2; | |
| 1219 | + start_idx = 1; | |
| 1220 | + } | |
| 1221 | + else | |
| 1222 | + { | |
| 1223 | + throw std::runtime_error("unexpected even/odd modifier"); | |
| 1224 | + } | |
| 1225 | + } | |
| 1200 | 1226 | |
| 1201 | 1227 | p = 0; |
| 1202 | 1228 | for (size_t i = 0; i < work.size(); i += 2) |
| ... | ... | @@ -1245,6 +1271,15 @@ QUtil::parse_numrange(char const* range, int max) |
| 1245 | 1271 | } |
| 1246 | 1272 | } |
| 1247 | 1273 | } |
| 1274 | + if ((start_idx > 0) || (skip != 1)) | |
| 1275 | + { | |
| 1276 | + auto t = result; | |
| 1277 | + result.clear(); | |
| 1278 | + for (size_t i = start_idx; i < t.size(); i += skip) | |
| 1279 | + { | |
| 1280 | + result.push_back(t.at(i)); | |
| 1281 | + } | |
| 1282 | + } | |
| 1248 | 1283 | } |
| 1249 | 1284 | catch (std::runtime_error const& e) |
| 1250 | 1285 | { | ... | ... |
libtests/qtest/numrange.test
| ... | ... | @@ -49,6 +49,24 @@ my @nrange_tests = ( |
| 49 | 49 | "numeric range r1-r15" . |
| 50 | 50 | " -> 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1", |
| 51 | 51 | 0], |
| 52 | + ["1-10:quack", | |
| 53 | + "error at * in numeric range 1-10*:quack: unexpected even/odd modifier", | |
| 54 | + 2], | |
| 55 | + ["1-10:", | |
| 56 | + "error at * in numeric range 1-10*:: unexpected even/odd modifier", | |
| 57 | + 2], | |
| 58 | + ["1-10,r:", | |
| 59 | + "error at * in numeric range 1-10,r*:: unexpected even/odd modifier", | |
| 60 | + 2], | |
| 61 | + ["1-10,:", | |
| 62 | + "error at * in numeric range 1-10,*:: unexpected colon", | |
| 63 | + 2], | |
| 64 | + ["1-6,8-12:odd", | |
| 65 | + "numeric range 1-6,8-12:odd -> 1 3 5 8 10 12", | |
| 66 | + 0], | |
| 67 | + ["1-6,8-12:even", | |
| 68 | + "numeric range 1-6,8-12:even -> 2 4 6 9 11", | |
| 69 | + 0], | |
| 52 | 70 | ); |
| 53 | 71 | foreach my $d (@nrange_tests) |
| 54 | 72 | { | ... | ... |
manual/qpdf-manual.xml
| ... | ... | @@ -1391,9 +1391,12 @@ make |
| 1391 | 1391 | <literal>r3-r1</literal> would be the last three pages of the |
| 1392 | 1392 | document. Pages can appear in any order. Ranges can appear with a |
| 1393 | 1393 | high number followed by a low number, which causes the pages to |
| 1394 | - appear in reverse. Repeating a number will cause an error, but you | |
| 1395 | - can use the workaround discussed above should you really want to | |
| 1396 | - include the same page twice. | |
| 1394 | + appear in reverse. Numbers may be repeated in a page range. A page | |
| 1395 | + range may be optionally appended with <literal>:even</literal> or | |
| 1396 | + <literal>:odd</literal> to indicate only the even or odd pages in | |
| 1397 | + the given range. Note that even and odd refer to the positions | |
| 1398 | + within the specified, range, not whether the original number is | |
| 1399 | + even or odd. | |
| 1397 | 1400 | </para> |
| 1398 | 1401 | <para> |
| 1399 | 1402 | Example page ranges: |
| ... | ... | @@ -1420,6 +1423,18 @@ make |
| 1420 | 1423 | in reverse order |
| 1421 | 1424 | </para> |
| 1422 | 1425 | </listitem> |
| 1426 | + <listitem> | |
| 1427 | + <para> | |
| 1428 | + <literal>1-20:even</literal>: even pages from 2 to 20 | |
| 1429 | + </para> | |
| 1430 | + </listitem> | |
| 1431 | + <listitem> | |
| 1432 | + <para> | |
| 1433 | + <literal>5,7-9,12:odd</literal>: pages 5, 8, and, 12, which are | |
| 1434 | + the pages in odd positions from among the original range, which | |
| 1435 | + represents pages 5, 7, 8, 9, and 12. | |
| 1436 | + </para> | |
| 1437 | + </listitem> | |
| 1423 | 1438 | </itemizedlist> |
| 1424 | 1439 | </para> |
| 1425 | 1440 | <para> |
| ... | ... | @@ -4663,6 +4678,13 @@ print "\n"; |
| 4663 | 4678 | <xref linkend="ref.crypto"/>. |
| 4664 | 4679 | </para> |
| 4665 | 4680 | </listitem> |
| 4681 | + <listitem> | |
| 4682 | + <para> | |
| 4683 | + Allow <literal>:even</literal> or <literal>:odd</literal> to | |
| 4684 | + be appended to numeric ranges for specification of the even | |
| 4685 | + or odd pages from among the pages specified in the range. | |
| 4686 | + </para> | |
| 4687 | + </listitem> | |
| 4666 | 4688 | </itemizedlist> |
| 4667 | 4689 | </listitem> |
| 4668 | 4690 | </itemizedlist> | ... | ... |
qpdf/qpdf.cc
| ... | ... | @@ -1286,8 +1286,9 @@ ArgParser::argHelp() |
| 1286 | 1286 | << "to count from the end, so \"r3-r1\" would be the last three pages of the\n" |
| 1287 | 1287 | << "document. Pages can appear in any order. Ranges can appear with a\n" |
| 1288 | 1288 | << "high number followed by a low number, which causes the pages to appear in\n" |
| 1289 | - << "reverse. Repeating a number will cause an error, but the manual discusses\n" | |
| 1290 | - << "a workaround should you really want to include the same page twice.\n" | |
| 1289 | + << "reverse. Numbers may be repeated. A page range may be appended with :odd\n" | |
| 1290 | + << "to indicate odd pages in the selected range or :even to indicate even\n" | |
| 1291 | + << "pages.\n" | |
| 1291 | 1292 | << "\n" |
| 1292 | 1293 | << "If the page range is omitted, the range of 1-z is assumed. qpdf decides\n" |
| 1293 | 1294 | << "that the page range is omitted if the range argument is either -- or a\n" | ... | ... |