aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2024-01-02 00:55:45 +0100
committerJay Berkenbilt <ejb@ql.org>2024-01-02 00:57:32 +0100
commitd8d70ecca264fa5c681dca992a03cfa4c46b3f43 (patch)
treef9960a48f4550f6a9fc2d09cef27fba66852bdd5
parent9db5d75b2b40083f339525f7a59b4408cb1b5d2c (diff)
downloadqpdf-d8d70ecca264fa5c681dca992a03cfa4c46b3f43.tar.zst
Support comma-separated numeric values with --collate (fixes #505)
-rw-r--r--ChangeLog6
-rw-r--r--include/qpdf/QPDFJob.hh2
-rw-r--r--job.sums6
-rw-r--r--libqpdf/QPDFJob.cc35
-rw-r--r--libqpdf/QPDFJob_config.cc31
-rw-r--r--libqpdf/qpdf/auto_job_help.hh8
-rw-r--r--manual/cli.rst49
-rw-r--r--manual/qpdf.18
-rw-r--r--manual/release-notes.rst4
-rw-r--r--qpdf/qtest/arg-parsing.test10
-rw-r--r--qpdf/qtest/collate.test8
-rw-r--r--qpdf/qtest/qpdf/three-files-2,3,4-collate-out.pdf988
12 files changed, 1116 insertions, 39 deletions
diff --git a/ChangeLog b/ChangeLog
index a5bc330a..c5336c11 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,11 @@
2024-01-01 Jay Berkenbilt <ejb@ql.org>
+ * Support comma-separated numeric values with --collate to select
+ different group sizes from different files. Fixes #505.
+
* Support "x" before a group in a numeric range to exclude a group
- from the previous group. Details are in the manual.
+ from the previous group. Details are in the manual. Fixes #564,
+ #790.
2023-12-29 Jay Berkenbilt <ejb@ql.org>
diff --git a/include/qpdf/QPDFJob.hh b/include/qpdf/QPDFJob.hh
index d7f14957..3cfc550e 100644
--- a/include/qpdf/QPDFJob.hh
+++ b/include/qpdf/QPDFJob.hh
@@ -637,7 +637,7 @@ class QPDFJob
bool show_filtered_stream_data{false};
bool show_pages{false};
bool show_page_images{false};
- size_t collate{0};
+ std::vector<size_t> collate;
bool flatten_rotation{false};
bool list_attachments{false};
std::string attachment_to_show;
diff --git a/job.sums b/job.sums
index adde95f6..bf044dcb 100644
--- a/job.sums
+++ b/job.sums
@@ -9,12 +9,12 @@ include/qpdf/auto_job_c_pages.hh b3cc0f21029f6d89efa043dcdbfa183cb59325b6506001c
include/qpdf/auto_job_c_uo.hh ae21b69a1efa9333050f4833d465f6daff87e5b38e5106e49bbef5d4132e4ed1
job.yml 4f89fc7b622df897d30d403d8035aa36fc7de8d8c43042c736e0300d904cb05c
libqpdf/qpdf/auto_job_decl.hh 9c6f701c29f3f764d620186bed92685a2edf2e4d11e4f4532862c05470cfc4d2
-libqpdf/qpdf/auto_job_help.hh 838f4065f64dc3fbd493510fd21d8ab4e16ee2434592776f44f80cbe3045cb50
+libqpdf/qpdf/auto_job_help.hh ea0d0cdebeb190d305bd5f9bca85a4430dbcfa0881ac9be839216b878765b379
libqpdf/qpdf/auto_job_init.hh b4c2b3724fba61f1206fd3bae81951636852592f67a63ef9539839c2c5995065
libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a9474319fb7c86d92634cc8297
libqpdf/qpdf/auto_job_json_init.hh f5acb9aa103131cb68dec0e12c4d237a6459bdb49b24773c24f0c2724a462b8f
libqpdf/qpdf/auto_job_schema.hh b53c006fec2e75b1b73588d242d49a32f7d3db820b1541de106c5d4c27fbb4d9
manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580
-manual/cli.rst d6d1ca82c936ffeaf137c586f988f80043db4c3b226d26fdf94f19a6005d012e
-manual/qpdf.1 10dc52d32a6d8885ce4e4292875ee7fe8e7a826ef3fc28db5671be413bcaacc7
+manual/cli.rst fc8488129c479b6cde9dffbbddb150bc1ffb45bc38b3bff2c5dba4378f0edb67
+manual/qpdf.1 738dc9b732ad4c880d034b99f957077628fde1d0006943aaf813e98f8e2f9635
manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b
diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc
index b2f905a1..a7cb3e3a 100644
--- a/libqpdf/QPDFJob.cc
+++ b/libqpdf/QPDFJob.cc
@@ -1,10 +1,6 @@
#include <qpdf/QPDFJob.hh>
-#include <cctype>
-#include <cstdio>
-#include <cstdlib>
#include <cstring>
-#include <fcntl.h>
#include <iostream>
#include <memory>
@@ -14,13 +10,11 @@
#include <qpdf/Pl_DCT.hh>
#include <qpdf/Pl_Discard.hh>
#include <qpdf/Pl_Flate.hh>
-#include <qpdf/Pl_OStream.hh>
#include <qpdf/Pl_StdioFile.hh>
#include <qpdf/Pl_String.hh>
#include <qpdf/QIntC.hh>
#include <qpdf/QPDF.hh>
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
-#include <qpdf/QPDFArgParser.hh>
#include <qpdf/QPDFCryptoProvider.hh>
#include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
#include <qpdf/QPDFExc.hh>
@@ -2419,26 +2413,32 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea
dh.removePage(page);
}
- if (m->collate && (parsed_specs.size() > 1)) {
+ auto n_collate = m->collate.size();
+ auto n_specs = parsed_specs.size();
+ if (n_collate > 0 && n_specs > 1) {
// Collate the pages by selecting one page from each spec in order. When a spec runs out of
// pages, stop selecting from it.
std::vector<QPDFPageData> new_parsed_specs;
- size_t nspecs = parsed_specs.size();
- size_t cur_page = 0;
+ // Make sure we have a collate value for each spec. We have already checked that a non-empty
+ // collate has either one value or one value per spec.
+ for (auto i = n_collate; i < n_specs; ++i) {
+ m->collate.push_back(m->collate.at(0));
+ }
+ std::vector<size_t> cur_page(n_specs, 0);
bool got_pages = true;
while (got_pages) {
got_pages = false;
- for (size_t i = 0; i < nspecs; ++i) {
+ for (size_t i = 0; i < n_specs; ++i) {
QPDFPageData& page_data = parsed_specs.at(i);
- for (size_t j = 0; j < m->collate; ++j) {
- if (cur_page + j < page_data.selected_pages.size()) {
+ for (size_t j = 0; j < m->collate.at(i); ++j) {
+ if (cur_page.at(i) + j < page_data.selected_pages.size()) {
got_pages = true;
new_parsed_specs.emplace_back(
- page_data, page_data.selected_pages.at(cur_page + j));
+ page_data, page_data.selected_pages.at(cur_page.at(i) + j));
}
}
+ cur_page.at(i) += m->collate.at(i);
}
- cur_page += m->collate;
}
parsed_specs = new_parsed_specs;
}
@@ -3019,9 +3019,10 @@ QPDFJob::writeOutfile(QPDF& pdf)
try {
QUtil::remove_file(backup.c_str());
} catch (QPDFSystemError& e) {
- *m->log->getError() << m->message_prefix << ": unable to delete original file ("
- << e.what() << ");" << " original file left in " << backup
- << ", but the input was successfully replaced\n";
+ *m->log->getError()
+ << m->message_prefix << ": unable to delete original file (" << e.what() << ");"
+ << " original file left in " << backup
+ << ", but the input was successfully replaced\n";
}
}
}
diff --git a/libqpdf/QPDFJob_config.cc b/libqpdf/QPDFJob_config.cc
index 4798ce9b..a0dc10f6 100644
--- a/libqpdf/QPDFJob_config.cc
+++ b/libqpdf/QPDFJob_config.cc
@@ -98,8 +98,27 @@ QPDFJob::Config::collate()
QPDFJob::Config*
QPDFJob::Config::collate(std::string const& parameter)
{
- auto n = (parameter.empty() ? 1 : QUtil::string_to_uint(parameter.c_str()));
- o.m->collate = QIntC::to_size(n);
+ if (parameter.empty()) {
+ o.m->collate.push_back(1);
+ return this;
+ }
+ size_t pos = 0;
+ // Parse a,b,c
+ while (true) {
+ auto end = parameter.find(',', pos);
+ auto n = parameter.substr(pos, end);
+ if (n.empty()) {
+ usage("--collate: trailing comma");
+ }
+ o.m->collate.push_back(QIntC::to_size(QUtil::string_to_uint(n.c_str())));
+ if (end == std::string::npos) {
+ break;
+ }
+ pos = end + 1;
+ }
+ if (o.m->collate.empty()) {
+ o.m->collate.push_back(1);
+ }
return this;
}
@@ -932,9 +951,15 @@ QPDFJob::Config::pages()
QPDFJob::Config*
QPDFJob::PagesConfig::endPages()
{
- if (this->config->o.m->page_specs.empty()) {
+ auto n_specs = config->o.m->page_specs.size();
+ if (n_specs == 0) {
usage("--pages: no page specifications given");
}
+ auto n_collate = config->o.m->collate.size();
+ if (!(n_collate == 0 || n_collate == 1 || n_collate == n_specs)) {
+ usage("--pages: if --collate has more than one value, it must have one value per page "
+ "specification");
+ }
return this->config;
}
diff --git a/libqpdf/qpdf/auto_job_help.hh b/libqpdf/qpdf/auto_job_help.hh
index 4ee0f56e..c83f94b4 100644
--- a/libqpdf/qpdf/auto_job_help.hh
+++ b/libqpdf/qpdf/auto_job_help.hh
@@ -315,11 +315,13 @@ ap.addOptionHelp("--pages", "modification", "begin page selection", R"(--pages f
Run qpdf --help=page-selection for details.
)");
-ap.addOptionHelp("--collate", "modification", "collate with --pages", R"(--collate[=n]
+ap.addOptionHelp("--collate", "modification", "collate with --pages", R"(--collate[=n[,m,...]]
Collate rather than concatenate pages specified with --pages.
With a numeric parameter, collate in groups of n. The default
-is 1. Run qpdf --help=page-selection for additional details.
+is 1. With comma-separated numeric parameters, take n from the
+first file, m from the second, etc. Run
+qpdf --help=page-selection for additional details.
)");
ap.addOptionHelp("--split-pages", "modification", "write pages to separate files", R"(--split-pages[=n]
@@ -607,6 +609,8 @@ Run qpdf --help=page-ranges for help with page ranges.
Use --collate=n to cause pages to be collated in groups of n pages
(default 1) instead of concatenating the input.
+Use --collate=i,j,k,... to take i from the first, then j from the
+second, then k from the third, then i from the first, etc.
Examples:
diff --git a/manual/cli.rst b/manual/cli.rst
index 592ba6ef..4927205a 100644
--- a/manual/cli.rst
+++ b/manual/cli.rst
@@ -1403,18 +1403,21 @@ Related Options
See also :qpdf:ref:`--split-pages`, :qpdf:ref:`--collate`,
:ref:`page-ranges`.
-.. qpdf:option:: --collate[=n]
+.. qpdf:option:: --collate[=n[,m,...]]
.. help: collate with --pages
Collate rather than concatenate pages specified with --pages.
With a numeric parameter, collate in groups of n. The default
- is 1. Run qpdf --help=page-selection for additional details.
+ is 1. With comma-separated numeric parameters, take n from the
+ first file, m from the second, etc. Run
+ qpdf --help=page-selection for additional details.
This option causes :command:`qpdf` to collate rather than
concatenate pages specified with :qpdf:ref:`--pages`. With a
numeric parameter, collate in groups of :samp:`{n}`. The default
- is 1.
+ is 1. With comma-separated numeric parameters, take :samp:`{n}`
+ from the first file, :samp:`{m}` from the second, etc.
Please see :ref:`page-selection` for additional details.
@@ -2335,6 +2338,8 @@ Page Selection
Use --collate=n to cause pages to be collated in groups of n pages
(default 1) instead of concatenating the input.
+ Use --collate=i,j,k,... to take i from the first, then j from the
+ second, then k from the third, then i from the first, etc.
Examples:
@@ -2383,9 +2388,13 @@ Notes:
See :ref:`page-ranges` for help on specifying a page range.
Use :samp:`--collate={n}` to cause pages to be collated in groups of
-:samp:`{n}` pages (default 1) instead of concatenating the input. Note
-that the :qpdf:ref:`--collate` appears outside of ``--pages ... --``
-(before ``--pages`` or after ``--``). Pages are pulled from each
+:samp:`{n}` pages (default 1) instead of concatenating the input. Use
+:samp:`--collate={i},{j},{k},...` to take :samp:`{i}` from the first,
+then :samp:`{j}` from the second, then :samp:`{k}` from the third,
+then :samp:`{i}` from the first, etc.
+
+Note that the :qpdf:ref:`--collate` appears outside of ``--pages ...
+--`` (before ``--pages`` or after ``--``). Pages are pulled from each
document in turn. When a document is out of pages, it is skipped. See
examples below.
@@ -2481,6 +2490,34 @@ Examples
- a.pdf page 5
+- You can specify a multiple numeric parameters to :qpdf:ref:`--collate`. With
+ :samp:`--collate={i,j,k}`, pull groups of :samp:`{i}` pages from the
+ first file, then :samp:`{j}` from the second, thenm :samp:`{k}` from
+ the third, repeating. The number of parameters must equal the number
+ of groups. For example, if you ran
+
+ ::
+
+ qpdf --collate=2,1,3 --empty --pages a.pdf 1-5 b.pdf 6-4 c.pdf r1-r4 -- out.pdf
+
+ you would get the following pages in this order:
+
+ - a.pdf pages 1 and 2
+
+ - b.pdf page 6
+
+ - c.pdf last three pages in reverse order
+
+ - a.pdf pages 3 and 4
+
+ - b.pdf page 5
+
+ - c.pdf fourth to last page
+
+ - a.pdf page 5
+
+ - b.pdf page 4
+
- Take pages 1 through 5 from :file:`file1.pdf` and pages 11 through
15 in reverse from :file:`file2.pdf`, taking document-level metadata
from :file:`file2.pdf`.
diff --git a/manual/qpdf.1 b/manual/qpdf.1
index d758dca3..bf5e9792 100644
--- a/manual/qpdf.1
+++ b/manual/qpdf.1
@@ -414,11 +414,13 @@ Related Options:
Run qpdf --help=page-selection for details.
.TP
.B --collate \-\- collate with --pages
---collate[=n]
+--collate[=n[,m,...]]
Collate rather than concatenate pages specified with --pages.
With a numeric parameter, collate in groups of n. The default
-is 1. Run qpdf --help=page-selection for additional details.
+is 1. With comma-separated numeric parameters, take n from the
+first file, m from the second, etc. Run
+qpdf --help=page-selection for additional details.
.TP
.B --split-pages \-\- write pages to separate files
--split-pages[=n]
@@ -737,6 +739,8 @@ Run qpdf --help=page-ranges for help with page ranges.
Use --collate=n to cause pages to be collated in groups of n pages
(default 1) instead of concatenating the input.
+Use --collate=i,j,k,... to take i from the first, then j from the
+second, then k from the third, then i from the first, etc.
Examples:
diff --git a/manual/release-notes.rst b/manual/release-notes.rst
index 17dc116d..72b1aad6 100644
--- a/manual/release-notes.rst
+++ b/manual/release-notes.rst
@@ -50,6 +50,10 @@ Planned changes for future 12.x (subject to change):
of pages within a page range. See :ref:`page-ranges` for
details.
+ - Support comma-separated numeric values with
+ :qpdf:ref:`--collate` to select different numbers of pages from
+ different groups.
+
11.7.0: December 24, 2023
- Bug fixes:
diff --git a/qpdf/qtest/arg-parsing.test b/qpdf/qtest/arg-parsing.test
index aca8c7ef..82594674 100644
--- a/qpdf/qtest/arg-parsing.test
+++ b/qpdf/qtest/arg-parsing.test
@@ -15,7 +15,7 @@ cleanup();
my $td = new TestDriver('arg-parsing');
-my $n_tests = 22;
+my $n_tests = 23;
$td->runtest("required argument",
{$td->COMMAND => "qpdf --password minimal.pdf"},
@@ -94,6 +94,14 @@ $td->runtest("v2-only qpdf json-key",
{$td->REGEXP => ".*\"qpdf\" is only valid for json version > 1.*",
$td->EXIT_STATUS => 2},
$td->NORMALIZE_NEWLINES);
+$td->runtest("wrong number of collate args",
+ {$td->COMMAND =>
+ "qpdf --collate=2,3 collate-odd.pdf" .
+ " --pages . minimal.pdf collate-even.pdf -- a.pdf"},
+ {$td->REGEXP => ".*--collate has more than one value.*",
+ $td->EXIT_STATUS => 2},
+ $td->NORMALIZE_NEWLINES);
+
# Ignoring -- at the top level was never intended but turned out to
# have been there for a long time so that people relied on it. It is
# intentionally not documented.
diff --git a/qpdf/qtest/collate.test b/qpdf/qtest/collate.test
index fbbea641..fb296e29 100644
--- a/qpdf/qtest/collate.test
+++ b/qpdf/qtest/collate.test
@@ -17,9 +17,11 @@ my $td = new TestDriver('collate');
my @collate = (
["", "three-files", "collate-odd",
"collate-odd.pdf 1-5 minimal.pdf collate-even.pdf 7-1"],
- [1, "three-files", "collate-odd",
+ ["1", "three-files", "collate-odd",
"collate-odd.pdf 1-5 minimal.pdf collate-even.pdf 7-1"],
- [2, "three-files-2", "collate-odd",
+ ["2", "three-files-2", "collate-odd",
+ "collate-odd.pdf 1-5 minimal.pdf collate-even.pdf 7-1"],
+ ["2,3,4", "three-files-2,3,4", "collate-odd",
"collate-odd.pdf 1-5 minimal.pdf collate-even.pdf 7-1"],
);
my $n_tests = 2 * scalar(@collate);
@@ -28,7 +30,7 @@ foreach my $d (@collate)
{
my ($n, $description, $first, $args) = @$d;
my $collate = '--collate';
- if ($n)
+ if ($n ne "")
{
$collate .= "=$n";
}
diff --git a/qpdf/qtest/qpdf/three-files-2,3,4-collate-out.pdf b/qpdf/qtest/qpdf/three-files-2,3,4-collate-out.pdf
new file mode 100644
index 00000000..8be9c056
--- /dev/null
+++ b/qpdf/qtest/qpdf/three-files-2,3,4-collate-out.pdf
@@ -0,0 +1,988 @@
+%PDF-1.3
+%¿÷¢þ
+%QDF-1.0
+
+%% Original object ID: 1 0
+1 0 obj
+<<
+ /Outlines 2 0 R
+ /PageLabels <<
+ /Nums [
+ 0
+ <<
+ /P ()
+ /St 1
+ >>
+ 1
+ <<
+ /S /r
+ /St 1
+ >>
+ 2
+ <<
+ /St 3
+ >>
+ 3
+ <<
+ /S /D
+ /St 3
+ >>
+ 4
+ <<
+ /P ()
+ /St 1
+ >>
+ 5
+ <<
+ /S /r
+ /St 6
+ >>
+ 6
+ <<
+ /P ()
+ /St 1
+ >>
+ 7
+ <<
+ /S /r
+ /St 3
+ >>
+ 8
+ <<
+ /S /r
+ /St 5
+ >>
+ 9
+ <<
+ /S /r
+ /St 4
+ >>
+ 10
+ <<
+ /S /r
+ /St 2
+ >>
+ 11
+ <<
+ /P ()
+ /St 2
+ >>
+ 12
+ <<
+ /P ()
+ /St 2
+ >>
+ ]
+ >>
+ /PageMode /UseOutlines
+ /Pages 3 0 R
+ /Type /Catalog
+>>
+endobj
+
+%% Original object ID: 2 0
+2 0 obj
+<<
+ /Count 6
+ /First 4 0 R
+ /Last 5 0 R
+ /Type /Outlines
+>>
+endobj
+
+%% Original object ID: 3 0
+3 0 obj
+<<
+ /Count 13
+ /Kids [
+ 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
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Original object ID: 4 0
+4 0 obj
+<<
+ /Count 4
+ /Dest [
+ null
+ /XYZ
+ null
+ null
+ null
+ ]
+ /First 19 0 R
+ /Last 20 0 R
+ /Next 5 0 R
+ /Parent 2 0 R
+ /Title (Isís 1 -> 5: /XYZ null null null)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 5 0
+5 0 obj
+<<
+ /Dest [
+ null
+ /XYZ
+ 66
+ 756
+ 3
+ ]
+ /Parent 2 0 R
+ /Prev 4 0 R
+ /Title (Trepak 2 -> 15: /XYZ 66 756 3)
+ /Type /Outline
+>>
+endobj
+
+%% Page 1
+%% Original object ID: 6 0
+6 0 obj
+<<
+ /Contents 21 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 23 0 R
+ >>
+ /ProcSet 24 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 2
+%% Original object ID: 7 0
+7 0 obj
+<<
+ /Contents 25 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 23 0 R
+ >>
+ /ProcSet 24 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 3
+%% Original object ID: 47 0
+8 0 obj
+<<
+ /Contents 27 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 29 0 R
+ >>
+ /ProcSet 30 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 4
+%% Original object ID: 51 0
+9 0 obj
+<<
+ /Contents 31 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 33 0 R
+ >>
+ /ProcSet 34 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 5
+%% Original object ID: 55 0
+10 0 obj
+<<
+ /Contents 35 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 33 0 R
+ >>
+ /ProcSet 34 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 6
+%% Original object ID: 57 0
+11 0 obj
+<<
+ /Contents 37 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 33 0 R
+ >>
+ /ProcSet 34 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 7
+%% Original object ID: 59 0
+12 0 obj
+<<
+ /Contents 39 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 33 0 R
+ >>
+ /ProcSet 34 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 8
+%% Original object ID: 8 0
+13 0 obj
+<<
+ /Contents 41 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 23 0 R
+ >>
+ /ProcSet 24 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 9
+%% Original object ID: 9 0
+14 0 obj
+<<
+ /Contents 43 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 23 0 R
+ >>
+ /ProcSet 24 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 10
+%% Original object ID: 61 0
+15 0 obj
+<<
+ /Contents 45 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 33 0 R
+ >>
+ /ProcSet 34 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 11
+%% Original object ID: 63 0
+16 0 obj
+<<
+ /Contents 47 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 33 0 R
+ >>
+ /ProcSet 34 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 12
+%% Original object ID: 65 0
+17 0 obj
+<<
+ /Contents 49 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 33 0 R
+ >>
+ /ProcSet 34 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 13
+%% Original object ID: 10 0
+18 0 obj
+<<
+ /Contents 51 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 23 0 R
+ >>
+ /ProcSet 24 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Original object ID: 21 0
+19 0 obj
+<<
+ /Count -3
+ /Dest [
+ null
+ /Fit
+ ]
+ /First 53 0 R
+ /Last 54 0 R
+ /Next 20 0 R
+ /Parent 4 0 R
+ /Title (Amanda 1.1 -> 11: /Fit)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 22 0
+20 0 obj
+<<
+ /Count 2
+ /Dest [
+ null
+ /FitH
+ 792
+ ]
+ /First 55 0 R
+ /Last 56 0 R
+ /Parent 4 0 R
+ /Prev 19 0 R
+ /Title <feff00530061006e00640079002000f703a303b103bd03b403b900f700200031002e00320020002d003e002000310033003a0020002f00460069007400480020003700390032>
+ /Type /Outline
+>>
+endobj
+
+%% Contents for page 1
+%% Original object ID: 23 0
+21 0 obj
+<<
+ /Length 22 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 0) Tj
+ET
+endstream
+endobj
+
+22 0 obj
+46
+endobj
+
+%% Original object ID: 24 0
+23 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Original object ID: 25 0
+24 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+%% Contents for page 2
+%% Original object ID: 26 0
+25 0 obj
+<<
+ /Length 26 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 2) Tj
+ET
+endstream
+endobj
+
+26 0 obj
+46
+endobj
+
+%% Contents for page 3
+%% Original object ID: 48 0
+27 0 obj
+<<
+ /Length 28 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+28 0 obj
+44
+endobj
+
+%% Original object ID: 49 0
+29 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Original object ID: 50 0
+30 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+%% Contents for page 4
+%% Original object ID: 52 0
+31 0 obj
+<<
+ /Length 32 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 13) Tj
+ET
+endstream
+endobj
+
+32 0 obj
+47
+endobj
+
+%% Original object ID: 53 0
+33 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Original object ID: 54 0
+34 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+%% Contents for page 5
+%% Original object ID: 56 0
+35 0 obj
+<<
+ /Length 36 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 11) Tj
+ET
+endstream
+endobj
+
+36 0 obj
+47
+endobj
+
+%% Contents for page 6
+%% Original object ID: 58 0
+37 0 obj
+<<
+ /Length 38 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 9) Tj
+ET
+endstream
+endobj
+
+38 0 obj
+46
+endobj
+
+%% Contents for page 7
+%% Original object ID: 60 0
+39 0 obj
+<<
+ /Length 40 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 7) Tj
+ET
+endstream
+endobj
+
+40 0 obj
+46
+endobj
+
+%% Contents for page 8
+%% Original object ID: 27 0
+41 0 obj
+<<
+ /Length 42 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 4) Tj
+ET
+endstream
+endobj
+
+42 0 obj
+46
+endobj
+
+%% Contents for page 9
+%% Original object ID: 28 0
+43 0 obj
+<<
+ /Length 44 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 6) Tj
+ET
+endstream
+endobj
+
+44 0 obj
+46
+endobj
+
+%% Contents for page 10
+%% Original object ID: 62 0
+45 0 obj
+<<
+ /Length 46 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 5) Tj
+ET
+endstream
+endobj
+
+46 0 obj
+46
+endobj
+
+%% Contents for page 11
+%% Original object ID: 64 0
+47 0 obj
+<<
+ /Length 48 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 3) Tj
+ET
+endstream
+endobj
+
+48 0 obj
+46
+endobj
+
+%% Contents for page 12
+%% Original object ID: 66 0
+49 0 obj
+<<
+ /Length 50 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 1) Tj
+ET
+endstream
+endobj
+
+50 0 obj
+46
+endobj
+
+%% Contents for page 13
+%% Original object ID: 29 0
+51 0 obj
+<<
+ /Length 52 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 8) Tj
+ET
+endstream
+endobj
+
+52 0 obj
+46
+endobj
+
+%% Original object ID: 40 0
+53 0 obj
+<<
+ /Count -2
+ /Dest [
+ 57 0 R
+ /FitV
+ 100
+ ]
+ /First 58 0 R
+ /Last 59 0 R
+ /Next 54 0 R
+ /Parent 19 0 R
+ /Title (Isosicle 1.1.1 -> 12: /FitV 100)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 41 0
+54 0 obj
+<<
+ /Count 1
+ /Dest [
+ 57 0 R
+ /XYZ
+ null
+ null
+ null
+ ]
+ /First 60 0 R
+ /Last 60 0 R
+ /Parent 19 0 R
+ /Prev 53 0 R
+ /Title (Isosicle 1.1.2 -> 12: /XYZ null null null)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 42 0
+55 0 obj
+<<
+ /Dest [
+ null
+ /FitR
+ 66
+ 714
+ 180
+ 770
+ ]
+ /Next 56 0 R
+ /Parent 20 0 R
+ /Title (Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 43 0
+56 0 obj
+<<
+ /Dest [
+ 6 0 R
+ /XYZ
+ null
+ null
+ null
+ ]
+ /Parent 20 0 R
+ /Prev 55 0 R
+ /Title (Trepsicle 1.2.2 -> 0: /XYZ null null null)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 12 0
+57 0 obj
+null
+endobj
+
+%% Original object ID: 44 0
+58 0 obj
+<<
+ /Dest [
+ 61 0 R
+ /XYZ
+ null
+ null
+ null
+ ]
+ /Next 59 0 R
+ /Parent 53 0 R
+ /Title (Isosicle 1.1.1.1 -> 18: /XYZ null null null)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 45 0
+59 0 obj
+<<
+ /Dest [
+ null
+ /XYZ
+ null
+ null
+ null
+ ]
+ /Parent 53 0 R
+ /Prev 58 0 R
+ /Title (Isosicle 1.1.1.2 -> 19: /XYZ null null null)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 46 0
+60 0 obj
+<<
+ /Dest [
+ 62 0 R
+ /XYZ
+ null
+ null
+ null
+ ]
+ /Parent 54 0 R
+ /Title (Isosicle 1.1.2.1 -> 22: /XYZ null null null)
+ /Type /Outline
+>>
+endobj
+
+%% Original object ID: 15 0
+61 0 obj
+null
+endobj
+
+%% Original object ID: 17 0
+62 0 obj
+null
+endobj
+
+xref
+0 63
+0000000000 65535 f
+0000000052 00000 n
+0000000907 00000 n
+0000001014 00000 n
+0000001243 00000 n
+0000001484 00000 n
+0000001684 00000 n
+0000001916 00000 n
+0000002149 00000 n
+0000002382 00000 n
+0000002615 00000 n
+0000002849 00000 n
+0000003083 00000 n
+0000003316 00000 n
+0000003549 00000 n
+0000003784 00000 n
+0000004019 00000 n
+0000004254 00000 n
+0000004489 00000 n
+0000004713 00000 n
+0000004921 00000 n
+0000005278 00000 n
+0000005381 00000 n
+0000005429 00000 n
+0000005576 00000 n
+0000005663 00000 n
+0000005766 00000 n
+0000005837 00000 n
+0000005938 00000 n
+0000005986 00000 n
+0000006133 00000 n
+0000006220 00000 n
+0000006324 00000 n
+0000006372 00000 n
+0000006519 00000 n
+0000006606 00000 n
+0000006710 00000 n
+0000006781 00000 n
+0000006884 00000 n
+0000006955 00000 n
+0000007058 00000 n
+0000007129 00000 n
+0000007232 00000 n
+0000007303 00000 n
+0000007406 00000 n
+0000007478 00000 n
+0000007581 00000 n
+0000007653 00000 n
+0000007756 00000 n
+0000007828 00000 n
+0000007931 00000 n
+0000008003 00000 n
+0000008106 00000 n
+0000008154 00000 n
+0000008383 00000 n
+0000008639 00000 n
+0000008859 00000 n
+0000009072 00000 n
+0000009122 00000 n
+0000009338 00000 n
+0000009552 00000 n
+0000009753 00000 n
+0000009803 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 63
+ /ID [<d3fab8d0603e683dc94e42ac31141868><31415926535897932384626433832795>]
+>>
+startxref
+9825
+%%EOF