aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2018-12-18 17:29:00 +0100
committerJay Berkenbilt <ejb@ql.org>2018-12-18 22:59:24 +0100
commit6ef9e31233723f2e73a4ed9bc3cd1fcaa6c69b5d (patch)
treefbf03926470e1cc7b4c4abd06b88c2d8d16ea0d9
parentf38df27aa3eae905e3ee90365099335e317173d8 (diff)
downloadqpdf-6ef9e31233723f2e73a4ed9bc3cd1fcaa6c69b5d.tar.zst
Add QPDFPageLabelDocumentHelper
-rw-r--r--ChangeLog7
-rw-r--r--include/qpdf/QPDFPageLabelDocumentHelper.hh100
-rw-r--r--libqpdf/QPDFPageLabelDocumentHelper.cc125
-rw-r--r--libqpdf/build.mk1
-rw-r--r--qpdf/qtest/qpdf.test18
-rw-r--r--qpdf/qtest/qpdf/no-page-labels.out2
-rw-r--r--qpdf/qtest/qpdf/page-labels-no-zero.out15
-rw-r--r--qpdf/qtest/qpdf/page-labels-no-zero.pdf1364
-rw-r--r--qpdf/qtest/qpdf/page-labels-num-tree.out15
-rw-r--r--qpdf/qtest/qpdf/page-labels-num-tree.pdf1417
-rw-r--r--qpdf/test_driver.cc16
11 files changed, 3080 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 27f790f2..75fad549 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
2018-12-18 Jay Berkenbilt <ejb@ql.org>
+ * Add QPDFPageLabelDocumentHelper class. This is a document helper
+ class that provides useful methods for dealing with page labels.
+ It abstracts the fact that they are stored as number trees and
+ deals with interpolating intermediate values that are not in the
+ tree. It also has helper functions used by the qpdf command line
+ tool to preserve page labels when merging and splitting files.
+
* Add QPDFNumberTreeObjectHelper class. This class provides useful
methods for dealing with number trees, which are discussed in
section 7.9.7 of the PDF spec (ISO-32000). Page label dictionaries
diff --git a/include/qpdf/QPDFPageLabelDocumentHelper.hh b/include/qpdf/QPDFPageLabelDocumentHelper.hh
new file mode 100644
index 00000000..a5a13ff2
--- /dev/null
+++ b/include/qpdf/QPDFPageLabelDocumentHelper.hh
@@ -0,0 +1,100 @@
+// Copyright (c) 2005-2018 Jay Berkenbilt
+//
+// This file is part of qpdf.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Versions of qpdf prior to version 7 were released under the terms
+// of version 2.0 of the Artistic License. At your option, you may
+// continue to consider qpdf to be licensed under those terms. Please
+// see the manual for additional information.
+
+#ifndef QPDFPAGELABELDOCUMENTHELPER_HH
+#define QPDFPAGELABELDOCUMENTHELPER_HH
+
+#include <qpdf/QPDFDocumentHelper.hh>
+
+#include <qpdf/QPDF.hh>
+#include <qpdf/QPDFNumberTreeObjectHelper.hh>
+#include <vector>
+
+#include <qpdf/DLL.h>
+
+// Page labels are discussed in the PDF spec (ISO-32000) in section
+// 12.4.2.
+//
+// Page labels are implemented as a number tree. Each key is a page
+// index, numbered from 0. The values are dictionaries with the
+// following keys, all optional:
+//
+// * /Type: if present, must be /PageLabel
+// * /S: one of /D, /R, /r, /A, or /a for decimal, upper-case and
+// lower-case Roman numeral, or upper-case and lower-case alphabetic
+// * /P: if present, a fixed prefix string that is prepended to each
+// page number
+// * /St: the starting number, or 1 if not specified
+
+class QPDFPageLabelDocumentHelper: public QPDFDocumentHelper
+{
+ public:
+ QPDF_DLL
+ QPDFPageLabelDocumentHelper(QPDF&);
+
+ QPDF_DLL
+ bool hasPageLabels();
+
+ // Return a page label dictionary representing the page label for
+ // the given page. The page does not need to appear explicitly in
+ // the page label dictionary. This method will adjust /St as
+ // needed to produce a label that is suitable for the page.
+ QPDF_DLL
+ QPDFObjectHandle getLabelForPage(long long page_idx);
+
+ // Append to the incoming vector a list of objects suitable for
+ // inclusion in a /PageLabels dictionary's /Nums field. start_idx
+ // and end_idx are the indexes to the starting and ending pages
+ // (inclusive) in the original file, and new_start_idx is the
+ // index to the first page in the new file. For example, if pages
+ // 10 through 12 of one file are being copied to a new file as
+ // pages 6 through 8, you would call getLabelsForPageRange(10, 12,
+ // 6), which would return as many entries as are required to add
+ // to the new file's PageLabels. This method fabricates a suitable
+ // entry even if the original document has no page labels. This
+ // behavior facilitates using this function to incrementally build
+ // up a page labels tree when merging files.
+ QPDF_DLL
+ void
+ getLabelsForPageRange(long long start_idx, long long end_idx,
+ long long new_start_idx,
+ std::vector<QPDFObjectHandle>& new_labels);
+
+ private:
+ class Members
+ {
+ friend class QPDFPageLabelDocumentHelper;
+
+ public:
+ QPDF_DLL
+ ~Members();
+
+ private:
+ Members();
+ Members(Members const&);
+
+ PointerHolder<QPDFNumberTreeObjectHelper> labels;
+ };
+
+ PointerHolder<Members> m;
+};
+
+#endif // QPDFPAGELABELDOCUMENTHELPER_HH
diff --git a/libqpdf/QPDFPageLabelDocumentHelper.cc b/libqpdf/QPDFPageLabelDocumentHelper.cc
new file mode 100644
index 00000000..63be5809
--- /dev/null
+++ b/libqpdf/QPDFPageLabelDocumentHelper.cc
@@ -0,0 +1,125 @@
+#include <qpdf/QPDFPageLabelDocumentHelper.hh>
+#include <qpdf/QTC.hh>
+
+QPDFPageLabelDocumentHelper::Members::~Members()
+{
+}
+
+QPDFPageLabelDocumentHelper::Members::Members()
+{
+}
+
+QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF& qpdf) :
+ QPDFDocumentHelper(qpdf),
+ m(new Members())
+{
+ QPDFObjectHandle root = qpdf.getRoot();
+ if (root.hasKey("/PageLabels"))
+ {
+ this->m->labels = new QPDFNumberTreeObjectHelper(
+ root.getKey("/PageLabels"));
+ }
+}
+
+bool
+QPDFPageLabelDocumentHelper::hasPageLabels()
+{
+ return 0 != this->m->labels.getPointer();
+}
+
+QPDFObjectHandle
+QPDFPageLabelDocumentHelper::getLabelForPage(long long page_idx)
+{
+ QPDFObjectHandle result(QPDFObjectHandle::newNull());
+ if (! hasPageLabels())
+ {
+ return result;
+ }
+ QPDFNumberTreeObjectHelper::numtree_number offset = 0;
+ QPDFObjectHandle label;
+ if (! this->m->labels->findObjectAtOrBelow(page_idx, label, offset))
+ {
+ return result;
+ }
+ if (! label.isDictionary())
+ {
+ return result;
+ }
+ QPDFObjectHandle S = label.getKey("/S"); // type (D, R, r, A, a)
+ QPDFObjectHandle P = label.getKey("/P"); // prefix
+ QPDFObjectHandle St = label.getKey("/St"); // starting number
+ long long start = 1;
+ if (St.isInteger())
+ {
+ start = St.getIntValue();
+ }
+ start += offset;
+ result = QPDFObjectHandle::newDictionary();
+ result.replaceOrRemoveKey("/S", S);
+ result.replaceOrRemoveKey("/P", P);
+ result.replaceOrRemoveKey("/St", QPDFObjectHandle::newInteger(start));
+ return result;
+}
+
+void
+QPDFPageLabelDocumentHelper::getLabelsForPageRange(
+ long long start_idx, long long end_idx, long long new_start_idx,
+ std::vector<QPDFObjectHandle>& new_labels)
+{
+ // Start off with a suitable label for the first page. For every
+ // remaining page, if that page has an explicit entry, copy it.
+ // Otherwise, let the subsequent page just sequence from the prior
+ // entry. If there is no entry for the first page, fabricate one
+ // that would match how the page would look in a new file in which
+ // it also didn't have an explicit label.
+ QPDFObjectHandle label = getLabelForPage(start_idx);
+ if (label.isNull())
+ {
+ label = QPDFObjectHandle::newDictionary();
+ label.replaceKey(
+ "/St", QPDFObjectHandle::newInteger(1 + new_start_idx));
+ }
+ // See if the new label is redundant based on the previous entry
+ // in the vector. If so, don't add it.
+ size_t size = new_labels.size();
+ bool skip_first = false;
+ if (size >= 2)
+ {
+ QPDFObjectHandle last = new_labels[size - 1];
+ QPDFObjectHandle last_idx = new_labels[size - 2];
+ if (last_idx.isInteger() && last.isDictionary() &&
+ (label.getKey("/S").unparse() == last.getKey("/S").unparse()) &&
+ (label.getKey("/P").unparse() == last.getKey("/P").unparse()) &&
+ label.getKey("/St").isInteger() &&
+ last.getKey("/St").isInteger())
+ {
+ long long int st_delta =
+ label.getKey("/St").getIntValue() -
+ last.getKey("/St").getIntValue();
+ long long int idx_delta =
+ new_start_idx - last_idx.getIntValue();
+ if (st_delta == idx_delta)
+ {
+ QTC::TC("qpdf", "QPDFPageLabelDocumentHelper skip first");
+ skip_first = true;
+ }
+ }
+ }
+ if (! skip_first)
+ {
+ new_labels.push_back(QPDFObjectHandle::newInteger(new_start_idx));
+ new_labels.push_back(label);
+ }
+
+ long long int idx_offset = new_start_idx - start_idx;
+ for (long long i = start_idx + 1; i <= end_idx; ++i)
+ {
+ if (this->m->labels->hasIndex(i) &&
+ (label = getLabelForPage(i)).isDictionary())
+ {
+ new_labels.push_back(QPDFObjectHandle::newInteger(i + idx_offset));
+ new_labels.push_back(label);
+ }
+ }
+}
+
diff --git a/libqpdf/build.mk b/libqpdf/build.mk
index 147bb16a..8a2030d1 100644
--- a/libqpdf/build.mk
+++ b/libqpdf/build.mk
@@ -45,6 +45,7 @@ SRCS_libqpdf = \
libqpdf/QPDFObject.cc \
libqpdf/QPDFObjectHandle.cc \
libqpdf/QPDFPageDocumentHelper.cc \
+ libqpdf/QPDFPageLabelDocumentHelper.cc \
libqpdf/QPDFPageObjectHelper.cc \
libqpdf/QPDFSystemError.cc \
libqpdf/QPDFTokenizer.cc \
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 9ea5b61d..aba78938 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -236,6 +236,24 @@ $td->runtest("number trees",
show_ntests();
# ----------
+$td->notify("--- Page Labels ---");
+$n_tests += 3;
+
+$td->runtest("complex page labels",
+ {$td->COMMAND => "test_driver 47 page-labels-num-tree.pdf"},
+ {$td->FILE => "page-labels-num-tree.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("no zero entry for page labels",
+ {$td->COMMAND => "test_driver 47 page-labels-no-zero.pdf"},
+ {$td->FILE => "page-labels-no-zero.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("no page labels",
+ {$td->COMMAND => "test_driver 47 minimal.pdf"},
+ {$td->FILE => "no-page-labels.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+show_ntests();
+# ----------
$td->notify("--- Page API Tests ---");
$n_tests += 9;
diff --git a/qpdf/qtest/qpdf/no-page-labels.out b/qpdf/qtest/qpdf/no-page-labels.out
new file mode 100644
index 00000000..f8ebd91d
--- /dev/null
+++ b/qpdf/qtest/qpdf/no-page-labels.out
@@ -0,0 +1,2 @@
+1 << /St 2 >>
+test 47 done
diff --git a/qpdf/qtest/qpdf/page-labels-no-zero.out b/qpdf/qtest/qpdf/page-labels-no-zero.out
new file mode 100644
index 00000000..7d9c926e
--- /dev/null
+++ b/qpdf/qtest/qpdf/page-labels-no-zero.out
@@ -0,0 +1,15 @@
+1 << /St 2 >>
+3 << /P (blank) /St 1 >>
+4 << /P (X-) /S /A /St 17 >>
+6 << /P () /St 1 >>
+7 << /S /R /St 3 >>
+10 << /S /D /St 1 >>
+12 << /S /a /St 1 >>
+13 << /S /a /St 3 >>
+16 << /P (q.) /S /D /St 6 >>
+20 << /P (www) /St 1 >>
+21 << /S /D /St 12 >>
+23 << /S /D /St 16059 >>
+24 << /S /R /St 50 >>
+30 << /S /r /St 54 >>
+test 47 done
diff --git a/qpdf/qtest/qpdf/page-labels-no-zero.pdf b/qpdf/qtest/qpdf/page-labels-no-zero.pdf
new file mode 100644
index 00000000..1d106988
--- /dev/null
+++ b/qpdf/qtest/qpdf/page-labels-no-zero.pdf
@@ -0,0 +1,1364 @@
+%PDF-1.3
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /PageLabels 2 0 R
+ /Pages 3 0 R
+ /Type /Catalog
+>>
+endobj
+
+2 0 obj
+<<
+ /Nums [
+ 2 << /P (blank) >>
+ 3 << /P (X-) /S /A /St 17 >>
+ 5 << /P () >>
+ 6 << /S /R /St 3 >>
+ 9 << /S /D >>
+ 11 << /S /a >>
+ 12 << /S /a /St 3 >>
+ 15 << /P (q.) /S /D /St 6 >>
+ 19 << /P (www) >>
+ 20 << /S /D /St 12 >>
+ 22 << /S /D /St 16059 >>
+ 23 << /S /R /St 50 >>
+ 29 << /S /r /St 54 >>
+ ]
+>>
+endobj
+
+3 0 obj
+<<
+ /Count 30
+ /Kids [
+ 4 0 R
+ 5 0 R
+ 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
+ 19 0 R
+ 20 0 R
+ 21 0 R
+ 22 0 R
+ 23 0 R
+ 24 0 R
+ 25 0 R
+ 26 0 R
+ 27 0 R
+ 28 0 R
+ 29 0 R
+ 30 0 R
+ 31 0 R
+ 32 0 R
+ 33 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+4 0 obj
+<<
+ /Contents 34 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 2
+5 0 obj
+<<
+ /Contents 38 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 3
+6 0 obj
+<<
+ /Contents 40 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 4
+7 0 obj
+<<
+ /Contents 42 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 5
+8 0 obj
+<<
+ /Contents 44 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 6
+9 0 obj
+<<
+ /Contents 46 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 7
+10 0 obj
+<<
+ /Contents 48 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 8
+11 0 obj
+<<
+ /Contents 50 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 9
+12 0 obj
+<<
+ /Contents 52 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 10
+13 0 obj
+<<
+ /Contents 54 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 11
+14 0 obj
+<<
+ /Contents 56 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 12
+15 0 obj
+<<
+ /Contents 58 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 13
+16 0 obj
+<<
+ /Contents 60 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 14
+17 0 obj
+<<
+ /Contents 62 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 15
+18 0 obj
+<<
+ /Contents 64 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 16
+19 0 obj
+<<
+ /Contents 66 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 17
+20 0 obj
+<<
+ /Contents 68 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 18
+21 0 obj
+<<
+ /Contents 70 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 19
+22 0 obj
+<<
+ /Contents 72 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 20
+23 0 obj
+<<
+ /Contents 74 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 21
+24 0 obj
+<<
+ /Contents 76 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 22
+25 0 obj
+<<
+ /Contents 78 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 23
+26 0 obj
+<<
+ /Contents 80 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 24
+27 0 obj
+<<
+ /Contents 82 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 25
+28 0 obj
+<<
+ /Contents 84 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 26
+29 0 obj
+<<
+ /Contents 86 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 27
+30 0 obj
+<<
+ /Contents 88 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 28
+31 0 obj
+<<
+ /Contents 90 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 29
+32 0 obj
+<<
+ /Contents 92 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 30
+33 0 obj
+<<
+ /Contents 94 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 36 0 R
+ >>
+ /ProcSet 37 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+34 0 obj
+<<
+ /Length 35 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 0) Tj
+ET
+endstream
+endobj
+
+35 0 obj
+46
+endobj
+
+36 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+37 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+%% Contents for page 2
+38 0 obj
+<<
+ /Length 39 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 1) Tj
+ET
+endstream
+endobj
+
+39 0 obj
+46
+endobj
+
+%% Contents for page 3
+40 0 obj
+<<
+ /Length 41 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 2) Tj
+ET
+endstream
+endobj
+
+41 0 obj
+46
+endobj
+
+%% Contents for page 4
+42 0 obj
+<<
+ /Length 43 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 3) Tj
+ET
+endstream
+endobj
+
+43 0 obj
+46
+endobj
+
+%% Contents for page 5
+44 0 obj
+<<
+ /Length 45 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 4) Tj
+ET
+endstream
+endobj
+
+45 0 obj
+46
+endobj
+
+%% Contents for page 6
+46 0 obj
+<<
+ /Length 47 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 5) Tj
+ET
+endstream
+endobj
+
+47 0 obj
+46
+endobj
+
+%% Contents for page 7
+48 0 obj
+<<
+ /Length 49 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 6) Tj
+ET
+endstream
+endobj
+
+49 0 obj
+46
+endobj
+
+%% Contents for page 8
+50 0 obj
+<<
+ /Length 51 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 7) Tj
+ET
+endstream
+endobj
+
+51 0 obj
+46
+endobj
+
+%% Contents for page 9
+52 0 obj
+<<
+ /Length 53 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 8) Tj
+ET
+endstream
+endobj
+
+53 0 obj
+46
+endobj
+
+%% Contents for page 10
+54 0 obj
+<<
+ /Length 55 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 9) Tj
+ET
+endstream
+endobj
+
+55 0 obj
+46
+endobj
+
+%% Contents for page 11
+56 0 obj
+<<
+ /Length 57 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 10) Tj
+ET
+endstream
+endobj
+
+57 0 obj
+47
+endobj
+
+%% Contents for page 12
+58 0 obj
+<<
+ /Length 59 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 11) Tj
+ET
+endstream
+endobj
+
+59 0 obj
+47
+endobj
+
+%% Contents for page 13
+60 0 obj
+<<
+ /Length 61 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 12) Tj
+ET
+endstream
+endobj
+
+61 0 obj
+47
+endobj
+
+%% Contents for page 14
+62 0 obj
+<<
+ /Length 63 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 13) Tj
+ET
+endstream
+endobj
+
+63 0 obj
+47
+endobj
+
+%% Contents for page 15
+64 0 obj
+<<
+ /Length 65 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 14) Tj
+ET
+endstream
+endobj
+
+65 0 obj
+47
+endobj
+
+%% Contents for page 16
+66 0 obj
+<<
+ /Length 67 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 15) Tj
+ET
+endstream
+endobj
+
+67 0 obj
+47
+endobj
+
+%% Contents for page 17
+68 0 obj
+<<
+ /Length 69 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 16) Tj
+ET
+endstream
+endobj
+
+69 0 obj
+47
+endobj
+
+%% Contents for page 18
+70 0 obj
+<<
+ /Length 71 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 17) Tj
+ET
+endstream
+endobj
+
+71 0 obj
+47
+endobj
+
+%% Contents for page 19
+72 0 obj
+<<
+ /Length 73 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 18) Tj
+ET
+endstream
+endobj
+
+73 0 obj
+47
+endobj
+
+%% Contents for page 20
+74 0 obj
+<<
+ /Length 75 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 19) Tj
+ET
+endstream
+endobj
+
+75 0 obj
+47
+endobj
+
+%% Contents for page 21
+76 0 obj
+<<
+ /Length 77 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 20) Tj
+ET
+endstream
+endobj
+
+77 0 obj
+47
+endobj
+
+%% Contents for page 22
+78 0 obj
+<<
+ /Length 79 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 21) Tj
+ET
+endstream
+endobj
+
+79 0 obj
+47
+endobj
+
+%% Contents for page 23
+80 0 obj
+<<
+ /Length 81 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 22) Tj
+ET
+endstream
+endobj
+
+81 0 obj
+47
+endobj
+
+%% Contents for page 24
+82 0 obj
+<<
+ /Length 83 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 23) Tj
+ET
+endstream
+endobj
+
+83 0 obj
+47
+endobj
+
+%% Contents for page 25
+84 0 obj
+<<
+ /Length 85 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 24) Tj
+ET
+endstream
+endobj
+
+85 0 obj
+47
+endobj
+
+%% Contents for page 26
+86 0 obj
+<<
+ /Length 87 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 25) Tj
+ET
+endstream
+endobj
+
+87 0 obj
+47
+endobj
+
+%% Contents for page 27
+88 0 obj
+<<
+ /Length 89 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 26) Tj
+ET
+endstream
+endobj
+
+89 0 obj
+47
+endobj
+
+%% Contents for page 28
+90 0 obj
+<<
+ /Length 91 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 27) Tj
+ET
+endstream
+endobj
+
+91 0 obj
+47
+endobj
+
+%% Contents for page 29
+92 0 obj
+<<
+ /Length 93 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 28) Tj
+ET
+endstream
+endobj
+
+93 0 obj
+47
+endobj
+
+%% Contents for page 30
+94 0 obj
+<<
+ /Length 95 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 29) Tj
+ET
+endstream
+endobj
+
+95 0 obj
+47
+endobj
+
+xref
+0 96
+0000000000 65535 f
+0000000025 00000 n
+0000000099 00000 n
+0000000457 00000 n
+0000000854 00000 n
+0000001059 00000 n
+0000001264 00000 n
+0000001469 00000 n
+0000001674 00000 n
+0000001879 00000 n
+0000002084 00000 n
+0000002290 00000 n
+0000002496 00000 n
+0000002703 00000 n
+0000002910 00000 n
+0000003117 00000 n
+0000003324 00000 n
+0000003531 00000 n
+0000003738 00000 n
+0000003945 00000 n
+0000004152 00000 n
+0000004359 00000 n
+0000004566 00000 n
+0000004773 00000 n
+0000004980 00000 n
+0000005187 00000 n
+0000005394 00000 n
+0000005601 00000 n
+0000005808 00000 n
+0000006015 00000 n
+0000006222 00000 n
+0000006429 00000 n
+0000006636 00000 n
+0000006843 00000 n
+0000007062 00000 n
+0000007165 00000 n
+0000007185 00000 n
+0000007304 00000 n
+0000007363 00000 n
+0000007466 00000 n
+0000007509 00000 n
+0000007612 00000 n
+0000007655 00000 n
+0000007758 00000 n
+0000007801 00000 n
+0000007904 00000 n
+0000007947 00000 n
+0000008050 00000 n
+0000008093 00000 n
+0000008196 00000 n
+0000008239 00000 n
+0000008342 00000 n
+0000008385 00000 n
+0000008488 00000 n
+0000008532 00000 n
+0000008635 00000 n
+0000008679 00000 n
+0000008783 00000 n
+0000008827 00000 n
+0000008931 00000 n
+0000008975 00000 n
+0000009079 00000 n
+0000009123 00000 n
+0000009227 00000 n
+0000009271 00000 n
+0000009375 00000 n
+0000009419 00000 n
+0000009523 00000 n
+0000009567 00000 n
+0000009671 00000 n
+0000009715 00000 n
+0000009819 00000 n
+0000009863 00000 n
+0000009967 00000 n
+0000010011 00000 n
+0000010115 00000 n
+0000010159 00000 n
+0000010263 00000 n
+0000010307 00000 n
+0000010411 00000 n
+0000010455 00000 n
+0000010559 00000 n
+0000010603 00000 n
+0000010707 00000 n
+0000010751 00000 n
+0000010855 00000 n
+0000010899 00000 n
+0000011003 00000 n
+0000011047 00000 n
+0000011151 00000 n
+0000011195 00000 n
+0000011299 00000 n
+0000011343 00000 n
+0000011447 00000 n
+0000011491 00000 n
+0000011595 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 96
+ /ID [<90f919de7874f3bc5cb7afbd1e9537bb><55983ed25b1ed15804ad4831bad000da>]
+>>
+startxref
+11615
+%%EOF
diff --git a/qpdf/qtest/qpdf/page-labels-num-tree.out b/qpdf/qtest/qpdf/page-labels-num-tree.out
new file mode 100644
index 00000000..0f3ccdbc
--- /dev/null
+++ b/qpdf/qtest/qpdf/page-labels-num-tree.out
@@ -0,0 +1,15 @@
+1 << /S /r /St 1 >>
+3 << /P (blank) /St 1 >>
+4 << /P (X-) /S /A /St 17 >>
+6 << /P () /St 1 >>
+7 << /S /R /St 3 >>
+10 << /S /D /St 1 >>
+12 << /S /a /St 1 >>
+13 << /S /a /St 3 >>
+16 << /P (q.) /S /D /St 6 >>
+20 << /P (www) /St 1 >>
+21 << /S /D /St 12 >>
+23 << /S /D /St 16059 >>
+24 << /S /R /St 50 >>
+30 << /S /r /St 54 >>
+test 47 done
diff --git a/qpdf/qtest/qpdf/page-labels-num-tree.pdf b/qpdf/qtest/qpdf/page-labels-num-tree.pdf
new file mode 100644
index 00000000..ad0ad0fc
--- /dev/null
+++ b/qpdf/qtest/qpdf/page-labels-num-tree.pdf
@@ -0,0 +1,1417 @@
+%PDF-1.3
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /PageLabels 2 0 R
+ /Pages 3 0 R
+ /Type /Catalog
+>>
+endobj
+
+2 0 obj
+<<
+ /Kids [
+ 4 0 R
+ 5 0 R
+ ]
+>>
+endobj
+
+3 0 obj
+<<
+ /Count 30
+ /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
+ 19 0 R
+ 20 0 R
+ 21 0 R
+ 22 0 R
+ 23 0 R
+ 24 0 R
+ 25 0 R
+ 26 0 R
+ 27 0 R
+ 28 0 R
+ 29 0 R
+ 30 0 R
+ 31 0 R
+ 32 0 R
+ 33 0 R
+ 34 0 R
+ 35 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+4 0 obj
+<<
+ /Kids [
+ 36 0 R
+ 37 0 R
+ ]
+ /Limits [
+ 0
+ 19
+ ]
+>>
+endobj
+
+5 0 obj
+<<
+ /Limits [
+ 20
+ 29
+ ]
+ /Nums [
+ 20 << /S /D /St 12 >>
+ 22 << /S /D /St 16059 >>
+ 23 << /S /R /St 50 >>
+ 29 << /S /r /St 54 >>
+ ]
+>>
+endobj
+
+%% Page 1
+6 0 obj
+<<
+ /Contents 38 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 2
+7 0 obj
+<<
+ /Contents 42 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 3
+8 0 obj
+<<
+ /Contents 44 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 4
+9 0 obj
+<<
+ /Contents 46 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 5
+10 0 obj
+<<
+ /Contents 48 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 6
+11 0 obj
+<<
+ /Contents 50 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 7
+12 0 obj
+<<
+ /Contents 52 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 8
+13 0 obj
+<<
+ /Contents 54 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 9
+14 0 obj
+<<
+ /Contents 56 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 10
+15 0 obj
+<<
+ /Contents 58 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 11
+16 0 obj
+<<
+ /Contents 60 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 12
+17 0 obj
+<<
+ /Contents 62 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 13
+18 0 obj
+<<
+ /Contents 64 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 14
+19 0 obj
+<<
+ /Contents 66 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 15
+20 0 obj
+<<
+ /Contents 68 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 16
+21 0 obj
+<<
+ /Contents 70 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 17
+22 0 obj
+<<
+ /Contents 72 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 18
+23 0 obj
+<<
+ /Contents 74 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 19
+24 0 obj
+<<
+ /Contents 76 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 20
+25 0 obj
+<<
+ /Contents 78 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 21
+26 0 obj
+<<
+ /Contents 80 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 22
+27 0 obj
+<<
+ /Contents 82 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 23
+28 0 obj
+<<
+ /Contents 84 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 24
+29 0 obj
+<<
+ /Contents 86 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 25
+30 0 obj
+<<
+ /Contents 88 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 26
+31 0 obj
+<<
+ /Contents 90 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 27
+32 0 obj
+<<
+ /Contents 92 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 28
+33 0 obj
+<<
+ /Contents 94 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 29
+34 0 obj
+<<
+ /Contents 96 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 30
+35 0 obj
+<<
+ /Contents 98 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 40 0 R
+ >>
+ /ProcSet 41 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+36 0 obj
+<<
+ /Limits [
+ 0
+ 9
+ ]
+ /Nums [
+ 0 << /S /r >>
+ 2 << /P (blank) >>
+ 3 << /P (X-) /S /A /St 17 >>
+ 5 << /P () >>
+ 6 << /S /R /St 3 >>
+ 9 << /S /D >>
+ ]
+>>
+endobj
+
+37 0 obj
+<<
+ /Limits [
+ 11
+ 19
+ ]
+ /Nums [
+ 11 << /S /a >>
+ 12 << /S /a /St 3 >>
+ 15 << /P (q.) /S /D /St 6 >>
+ 19 << /P (www) >>
+ ]
+>>
+endobj
+
+%% Contents for page 1
+38 0 obj
+<<
+ /Length 39 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 0) Tj
+ET
+endstream
+endobj
+
+39 0 obj
+46
+endobj
+
+40 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+41 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+%% Contents for page 2
+42 0 obj
+<<
+ /Length 43 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 1) Tj
+ET
+endstream
+endobj
+
+43 0 obj
+46
+endobj
+
+%% Contents for page 3
+44 0 obj
+<<
+ /Length 45 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 2) Tj
+ET
+endstream
+endobj
+
+45 0 obj
+46
+endobj
+
+%% Contents for page 4
+46 0 obj
+<<
+ /Length 47 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 3) Tj
+ET
+endstream
+endobj
+
+47 0 obj
+46
+endobj
+
+%% Contents for page 5
+48 0 obj
+<<
+ /Length 49 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 4) Tj
+ET
+endstream
+endobj
+
+49 0 obj
+46
+endobj
+
+%% Contents for page 6
+50 0 obj
+<<
+ /Length 51 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 5) Tj
+ET
+endstream
+endobj
+
+51 0 obj
+46
+endobj
+
+%% Contents for page 7
+52 0 obj
+<<
+ /Length 53 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 6) Tj
+ET
+endstream
+endobj
+
+53 0 obj
+46
+endobj
+
+%% Contents for page 8
+54 0 obj
+<<
+ /Length 55 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 7) Tj
+ET
+endstream
+endobj
+
+55 0 obj
+46
+endobj
+
+%% Contents for page 9
+56 0 obj
+<<
+ /Length 57 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 8) Tj
+ET
+endstream
+endobj
+
+57 0 obj
+46
+endobj
+
+%% Contents for page 10
+58 0 obj
+<<
+ /Length 59 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 9) Tj
+ET
+endstream
+endobj
+
+59 0 obj
+46
+endobj
+
+%% Contents for page 11
+60 0 obj
+<<
+ /Length 61 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 10) Tj
+ET
+endstream
+endobj
+
+61 0 obj
+47
+endobj
+
+%% Contents for page 12
+62 0 obj
+<<
+ /Length 63 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 11) Tj
+ET
+endstream
+endobj
+
+63 0 obj
+47
+endobj
+
+%% Contents for page 13
+64 0 obj
+<<
+ /Length 65 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 12) Tj
+ET
+endstream
+endobj
+
+65 0 obj
+47
+endobj
+
+%% Contents for page 14
+66 0 obj
+<<
+ /Length 67 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 13) Tj
+ET
+endstream
+endobj
+
+67 0 obj
+47
+endobj
+
+%% Contents for page 15
+68 0 obj
+<<
+ /Length 69 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 14) Tj
+ET
+endstream
+endobj
+
+69 0 obj
+47
+endobj
+
+%% Contents for page 16
+70 0 obj
+<<
+ /Length 71 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 15) Tj
+ET
+endstream
+endobj
+
+71 0 obj
+47
+endobj
+
+%% Contents for page 17
+72 0 obj
+<<
+ /Length 73 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 16) Tj
+ET
+endstream
+endobj
+
+73 0 obj
+47
+endobj
+
+%% Contents for page 18
+74 0 obj
+<<
+ /Length 75 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 17) Tj
+ET
+endstream
+endobj
+
+75 0 obj
+47
+endobj
+
+%% Contents for page 19
+76 0 obj
+<<
+ /Length 77 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 18) Tj
+ET
+endstream
+endobj
+
+77 0 obj
+47
+endobj
+
+%% Contents for page 20
+78 0 obj
+<<
+ /Length 79 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 19) Tj
+ET
+endstream
+endobj
+
+79 0 obj
+47
+endobj
+
+%% Contents for page 21
+80 0 obj
+<<
+ /Length 81 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 20) Tj
+ET
+endstream
+endobj
+
+81 0 obj
+47
+endobj
+
+%% Contents for page 22
+82 0 obj
+<<
+ /Length 83 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 21) Tj
+ET
+endstream
+endobj
+
+83 0 obj
+47
+endobj
+
+%% Contents for page 23
+84 0 obj
+<<
+ /Length 85 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 22) Tj
+ET
+endstream
+endobj
+
+85 0 obj
+47
+endobj
+
+%% Contents for page 24
+86 0 obj
+<<
+ /Length 87 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 23) Tj
+ET
+endstream
+endobj
+
+87 0 obj
+47
+endobj
+
+%% Contents for page 25
+88 0 obj
+<<
+ /Length 89 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 24) Tj
+ET
+endstream
+endobj
+
+89 0 obj
+47
+endobj
+
+%% Contents for page 26
+90 0 obj
+<<
+ /Length 91 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 25) Tj
+ET
+endstream
+endobj
+
+91 0 obj
+47
+endobj
+
+%% Contents for page 27
+92 0 obj
+<<
+ /Length 93 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 26) Tj
+ET
+endstream
+endobj
+
+93 0 obj
+47
+endobj
+
+%% Contents for page 28
+94 0 obj
+<<
+ /Length 95 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 27) Tj
+ET
+endstream
+endobj
+
+95 0 obj
+47
+endobj
+
+%% Contents for page 29
+96 0 obj
+<<
+ /Length 97 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 28) Tj
+ET
+endstream
+endobj
+
+97 0 obj
+47
+endobj
+
+%% Contents for page 30
+98 0 obj
+<<
+ /Length 99 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato 29) Tj
+ET
+endstream
+endobj
+
+99 0 obj
+47
+endobj
+
+xref
+0 100
+0000000000 65535 f
+0000000025 00000 n
+0000000099 00000 n
+0000000155 00000 n
+0000000544 00000 n
+0000000631 00000 n
+0000000814 00000 n
+0000001019 00000 n
+0000001224 00000 n
+0000001429 00000 n
+0000001634 00000 n
+0000001840 00000 n
+0000002046 00000 n
+0000002252 00000 n
+0000002458 00000 n
+0000002665 00000 n
+0000002872 00000 n
+0000003079 00000 n
+0000003286 00000 n
+0000003493 00000 n
+0000003700 00000 n
+0000003907 00000 n
+0000004114 00000 n
+0000004321 00000 n
+0000004528 00000 n
+0000004735 00000 n
+0000004942 00000 n
+0000005149 00000 n
+0000005356 00000 n
+0000005563 00000 n
+0000005770 00000 n
+0000005977 00000 n
+0000006184 00000 n
+0000006391 00000 n
+0000006598 00000 n
+0000006805 00000 n
+0000007001 00000 n
+0000007200 00000 n
+0000007389 00000 n
+0000007492 00000 n
+0000007512 00000 n
+0000007631 00000 n
+0000007690 00000 n
+0000007793 00000 n
+0000007836 00000 n
+0000007939 00000 n
+0000007982 00000 n
+0000008085 00000 n
+0000008128 00000 n
+0000008231 00000 n
+0000008274 00000 n
+0000008377 00000 n
+0000008420 00000 n
+0000008523 00000 n
+0000008566 00000 n
+0000008669 00000 n
+0000008712 00000 n
+0000008815 00000 n
+0000008859 00000 n
+0000008962 00000 n
+0000009006 00000 n
+0000009110 00000 n
+0000009154 00000 n
+0000009258 00000 n
+0000009302 00000 n
+0000009406 00000 n
+0000009450 00000 n
+0000009554 00000 n
+0000009598 00000 n
+0000009702 00000 n
+0000009746 00000 n
+0000009850 00000 n
+0000009894 00000 n
+0000009998 00000 n
+0000010042 00000 n
+0000010146 00000 n
+0000010190 00000 n
+0000010294 00000 n
+0000010338 00000 n
+0000010442 00000 n
+0000010486 00000 n
+0000010590 00000 n
+0000010634 00000 n
+0000010738 00000 n
+0000010782 00000 n
+0000010886 00000 n
+0000010930 00000 n
+0000011034 00000 n
+0000011078 00000 n
+0000011182 00000 n
+0000011226 00000 n
+0000011330 00000 n
+0000011374 00000 n
+0000011478 00000 n
+0000011522 00000 n
+0000011626 00000 n
+0000011670 00000 n
+0000011774 00000 n
+0000011818 00000 n
+0000011922 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 100
+ /ID [<90f919de7874f3bc5cb7afbd1e9537bb><0dfe18a94cde0f4bfdc86c03af19010e>]
+>>
+startxref
+11942
+%%EOF
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index 3bcb173e..89dd1d02 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -7,6 +7,7 @@
#include <qpdf/QPDFPageObjectHelper.hh>
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
#include <qpdf/QPDFNumberTreeObjectHelper.hh>
+#include <qpdf/QPDFPageLabelDocumentHelper.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh>
#include <qpdf/Pl_StdioFile.hh>
@@ -1690,6 +1691,21 @@ void runtest(int n, char const* filename1, char const* arg2)
assert("six" == oh.getStringValue());
assert(2 == offset);
}
+ else if (n == 47)
+ {
+ // Test page labels.
+ QPDFPageLabelDocumentHelper pldh(pdf);
+ size_t npages = pdf.getRoot().getKey("/Pages").
+ getKey("/Count").getIntValue();
+ std::vector<QPDFObjectHandle> labels;
+ pldh.getLabelsForPageRange(0, npages - 1, 1, labels);
+ assert(labels.size() % 2 == 0);
+ for (size_t i = 0; i < labels.size(); i+= 2)
+ {
+ std::cout << labels.at(i).getIntValue() << " "
+ << labels.at(i+1).unparse() << std::endl;
+ }
+ }
else
{
throw std::runtime_error(std::string("invalid test ") +