Commit d8d70ecca264fa5c681dca992a03cfa4c46b3f43

Authored by Jay Berkenbilt
1 parent 9db5d75b

Support comma-separated numeric values with --collate (fixes #505)

ChangeLog
1 1 2024-01-01 Jay Berkenbilt <ejb@ql.org>
2 2  
  3 + * Support comma-separated numeric values with --collate to select
  4 + different group sizes from different files. Fixes #505.
  5 +
3 6 * Support "x" before a group in a numeric range to exclude a group
4   - from the previous group. Details are in the manual.
  7 + from the previous group. Details are in the manual. Fixes #564,
  8 + #790.
5 9  
6 10 2023-12-29 Jay Berkenbilt <ejb@ql.org>
7 11  
... ...
include/qpdf/QPDFJob.hh
... ... @@ -637,7 +637,7 @@ class QPDFJob
637 637 bool show_filtered_stream_data{false};
638 638 bool show_pages{false};
639 639 bool show_page_images{false};
640   - size_t collate{0};
  640 + std::vector<size_t> collate;
641 641 bool flatten_rotation{false};
642 642 bool list_attachments{false};
643 643 std::string attachment_to_show;
... ...
job.sums
... ... @@ -9,12 +9,12 @@ include/qpdf/auto_job_c_pages.hh b3cc0f21029f6d89efa043dcdbfa183cb59325b6506001c
9 9 include/qpdf/auto_job_c_uo.hh ae21b69a1efa9333050f4833d465f6daff87e5b38e5106e49bbef5d4132e4ed1
10 10 job.yml 4f89fc7b622df897d30d403d8035aa36fc7de8d8c43042c736e0300d904cb05c
11 11 libqpdf/qpdf/auto_job_decl.hh 9c6f701c29f3f764d620186bed92685a2edf2e4d11e4f4532862c05470cfc4d2
12   -libqpdf/qpdf/auto_job_help.hh 838f4065f64dc3fbd493510fd21d8ab4e16ee2434592776f44f80cbe3045cb50
  12 +libqpdf/qpdf/auto_job_help.hh ea0d0cdebeb190d305bd5f9bca85a4430dbcfa0881ac9be839216b878765b379
13 13 libqpdf/qpdf/auto_job_init.hh b4c2b3724fba61f1206fd3bae81951636852592f67a63ef9539839c2c5995065
14 14 libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a9474319fb7c86d92634cc8297
15 15 libqpdf/qpdf/auto_job_json_init.hh f5acb9aa103131cb68dec0e12c4d237a6459bdb49b24773c24f0c2724a462b8f
16 16 libqpdf/qpdf/auto_job_schema.hh b53c006fec2e75b1b73588d242d49a32f7d3db820b1541de106c5d4c27fbb4d9
17 17 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580
18   -manual/cli.rst d6d1ca82c936ffeaf137c586f988f80043db4c3b226d26fdf94f19a6005d012e
19   -manual/qpdf.1 10dc52d32a6d8885ce4e4292875ee7fe8e7a826ef3fc28db5671be413bcaacc7
  18 +manual/cli.rst fc8488129c479b6cde9dffbbddb150bc1ffb45bc38b3bff2c5dba4378f0edb67
  19 +manual/qpdf.1 738dc9b732ad4c880d034b99f957077628fde1d0006943aaf813e98f8e2f9635
20 20 manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b
... ...
libqpdf/QPDFJob.cc
1 1 #include <qpdf/QPDFJob.hh>
2 2  
3   -#include <cctype>
4   -#include <cstdio>
5   -#include <cstdlib>
6 3 #include <cstring>
7   -#include <fcntl.h>
8 4 #include <iostream>
9 5 #include <memory>
10 6  
... ... @@ -14,13 +10,11 @@
14 10 #include <qpdf/Pl_DCT.hh>
15 11 #include <qpdf/Pl_Discard.hh>
16 12 #include <qpdf/Pl_Flate.hh>
17   -#include <qpdf/Pl_OStream.hh>
18 13 #include <qpdf/Pl_StdioFile.hh>
19 14 #include <qpdf/Pl_String.hh>
20 15 #include <qpdf/QIntC.hh>
21 16 #include <qpdf/QPDF.hh>
22 17 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
23   -#include <qpdf/QPDFArgParser.hh>
24 18 #include <qpdf/QPDFCryptoProvider.hh>
25 19 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
26 20 #include <qpdf/QPDFExc.hh>
... ... @@ -2419,26 +2413,32 @@ QPDFJob::handlePageSpecs(QPDF&amp; pdf, std::vector&lt;std::unique_ptr&lt;QPDF&gt;&gt;&amp; page_hea
2419 2413 dh.removePage(page);
2420 2414 }
2421 2415  
2422   - if (m->collate && (parsed_specs.size() > 1)) {
  2416 + auto n_collate = m->collate.size();
  2417 + auto n_specs = parsed_specs.size();
  2418 + if (n_collate > 0 && n_specs > 1) {
2423 2419 // Collate the pages by selecting one page from each spec in order. When a spec runs out of
2424 2420 // pages, stop selecting from it.
2425 2421 std::vector<QPDFPageData> new_parsed_specs;
2426   - size_t nspecs = parsed_specs.size();
2427   - size_t cur_page = 0;
  2422 + // Make sure we have a collate value for each spec. We have already checked that a non-empty
  2423 + // collate has either one value or one value per spec.
  2424 + for (auto i = n_collate; i < n_specs; ++i) {
  2425 + m->collate.push_back(m->collate.at(0));
  2426 + }
  2427 + std::vector<size_t> cur_page(n_specs, 0);
2428 2428 bool got_pages = true;
2429 2429 while (got_pages) {
2430 2430 got_pages = false;
2431   - for (size_t i = 0; i < nspecs; ++i) {
  2431 + for (size_t i = 0; i < n_specs; ++i) {
2432 2432 QPDFPageData& page_data = parsed_specs.at(i);
2433   - for (size_t j = 0; j < m->collate; ++j) {
2434   - if (cur_page + j < page_data.selected_pages.size()) {
  2433 + for (size_t j = 0; j < m->collate.at(i); ++j) {
  2434 + if (cur_page.at(i) + j < page_data.selected_pages.size()) {
2435 2435 got_pages = true;
2436 2436 new_parsed_specs.emplace_back(
2437   - page_data, page_data.selected_pages.at(cur_page + j));
  2437 + page_data, page_data.selected_pages.at(cur_page.at(i) + j));
2438 2438 }
2439 2439 }
  2440 + cur_page.at(i) += m->collate.at(i);
2440 2441 }
2441   - cur_page += m->collate;
2442 2442 }
2443 2443 parsed_specs = new_parsed_specs;
2444 2444 }
... ... @@ -3019,9 +3019,10 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3019 3019 try {
3020 3020 QUtil::remove_file(backup.c_str());
3021 3021 } catch (QPDFSystemError& e) {
3022   - *m->log->getError() << m->message_prefix << ": unable to delete original file ("
3023   - << e.what() << ");" << " original file left in " << backup
3024   - << ", but the input was successfully replaced\n";
  3022 + *m->log->getError()
  3023 + << m->message_prefix << ": unable to delete original file (" << e.what() << ");"
  3024 + << " original file left in " << backup
  3025 + << ", but the input was successfully replaced\n";
3025 3026 }
3026 3027 }
3027 3028 }
... ...
libqpdf/QPDFJob_config.cc
... ... @@ -98,8 +98,27 @@ QPDFJob::Config::collate()
98 98 QPDFJob::Config*
99 99 QPDFJob::Config::collate(std::string const& parameter)
100 100 {
101   - auto n = (parameter.empty() ? 1 : QUtil::string_to_uint(parameter.c_str()));
102   - o.m->collate = QIntC::to_size(n);
  101 + if (parameter.empty()) {
  102 + o.m->collate.push_back(1);
  103 + return this;
  104 + }
  105 + size_t pos = 0;
  106 + // Parse a,b,c
  107 + while (true) {
  108 + auto end = parameter.find(',', pos);
  109 + auto n = parameter.substr(pos, end);
  110 + if (n.empty()) {
  111 + usage("--collate: trailing comma");
  112 + }
  113 + o.m->collate.push_back(QIntC::to_size(QUtil::string_to_uint(n.c_str())));
  114 + if (end == std::string::npos) {
  115 + break;
  116 + }
  117 + pos = end + 1;
  118 + }
  119 + if (o.m->collate.empty()) {
  120 + o.m->collate.push_back(1);
  121 + }
103 122 return this;
104 123 }
105 124  
... ... @@ -932,9 +951,15 @@ QPDFJob::Config::pages()
932 951 QPDFJob::Config*
933 952 QPDFJob::PagesConfig::endPages()
934 953 {
935   - if (this->config->o.m->page_specs.empty()) {
  954 + auto n_specs = config->o.m->page_specs.size();
  955 + if (n_specs == 0) {
936 956 usage("--pages: no page specifications given");
937 957 }
  958 + auto n_collate = config->o.m->collate.size();
  959 + if (!(n_collate == 0 || n_collate == 1 || n_collate == n_specs)) {
  960 + usage("--pages: if --collate has more than one value, it must have one value per page "
  961 + "specification");
  962 + }
938 963 return this->config;
939 964 }
940 965  
... ...
libqpdf/qpdf/auto_job_help.hh
... ... @@ -315,11 +315,13 @@ ap.addOptionHelp(&quot;--pages&quot;, &quot;modification&quot;, &quot;begin page selection&quot;, R&quot;(--pages f
315 315  
316 316 Run qpdf --help=page-selection for details.
317 317 )");
318   -ap.addOptionHelp("--collate", "modification", "collate with --pages", R"(--collate[=n]
  318 +ap.addOptionHelp("--collate", "modification", "collate with --pages", R"(--collate[=n[,m,...]]
319 319  
320 320 Collate rather than concatenate pages specified with --pages.
321 321 With a numeric parameter, collate in groups of n. The default
322   -is 1. Run qpdf --help=page-selection for additional details.
  322 +is 1. With comma-separated numeric parameters, take n from the
  323 +first file, m from the second, etc. Run
  324 +qpdf --help=page-selection for additional details.
323 325 )");
324 326 ap.addOptionHelp("--split-pages", "modification", "write pages to separate files", R"(--split-pages[=n]
325 327  
... ... @@ -607,6 +609,8 @@ Run qpdf --help=page-ranges for help with page ranges.
607 609  
608 610 Use --collate=n to cause pages to be collated in groups of n pages
609 611 (default 1) instead of concatenating the input.
  612 +Use --collate=i,j,k,... to take i from the first, then j from the
  613 +second, then k from the third, then i from the first, etc.
610 614  
611 615 Examples:
612 616  
... ...
manual/cli.rst
... ... @@ -1403,18 +1403,21 @@ Related Options
1403 1403 See also :qpdf:ref:`--split-pages`, :qpdf:ref:`--collate`,
1404 1404 :ref:`page-ranges`.
1405 1405  
1406   -.. qpdf:option:: --collate[=n]
  1406 +.. qpdf:option:: --collate[=n[,m,...]]
1407 1407  
1408 1408 .. help: collate with --pages
1409 1409  
1410 1410 Collate rather than concatenate pages specified with --pages.
1411 1411 With a numeric parameter, collate in groups of n. The default
1412   - is 1. Run qpdf --help=page-selection for additional details.
  1412 + is 1. With comma-separated numeric parameters, take n from the
  1413 + first file, m from the second, etc. Run
  1414 + qpdf --help=page-selection for additional details.
1413 1415  
1414 1416 This option causes :command:`qpdf` to collate rather than
1415 1417 concatenate pages specified with :qpdf:ref:`--pages`. With a
1416 1418 numeric parameter, collate in groups of :samp:`{n}`. The default
1417   - is 1.
  1419 + is 1. With comma-separated numeric parameters, take :samp:`{n}`
  1420 + from the first file, :samp:`{m}` from the second, etc.
1418 1421  
1419 1422 Please see :ref:`page-selection` for additional details.
1420 1423  
... ... @@ -2335,6 +2338,8 @@ Page Selection
2335 2338  
2336 2339 Use --collate=n to cause pages to be collated in groups of n pages
2337 2340 (default 1) instead of concatenating the input.
  2341 + Use --collate=i,j,k,... to take i from the first, then j from the
  2342 + second, then k from the third, then i from the first, etc.
2338 2343  
2339 2344 Examples:
2340 2345  
... ... @@ -2383,9 +2388,13 @@ Notes:
2383 2388 See :ref:`page-ranges` for help on specifying a page range.
2384 2389  
2385 2390 Use :samp:`--collate={n}` to cause pages to be collated in groups of
2386   -:samp:`{n}` pages (default 1) instead of concatenating the input. Note
2387   -that the :qpdf:ref:`--collate` appears outside of ``--pages ... --``
2388   -(before ``--pages`` or after ``--``). Pages are pulled from each
  2391 +:samp:`{n}` pages (default 1) instead of concatenating the input. Use
  2392 +:samp:`--collate={i},{j},{k},...` to take :samp:`{i}` from the first,
  2393 +then :samp:`{j}` from the second, then :samp:`{k}` from the third,
  2394 +then :samp:`{i}` from the first, etc.
  2395 +
  2396 +Note that the :qpdf:ref:`--collate` appears outside of ``--pages ...
  2397 +--`` (before ``--pages`` or after ``--``). Pages are pulled from each
2389 2398 document in turn. When a document is out of pages, it is skipped. See
2390 2399 examples below.
2391 2400  
... ... @@ -2481,6 +2490,34 @@ Examples
2481 2490  
2482 2491 - a.pdf page 5
2483 2492  
  2493 +- You can specify a multiple numeric parameters to :qpdf:ref:`--collate`. With
  2494 + :samp:`--collate={i,j,k}`, pull groups of :samp:`{i}` pages from the
  2495 + first file, then :samp:`{j}` from the second, thenm :samp:`{k}` from
  2496 + the third, repeating. The number of parameters must equal the number
  2497 + of groups. For example, if you ran
  2498 +
  2499 + ::
  2500 +
  2501 + qpdf --collate=2,1,3 --empty --pages a.pdf 1-5 b.pdf 6-4 c.pdf r1-r4 -- out.pdf
  2502 +
  2503 + you would get the following pages in this order:
  2504 +
  2505 + - a.pdf pages 1 and 2
  2506 +
  2507 + - b.pdf page 6
  2508 +
  2509 + - c.pdf last three pages in reverse order
  2510 +
  2511 + - a.pdf pages 3 and 4
  2512 +
  2513 + - b.pdf page 5
  2514 +
  2515 + - c.pdf fourth to last page
  2516 +
  2517 + - a.pdf page 5
  2518 +
  2519 + - b.pdf page 4
  2520 +
2484 2521 - Take pages 1 through 5 from :file:`file1.pdf` and pages 11 through
2485 2522 15 in reverse from :file:`file2.pdf`, taking document-level metadata
2486 2523 from :file:`file2.pdf`.
... ...
manual/qpdf.1
... ... @@ -414,11 +414,13 @@ Related Options:
414 414 Run qpdf --help=page-selection for details.
415 415 .TP
416 416 .B --collate \-\- collate with --pages
417   ---collate[=n]
  417 +--collate[=n[,m,...]]
418 418  
419 419 Collate rather than concatenate pages specified with --pages.
420 420 With a numeric parameter, collate in groups of n. The default
421   -is 1. Run qpdf --help=page-selection for additional details.
  421 +is 1. With comma-separated numeric parameters, take n from the
  422 +first file, m from the second, etc. Run
  423 +qpdf --help=page-selection for additional details.
422 424 .TP
423 425 .B --split-pages \-\- write pages to separate files
424 426 --split-pages[=n]
... ... @@ -737,6 +739,8 @@ Run qpdf --help=page-ranges for help with page ranges.
737 739  
738 740 Use --collate=n to cause pages to be collated in groups of n pages
739 741 (default 1) instead of concatenating the input.
  742 +Use --collate=i,j,k,... to take i from the first, then j from the
  743 +second, then k from the third, then i from the first, etc.
740 744  
741 745 Examples:
742 746  
... ...
manual/release-notes.rst
... ... @@ -50,6 +50,10 @@ Planned changes for future 12.x (subject to change):
50 50 of pages within a page range. See :ref:`page-ranges` for
51 51 details.
52 52  
  53 + - Support comma-separated numeric values with
  54 + :qpdf:ref:`--collate` to select different numbers of pages from
  55 + different groups.
  56 +
53 57 11.7.0: December 24, 2023
54 58 - Bug fixes:
55 59  
... ...
qpdf/qtest/arg-parsing.test
... ... @@ -15,7 +15,7 @@ cleanup();
15 15  
16 16 my $td = new TestDriver('arg-parsing');
17 17  
18   -my $n_tests = 22;
  18 +my $n_tests = 23;
19 19  
20 20 $td->runtest("required argument",
21 21 {$td->COMMAND => "qpdf --password minimal.pdf"},
... ... @@ -94,6 +94,14 @@ $td-&gt;runtest(&quot;v2-only qpdf json-key&quot;,
94 94 {$td->REGEXP => ".*\"qpdf\" is only valid for json version > 1.*",
95 95 $td->EXIT_STATUS => 2},
96 96 $td->NORMALIZE_NEWLINES);
  97 +$td->runtest("wrong number of collate args",
  98 + {$td->COMMAND =>
  99 + "qpdf --collate=2,3 collate-odd.pdf" .
  100 + " --pages . minimal.pdf collate-even.pdf -- a.pdf"},
  101 + {$td->REGEXP => ".*--collate has more than one value.*",
  102 + $td->EXIT_STATUS => 2},
  103 + $td->NORMALIZE_NEWLINES);
  104 +
97 105 # Ignoring -- at the top level was never intended but turned out to
98 106 # have been there for a long time so that people relied on it. It is
99 107 # intentionally not documented.
... ...
qpdf/qtest/collate.test
... ... @@ -17,9 +17,11 @@ my $td = new TestDriver(&#39;collate&#39;);
17 17 my @collate = (
18 18 ["", "three-files", "collate-odd",
19 19 "collate-odd.pdf 1-5 minimal.pdf collate-even.pdf 7-1"],
20   - [1, "three-files", "collate-odd",
  20 + ["1", "three-files", "collate-odd",
21 21 "collate-odd.pdf 1-5 minimal.pdf collate-even.pdf 7-1"],
22   - [2, "three-files-2", "collate-odd",
  22 + ["2", "three-files-2", "collate-odd",
  23 + "collate-odd.pdf 1-5 minimal.pdf collate-even.pdf 7-1"],
  24 + ["2,3,4", "three-files-2,3,4", "collate-odd",
23 25 "collate-odd.pdf 1-5 minimal.pdf collate-even.pdf 7-1"],
24 26 );
25 27 my $n_tests = 2 * scalar(@collate);
... ... @@ -28,7 +30,7 @@ foreach my $d (@collate)
28 30 {
29 31 my ($n, $description, $first, $args) = @$d;
30 32 my $collate = '--collate';
31   - if ($n)
  33 + if ($n ne "")
32 34 {
33 35 $collate .= "=$n";
34 36 }
... ...
qpdf/qtest/qpdf/three-files-2,3,4-collate-out.pdf 0 โ†’ 100644
  1 +%PDF-1.3
  2 +%ยฟรทยขรพ
  3 +%QDF-1.0
  4 +
  5 +%% Original object ID: 1 0
  6 +1 0 obj
  7 +<<
  8 + /Outlines 2 0 R
  9 + /PageLabels <<
  10 + /Nums [
  11 + 0
  12 + <<
  13 + /P ()
  14 + /St 1
  15 + >>
  16 + 1
  17 + <<
  18 + /S /r
  19 + /St 1
  20 + >>
  21 + 2
  22 + <<
  23 + /St 3
  24 + >>
  25 + 3
  26 + <<
  27 + /S /D
  28 + /St 3
  29 + >>
  30 + 4
  31 + <<
  32 + /P ()
  33 + /St 1
  34 + >>
  35 + 5
  36 + <<
  37 + /S /r
  38 + /St 6
  39 + >>
  40 + 6
  41 + <<
  42 + /P ()
  43 + /St 1
  44 + >>
  45 + 7
  46 + <<
  47 + /S /r
  48 + /St 3
  49 + >>
  50 + 8
  51 + <<
  52 + /S /r
  53 + /St 5
  54 + >>
  55 + 9
  56 + <<
  57 + /S /r
  58 + /St 4
  59 + >>
  60 + 10
  61 + <<
  62 + /S /r
  63 + /St 2
  64 + >>
  65 + 11
  66 + <<
  67 + /P ()
  68 + /St 2
  69 + >>
  70 + 12
  71 + <<
  72 + /P ()
  73 + /St 2
  74 + >>
  75 + ]
  76 + >>
  77 + /PageMode /UseOutlines
  78 + /Pages 3 0 R
  79 + /Type /Catalog
  80 +>>
  81 +endobj
  82 +
  83 +%% Original object ID: 2 0
  84 +2 0 obj
  85 +<<
  86 + /Count 6
  87 + /First 4 0 R
  88 + /Last 5 0 R
  89 + /Type /Outlines
  90 +>>
  91 +endobj
  92 +
  93 +%% Original object ID: 3 0
  94 +3 0 obj
  95 +<<
  96 + /Count 13
  97 + /Kids [
  98 + 6 0 R
  99 + 7 0 R
  100 + 8 0 R
  101 + 9 0 R
  102 + 10 0 R
  103 + 11 0 R
  104 + 12 0 R
  105 + 13 0 R
  106 + 14 0 R
  107 + 15 0 R
  108 + 16 0 R
  109 + 17 0 R
  110 + 18 0 R
  111 + ]
  112 + /Type /Pages
  113 +>>
  114 +endobj
  115 +
  116 +%% Original object ID: 4 0
  117 +4 0 obj
  118 +<<
  119 + /Count 4
  120 + /Dest [
  121 + null
  122 + /XYZ
  123 + null
  124 + null
  125 + null
  126 + ]
  127 + /First 19 0 R
  128 + /Last 20 0 R
  129 + /Next 5 0 R
  130 + /Parent 2 0 R
  131 + /Title (Isรญs 1 -> 5: /XYZ null null null)
  132 + /Type /Outline
  133 +>>
  134 +endobj
  135 +
  136 +%% Original object ID: 5 0
  137 +5 0 obj
  138 +<<
  139 + /Dest [
  140 + null
  141 + /XYZ
  142 + 66
  143 + 756
  144 + 3
  145 + ]
  146 + /Parent 2 0 R
  147 + /Prev 4 0 R
  148 + /Title (Trepak 2 -> 15: /XYZ 66 756 3)
  149 + /Type /Outline
  150 +>>
  151 +endobj
  152 +
  153 +%% Page 1
  154 +%% Original object ID: 6 0
  155 +6 0 obj
  156 +<<
  157 + /Contents 21 0 R
  158 + /MediaBox [
  159 + 0
  160 + 0
  161 + 612
  162 + 792
  163 + ]
  164 + /Parent 3 0 R
  165 + /Resources <<
  166 + /Font <<
  167 + /F1 23 0 R
  168 + >>
  169 + /ProcSet 24 0 R
  170 + >>
  171 + /Type /Page
  172 +>>
  173 +endobj
  174 +
  175 +%% Page 2
  176 +%% Original object ID: 7 0
  177 +7 0 obj
  178 +<<
  179 + /Contents 25 0 R
  180 + /MediaBox [
  181 + 0
  182 + 0
  183 + 612
  184 + 792
  185 + ]
  186 + /Parent 3 0 R
  187 + /Resources <<
  188 + /Font <<
  189 + /F1 23 0 R
  190 + >>
  191 + /ProcSet 24 0 R
  192 + >>
  193 + /Type /Page
  194 +>>
  195 +endobj
  196 +
  197 +%% Page 3
  198 +%% Original object ID: 47 0
  199 +8 0 obj
  200 +<<
  201 + /Contents 27 0 R
  202 + /MediaBox [
  203 + 0
  204 + 0
  205 + 612
  206 + 792
  207 + ]
  208 + /Parent 3 0 R
  209 + /Resources <<
  210 + /Font <<
  211 + /F1 29 0 R
  212 + >>
  213 + /ProcSet 30 0 R
  214 + >>
  215 + /Type /Page
  216 +>>
  217 +endobj
  218 +
  219 +%% Page 4
  220 +%% Original object ID: 51 0
  221 +9 0 obj
  222 +<<
  223 + /Contents 31 0 R
  224 + /MediaBox [
  225 + 0
  226 + 0
  227 + 612
  228 + 792
  229 + ]
  230 + /Parent 3 0 R
  231 + /Resources <<
  232 + /Font <<
  233 + /F1 33 0 R
  234 + >>
  235 + /ProcSet 34 0 R
  236 + >>
  237 + /Type /Page
  238 +>>
  239 +endobj
  240 +
  241 +%% Page 5
  242 +%% Original object ID: 55 0
  243 +10 0 obj
  244 +<<
  245 + /Contents 35 0 R
  246 + /MediaBox [
  247 + 0
  248 + 0
  249 + 612
  250 + 792
  251 + ]
  252 + /Parent 3 0 R
  253 + /Resources <<
  254 + /Font <<
  255 + /F1 33 0 R
  256 + >>
  257 + /ProcSet 34 0 R
  258 + >>
  259 + /Type /Page
  260 +>>
  261 +endobj
  262 +
  263 +%% Page 6
  264 +%% Original object ID: 57 0
  265 +11 0 obj
  266 +<<
  267 + /Contents 37 0 R
  268 + /MediaBox [
  269 + 0
  270 + 0
  271 + 612
  272 + 792
  273 + ]
  274 + /Parent 3 0 R
  275 + /Resources <<
  276 + /Font <<
  277 + /F1 33 0 R
  278 + >>
  279 + /ProcSet 34 0 R
  280 + >>
  281 + /Type /Page
  282 +>>
  283 +endobj
  284 +
  285 +%% Page 7
  286 +%% Original object ID: 59 0
  287 +12 0 obj
  288 +<<
  289 + /Contents 39 0 R
  290 + /MediaBox [
  291 + 0
  292 + 0
  293 + 612
  294 + 792
  295 + ]
  296 + /Parent 3 0 R
  297 + /Resources <<
  298 + /Font <<
  299 + /F1 33 0 R
  300 + >>
  301 + /ProcSet 34 0 R
  302 + >>
  303 + /Type /Page
  304 +>>
  305 +endobj
  306 +
  307 +%% Page 8
  308 +%% Original object ID: 8 0
  309 +13 0 obj
  310 +<<
  311 + /Contents 41 0 R
  312 + /MediaBox [
  313 + 0
  314 + 0
  315 + 612
  316 + 792
  317 + ]
  318 + /Parent 3 0 R
  319 + /Resources <<
  320 + /Font <<
  321 + /F1 23 0 R
  322 + >>
  323 + /ProcSet 24 0 R
  324 + >>
  325 + /Type /Page
  326 +>>
  327 +endobj
  328 +
  329 +%% Page 9
  330 +%% Original object ID: 9 0
  331 +14 0 obj
  332 +<<
  333 + /Contents 43 0 R
  334 + /MediaBox [
  335 + 0
  336 + 0
  337 + 612
  338 + 792
  339 + ]
  340 + /Parent 3 0 R
  341 + /Resources <<
  342 + /Font <<
  343 + /F1 23 0 R
  344 + >>
  345 + /ProcSet 24 0 R
  346 + >>
  347 + /Type /Page
  348 +>>
  349 +endobj
  350 +
  351 +%% Page 10
  352 +%% Original object ID: 61 0
  353 +15 0 obj
  354 +<<
  355 + /Contents 45 0 R
  356 + /MediaBox [
  357 + 0
  358 + 0
  359 + 612
  360 + 792
  361 + ]
  362 + /Parent 3 0 R
  363 + /Resources <<
  364 + /Font <<
  365 + /F1 33 0 R
  366 + >>
  367 + /ProcSet 34 0 R
  368 + >>
  369 + /Type /Page
  370 +>>
  371 +endobj
  372 +
  373 +%% Page 11
  374 +%% Original object ID: 63 0
  375 +16 0 obj
  376 +<<
  377 + /Contents 47 0 R
  378 + /MediaBox [
  379 + 0
  380 + 0
  381 + 612
  382 + 792
  383 + ]
  384 + /Parent 3 0 R
  385 + /Resources <<
  386 + /Font <<
  387 + /F1 33 0 R
  388 + >>
  389 + /ProcSet 34 0 R
  390 + >>
  391 + /Type /Page
  392 +>>
  393 +endobj
  394 +
  395 +%% Page 12
  396 +%% Original object ID: 65 0
  397 +17 0 obj
  398 +<<
  399 + /Contents 49 0 R
  400 + /MediaBox [
  401 + 0
  402 + 0
  403 + 612
  404 + 792
  405 + ]
  406 + /Parent 3 0 R
  407 + /Resources <<
  408 + /Font <<
  409 + /F1 33 0 R
  410 + >>
  411 + /ProcSet 34 0 R
  412 + >>
  413 + /Type /Page
  414 +>>
  415 +endobj
  416 +
  417 +%% Page 13
  418 +%% Original object ID: 10 0
  419 +18 0 obj
  420 +<<
  421 + /Contents 51 0 R
  422 + /MediaBox [
  423 + 0
  424 + 0
  425 + 612
  426 + 792
  427 + ]
  428 + /Parent 3 0 R
  429 + /Resources <<
  430 + /Font <<
  431 + /F1 23 0 R
  432 + >>
  433 + /ProcSet 24 0 R
  434 + >>
  435 + /Type /Page
  436 +>>
  437 +endobj
  438 +
  439 +%% Original object ID: 21 0
  440 +19 0 obj
  441 +<<
  442 + /Count -3
  443 + /Dest [
  444 + null
  445 + /Fit
  446 + ]
  447 + /First 53 0 R
  448 + /Last 54 0 R
  449 + /Next 20 0 R
  450 + /Parent 4 0 R
  451 + /Title (Amanda 1.1 -> 11: /Fit)
  452 + /Type /Outline
  453 +>>
  454 +endobj
  455 +
  456 +%% Original object ID: 22 0
  457 +20 0 obj
  458 +<<
  459 + /Count 2
  460 + /Dest [
  461 + null
  462 + /FitH
  463 + 792
  464 + ]
  465 + /First 55 0 R
  466 + /Last 56 0 R
  467 + /Parent 4 0 R
  468 + /Prev 19 0 R
  469 + /Title <feff00530061006e00640079002000f703a303b103bd03b403b900f700200031002e00320020002d003e002000310033003a0020002f00460069007400480020003700390032>
  470 + /Type /Outline
  471 +>>
  472 +endobj
  473 +
  474 +%% Contents for page 1
  475 +%% Original object ID: 23 0
  476 +21 0 obj
  477 +<<
  478 + /Length 22 0 R
  479 +>>
  480 +stream
  481 +BT
  482 + /F1 24 Tf
  483 + 72 720 Td
  484 + (Potato 0) Tj
  485 +ET
  486 +endstream
  487 +endobj
  488 +
  489 +22 0 obj
  490 +46
  491 +endobj
  492 +
  493 +%% Original object ID: 24 0
  494 +23 0 obj
  495 +<<
  496 + /BaseFont /Helvetica
  497 + /Encoding /WinAnsiEncoding
  498 + /Name /F1
  499 + /Subtype /Type1
  500 + /Type /Font
  501 +>>
  502 +endobj
  503 +
  504 +%% Original object ID: 25 0
  505 +24 0 obj
  506 +[
  507 + /PDF
  508 + /Text
  509 +]
  510 +endobj
  511 +
  512 +%% Contents for page 2
  513 +%% Original object ID: 26 0
  514 +25 0 obj
  515 +<<
  516 + /Length 26 0 R
  517 +>>
  518 +stream
  519 +BT
  520 + /F1 24 Tf
  521 + 72 720 Td
  522 + (Potato 2) Tj
  523 +ET
  524 +endstream
  525 +endobj
  526 +
  527 +26 0 obj
  528 +46
  529 +endobj
  530 +
  531 +%% Contents for page 3
  532 +%% Original object ID: 48 0
  533 +27 0 obj
  534 +<<
  535 + /Length 28 0 R
  536 +>>
  537 +stream
  538 +BT
  539 + /F1 24 Tf
  540 + 72 720 Td
  541 + (Potato) Tj
  542 +ET
  543 +endstream
  544 +endobj
  545 +
  546 +28 0 obj
  547 +44
  548 +endobj
  549 +
  550 +%% Original object ID: 49 0
  551 +29 0 obj
  552 +<<
  553 + /BaseFont /Helvetica
  554 + /Encoding /WinAnsiEncoding
  555 + /Name /F1
  556 + /Subtype /Type1
  557 + /Type /Font
  558 +>>
  559 +endobj
  560 +
  561 +%% Original object ID: 50 0
  562 +30 0 obj
  563 +[
  564 + /PDF
  565 + /Text
  566 +]
  567 +endobj
  568 +
  569 +%% Contents for page 4
  570 +%% Original object ID: 52 0
  571 +31 0 obj
  572 +<<
  573 + /Length 32 0 R
  574 +>>
  575 +stream
  576 +BT
  577 + /F1 24 Tf
  578 + 72 720 Td
  579 + (Potato 13) Tj
  580 +ET
  581 +endstream
  582 +endobj
  583 +
  584 +32 0 obj
  585 +47
  586 +endobj
  587 +
  588 +%% Original object ID: 53 0
  589 +33 0 obj
  590 +<<
  591 + /BaseFont /Helvetica
  592 + /Encoding /WinAnsiEncoding
  593 + /Name /F1
  594 + /Subtype /Type1
  595 + /Type /Font
  596 +>>
  597 +endobj
  598 +
  599 +%% Original object ID: 54 0
  600 +34 0 obj
  601 +[
  602 + /PDF
  603 + /Text
  604 +]
  605 +endobj
  606 +
  607 +%% Contents for page 5
  608 +%% Original object ID: 56 0
  609 +35 0 obj
  610 +<<
  611 + /Length 36 0 R
  612 +>>
  613 +stream
  614 +BT
  615 + /F1 24 Tf
  616 + 72 720 Td
  617 + (Potato 11) Tj
  618 +ET
  619 +endstream
  620 +endobj
  621 +
  622 +36 0 obj
  623 +47
  624 +endobj
  625 +
  626 +%% Contents for page 6
  627 +%% Original object ID: 58 0
  628 +37 0 obj
  629 +<<
  630 + /Length 38 0 R
  631 +>>
  632 +stream
  633 +BT
  634 + /F1 24 Tf
  635 + 72 720 Td
  636 + (Potato 9) Tj
  637 +ET
  638 +endstream
  639 +endobj
  640 +
  641 +38 0 obj
  642 +46
  643 +endobj
  644 +
  645 +%% Contents for page 7
  646 +%% Original object ID: 60 0
  647 +39 0 obj
  648 +<<
  649 + /Length 40 0 R
  650 +>>
  651 +stream
  652 +BT
  653 + /F1 24 Tf
  654 + 72 720 Td
  655 + (Potato 7) Tj
  656 +ET
  657 +endstream
  658 +endobj
  659 +
  660 +40 0 obj
  661 +46
  662 +endobj
  663 +
  664 +%% Contents for page 8
  665 +%% Original object ID: 27 0
  666 +41 0 obj
  667 +<<
  668 + /Length 42 0 R
  669 +>>
  670 +stream
  671 +BT
  672 + /F1 24 Tf
  673 + 72 720 Td
  674 + (Potato 4) Tj
  675 +ET
  676 +endstream
  677 +endobj
  678 +
  679 +42 0 obj
  680 +46
  681 +endobj
  682 +
  683 +%% Contents for page 9
  684 +%% Original object ID: 28 0
  685 +43 0 obj
  686 +<<
  687 + /Length 44 0 R
  688 +>>
  689 +stream
  690 +BT
  691 + /F1 24 Tf
  692 + 72 720 Td
  693 + (Potato 6) Tj
  694 +ET
  695 +endstream
  696 +endobj
  697 +
  698 +44 0 obj
  699 +46
  700 +endobj
  701 +
  702 +%% Contents for page 10
  703 +%% Original object ID: 62 0
  704 +45 0 obj
  705 +<<
  706 + /Length 46 0 R
  707 +>>
  708 +stream
  709 +BT
  710 + /F1 24 Tf
  711 + 72 720 Td
  712 + (Potato 5) Tj
  713 +ET
  714 +endstream
  715 +endobj
  716 +
  717 +46 0 obj
  718 +46
  719 +endobj
  720 +
  721 +%% Contents for page 11
  722 +%% Original object ID: 64 0
  723 +47 0 obj
  724 +<<
  725 + /Length 48 0 R
  726 +>>
  727 +stream
  728 +BT
  729 + /F1 24 Tf
  730 + 72 720 Td
  731 + (Potato 3) Tj
  732 +ET
  733 +endstream
  734 +endobj
  735 +
  736 +48 0 obj
  737 +46
  738 +endobj
  739 +
  740 +%% Contents for page 12
  741 +%% Original object ID: 66 0
  742 +49 0 obj
  743 +<<
  744 + /Length 50 0 R
  745 +>>
  746 +stream
  747 +BT
  748 + /F1 24 Tf
  749 + 72 720 Td
  750 + (Potato 1) Tj
  751 +ET
  752 +endstream
  753 +endobj
  754 +
  755 +50 0 obj
  756 +46
  757 +endobj
  758 +
  759 +%% Contents for page 13
  760 +%% Original object ID: 29 0
  761 +51 0 obj
  762 +<<
  763 + /Length 52 0 R
  764 +>>
  765 +stream
  766 +BT
  767 + /F1 24 Tf
  768 + 72 720 Td
  769 + (Potato 8) Tj
  770 +ET
  771 +endstream
  772 +endobj
  773 +
  774 +52 0 obj
  775 +46
  776 +endobj
  777 +
  778 +%% Original object ID: 40 0
  779 +53 0 obj
  780 +<<
  781 + /Count -2
  782 + /Dest [
  783 + 57 0 R
  784 + /FitV
  785 + 100
  786 + ]
  787 + /First 58 0 R
  788 + /Last 59 0 R
  789 + /Next 54 0 R
  790 + /Parent 19 0 R
  791 + /Title (Isosicle 1.1.1 -> 12: /FitV 100)
  792 + /Type /Outline
  793 +>>
  794 +endobj
  795 +
  796 +%% Original object ID: 41 0
  797 +54 0 obj
  798 +<<
  799 + /Count 1
  800 + /Dest [
  801 + 57 0 R
  802 + /XYZ
  803 + null
  804 + null
  805 + null
  806 + ]
  807 + /First 60 0 R
  808 + /Last 60 0 R
  809 + /Parent 19 0 R
  810 + /Prev 53 0 R
  811 + /Title (Isosicle 1.1.2 -> 12: /XYZ null null null)
  812 + /Type /Outline
  813 +>>
  814 +endobj
  815 +
  816 +%% Original object ID: 42 0
  817 +55 0 obj
  818 +<<
  819 + /Dest [
  820 + null
  821 + /FitR
  822 + 66
  823 + 714
  824 + 180
  825 + 770
  826 + ]
  827 + /Next 56 0 R
  828 + /Parent 20 0 R
  829 + /Title (Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770)
  830 + /Type /Outline
  831 +>>
  832 +endobj
  833 +
  834 +%% Original object ID: 43 0
  835 +56 0 obj
  836 +<<
  837 + /Dest [
  838 + 6 0 R
  839 + /XYZ
  840 + null
  841 + null
  842 + null
  843 + ]
  844 + /Parent 20 0 R
  845 + /Prev 55 0 R
  846 + /Title (Trepsicle 1.2.2 -> 0: /XYZ null null null)
  847 + /Type /Outline
  848 +>>
  849 +endobj
  850 +
  851 +%% Original object ID: 12 0
  852 +57 0 obj
  853 +null
  854 +endobj
  855 +
  856 +%% Original object ID: 44 0
  857 +58 0 obj
  858 +<<
  859 + /Dest [
  860 + 61 0 R
  861 + /XYZ
  862 + null
  863 + null
  864 + null
  865 + ]
  866 + /Next 59 0 R
  867 + /Parent 53 0 R
  868 + /Title (Isosicle 1.1.1.1 -> 18: /XYZ null null null)
  869 + /Type /Outline
  870 +>>
  871 +endobj
  872 +
  873 +%% Original object ID: 45 0
  874 +59 0 obj
  875 +<<
  876 + /Dest [
  877 + null
  878 + /XYZ
  879 + null
  880 + null
  881 + null
  882 + ]
  883 + /Parent 53 0 R
  884 + /Prev 58 0 R
  885 + /Title (Isosicle 1.1.1.2 -> 19: /XYZ null null null)
  886 + /Type /Outline
  887 +>>
  888 +endobj
  889 +
  890 +%% Original object ID: 46 0
  891 +60 0 obj
  892 +<<
  893 + /Dest [
  894 + 62 0 R
  895 + /XYZ
  896 + null
  897 + null
  898 + null
  899 + ]
  900 + /Parent 54 0 R
  901 + /Title (Isosicle 1.1.2.1 -> 22: /XYZ null null null)
  902 + /Type /Outline
  903 +>>
  904 +endobj
  905 +
  906 +%% Original object ID: 15 0
  907 +61 0 obj
  908 +null
  909 +endobj
  910 +
  911 +%% Original object ID: 17 0
  912 +62 0 obj
  913 +null
  914 +endobj
  915 +
  916 +xref
  917 +0 63
  918 +0000000000 65535 f
  919 +0000000052 00000 n
  920 +0000000907 00000 n
  921 +0000001014 00000 n
  922 +0000001243 00000 n
  923 +0000001484 00000 n
  924 +0000001684 00000 n
  925 +0000001916 00000 n
  926 +0000002149 00000 n
  927 +0000002382 00000 n
  928 +0000002615 00000 n
  929 +0000002849 00000 n
  930 +0000003083 00000 n
  931 +0000003316 00000 n
  932 +0000003549 00000 n
  933 +0000003784 00000 n
  934 +0000004019 00000 n
  935 +0000004254 00000 n
  936 +0000004489 00000 n
  937 +0000004713 00000 n
  938 +0000004921 00000 n
  939 +0000005278 00000 n
  940 +0000005381 00000 n
  941 +0000005429 00000 n
  942 +0000005576 00000 n
  943 +0000005663 00000 n
  944 +0000005766 00000 n
  945 +0000005837 00000 n
  946 +0000005938 00000 n
  947 +0000005986 00000 n
  948 +0000006133 00000 n
  949 +0000006220 00000 n
  950 +0000006324 00000 n
  951 +0000006372 00000 n
  952 +0000006519 00000 n
  953 +0000006606 00000 n
  954 +0000006710 00000 n
  955 +0000006781 00000 n
  956 +0000006884 00000 n
  957 +0000006955 00000 n
  958 +0000007058 00000 n
  959 +0000007129 00000 n
  960 +0000007232 00000 n
  961 +0000007303 00000 n
  962 +0000007406 00000 n
  963 +0000007478 00000 n
  964 +0000007581 00000 n
  965 +0000007653 00000 n
  966 +0000007756 00000 n
  967 +0000007828 00000 n
  968 +0000007931 00000 n
  969 +0000008003 00000 n
  970 +0000008106 00000 n
  971 +0000008154 00000 n
  972 +0000008383 00000 n
  973 +0000008639 00000 n
  974 +0000008859 00000 n
  975 +0000009072 00000 n
  976 +0000009122 00000 n
  977 +0000009338 00000 n
  978 +0000009552 00000 n
  979 +0000009753 00000 n
  980 +0000009803 00000 n
  981 +trailer <<
  982 + /Root 1 0 R
  983 + /Size 63
  984 + /ID [<d3fab8d0603e683dc94e42ac31141868><31415926535897932384626433832795>]
  985 +>>
  986 +startxref
  987 +9825
  988 +%%EOF
... ...