mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
Add document and object helpers for outlines (bookmarks)
This commit is contained in:
parent
30a0c070e4
commit
d5d179f441
@ -1,3 +1,10 @@
|
||||
2018-12-19 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Add QPDFOutlineDocumentHelper and QPDFOutlineObjectHelper for
|
||||
handling outlines (bookmarks) including bidirectionally mapping
|
||||
between bookmarks and pages. Initially there is no support for
|
||||
modifying the outlines hierarchy.
|
||||
|
||||
2018-12-18 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* New method QPDFObjectHandle::getJSON() returns a JSON object
|
||||
|
108
include/qpdf/QPDFOutlineDocumentHelper.hh
Normal file
108
include/qpdf/QPDFOutlineDocumentHelper.hh
Normal file
@ -0,0 +1,108 @@
|
||||
// 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 QPDFOUTLINEDOCUMENTHELPER_HH
|
||||
#define QPDFOUTLINEDOCUMENTHELPER_HH
|
||||
|
||||
#include <qpdf/QPDFDocumentHelper.hh>
|
||||
#include <qpdf/QPDFOutlineObjectHelper.hh>
|
||||
#include <qpdf/QPDFNameTreeObjectHelper.hh>
|
||||
|
||||
#include <qpdf/QPDF.hh>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
#include <qpdf/DLL.h>
|
||||
|
||||
// This is a document helper for outlines, also known as bookmarks.
|
||||
// Outlines are discussed in section 12.3.3 of the PDF spec
|
||||
// (ISO-32000). With the help of QPDFOutlineObjectHelper, the outlines
|
||||
// tree is traversed, and a bidirectional map is made between pages
|
||||
// and outlines. See also QPDFOutlineObjectHelper.
|
||||
|
||||
class QPDFOutlineDocumentHelper: public QPDFDocumentHelper
|
||||
{
|
||||
public:
|
||||
QPDF_DLL
|
||||
QPDFOutlineDocumentHelper(QPDF&);
|
||||
QPDF_DLL
|
||||
virtual ~QPDFOutlineDocumentHelper();
|
||||
|
||||
QPDF_DLL
|
||||
bool hasOutlines();
|
||||
|
||||
QPDF_DLL
|
||||
std::list<QPDFOutlineObjectHelper> getTopLevelOutlines();
|
||||
|
||||
// If the name is a name object, look it up in the /Dests key of
|
||||
// the document catalog. If the name is a string, look it up in
|
||||
// the name tree pointed to by the /Dests key of the names
|
||||
// dictionary.
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle
|
||||
resolveNamedDest(QPDFObjectHandle name);
|
||||
|
||||
// Return a list outlines that are known to target the specified
|
||||
// page
|
||||
QPDF_DLL
|
||||
std::list<QPDFOutlineObjectHelper> getOutlinesForPage(QPDFObjGen const&);
|
||||
|
||||
class Accessor
|
||||
{
|
||||
friend class QPDFOutlineObjectHelper;
|
||||
|
||||
QPDF_DLL
|
||||
static bool
|
||||
checkSeen(QPDFOutlineDocumentHelper& dh, QPDFObjGen const& og)
|
||||
{
|
||||
return dh.checkSeen(og);
|
||||
}
|
||||
};
|
||||
friend class Accessor;
|
||||
|
||||
private:
|
||||
bool checkSeen(QPDFObjGen const& og);
|
||||
void initializeByPage();
|
||||
|
||||
class Members
|
||||
{
|
||||
friend class QPDFOutlineDocumentHelper;
|
||||
|
||||
public:
|
||||
QPDF_DLL
|
||||
~Members();
|
||||
|
||||
private:
|
||||
Members();
|
||||
Members(Members const&);
|
||||
|
||||
std::list<QPDFOutlineObjectHelper> outlines;
|
||||
std::set<QPDFObjGen> seen;
|
||||
QPDFObjectHandle dest_dict;
|
||||
PointerHolder<QPDFNameTreeObjectHelper> names_dest;
|
||||
std::map<QPDFObjGen, std::list<QPDFOutlineObjectHelper> > by_page;
|
||||
};
|
||||
|
||||
PointerHolder<Members> m;
|
||||
};
|
||||
|
||||
#endif // QPDFOUTLINEDOCUMENTHELPER_HH
|
122
include/qpdf/QPDFOutlineObjectHelper.hh
Normal file
122
include/qpdf/QPDFOutlineObjectHelper.hh
Normal file
@ -0,0 +1,122 @@
|
||||
// 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 QPDFOUTLINEOBJECTHELPER_HH
|
||||
#define QPDFOUTLINEOBJECTHELPER_HH
|
||||
|
||||
#include <qpdf/QPDFObjectHelper.hh>
|
||||
#include <qpdf/QPDFObjGen.hh>
|
||||
#include <list>
|
||||
|
||||
class QPDFOutlineDocumentHelper;
|
||||
|
||||
#include <qpdf/DLL.h>
|
||||
|
||||
// This is an object helper for outline items. Outlines, also known as
|
||||
// bookmarks, are described in section 12.3.3 of the PDF spec
|
||||
// (ISO-32000). See comments below for details.
|
||||
|
||||
class QPDFOutlineObjectHelper: public QPDFObjectHelper
|
||||
{
|
||||
public:
|
||||
QPDF_DLL
|
||||
virtual ~QPDFOutlineObjectHelper()
|
||||
{
|
||||
// This must be cleared explicitly to avoid circular references
|
||||
// that prevent cleanup of pointer holders.
|
||||
this->m->parent = 0;
|
||||
}
|
||||
|
||||
// All constructors are private. You can only create one of these
|
||||
// using QPDFOutlineDocumentHelper.
|
||||
|
||||
// Return parent pointer. Returns a null pointer if this is a
|
||||
// top-level outline.
|
||||
QPDF_DLL
|
||||
PointerHolder<QPDFOutlineObjectHelper> getParent();
|
||||
|
||||
// Return children as a list.
|
||||
QPDF_DLL
|
||||
std::list<QPDFOutlineObjectHelper> getKids();
|
||||
|
||||
// Return the destination, regardless of whether it is named or
|
||||
// explicit and whether it is directly provided or in a GoTo
|
||||
// action. Returns a null object if the destination can't be
|
||||
// determined. Named destinations can be resolved using the older
|
||||
// root /Dest dictionary or the current names tree.
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle getDest();
|
||||
|
||||
// Return the page that the outline points to. Returns a null
|
||||
// object if the destination page can't be determined.
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle getDestPage();
|
||||
|
||||
// Returns the value of /Count as present in the object, or 0 if
|
||||
// not present. If count is positive, the outline is open. If
|
||||
// negative, it is closed. Either way, the absolute value is the
|
||||
// number descendant items that would be visible if this were
|
||||
// open.
|
||||
QPDF_DLL
|
||||
int getCount();
|
||||
|
||||
// Returns the title as a UTF-8 string. Returns the empty string
|
||||
// if there is no title.
|
||||
QPDF_DLL
|
||||
std::string getTitle();
|
||||
|
||||
class Accessor
|
||||
{
|
||||
friend class QPDFOutlineDocumentHelper;
|
||||
|
||||
static QPDFOutlineObjectHelper
|
||||
create(QPDFObjectHandle oh, QPDFOutlineDocumentHelper& dh, int depth)
|
||||
{
|
||||
return QPDFOutlineObjectHelper(oh, dh, depth);
|
||||
}
|
||||
};
|
||||
friend class Accessor;
|
||||
|
||||
private:
|
||||
QPDF_DLL
|
||||
QPDFOutlineObjectHelper(QPDFObjectHandle, QPDFOutlineDocumentHelper&, int);
|
||||
|
||||
class Members
|
||||
{
|
||||
friend class QPDFOutlineObjectHelper;
|
||||
|
||||
public:
|
||||
QPDF_DLL
|
||||
~Members();
|
||||
|
||||
private:
|
||||
Members(QPDFOutlineDocumentHelper& dh);
|
||||
Members(Members const&);
|
||||
|
||||
QPDFOutlineDocumentHelper& dh;
|
||||
PointerHolder<QPDFOutlineObjectHelper> parent;
|
||||
std::list<QPDFOutlineObjectHelper> kids;
|
||||
};
|
||||
|
||||
PointerHolder<Members> m;
|
||||
};
|
||||
|
||||
#endif // QPDFOUTLINEOBJECTHELPER_HH
|
137
libqpdf/QPDFOutlineDocumentHelper.cc
Normal file
137
libqpdf/QPDFOutlineDocumentHelper.cc
Normal file
@ -0,0 +1,137 @@
|
||||
#include <qpdf/QPDFOutlineDocumentHelper.hh>
|
||||
#include <qpdf/QTC.hh>
|
||||
|
||||
QPDFOutlineDocumentHelper::Members::~Members()
|
||||
{
|
||||
}
|
||||
|
||||
QPDFOutlineDocumentHelper::Members::Members()
|
||||
{
|
||||
}
|
||||
|
||||
QPDFOutlineDocumentHelper::QPDFOutlineDocumentHelper(QPDF& qpdf) :
|
||||
QPDFDocumentHelper(qpdf),
|
||||
m(new Members())
|
||||
{
|
||||
QPDFObjectHandle root = qpdf.getRoot();
|
||||
if (! root.hasKey("/Outlines"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
QPDFObjectHandle outlines = root.getKey("/Outlines");
|
||||
if (! (outlines.isDictionary() && outlines.hasKey("/First")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
QPDFObjectHandle cur = outlines.getKey("/First");
|
||||
while (! cur.isNull())
|
||||
{
|
||||
this->m->outlines.push_back(
|
||||
QPDFOutlineObjectHelper::Accessor::create(cur, *this, 1));
|
||||
cur = cur.getKey("/Next");
|
||||
}
|
||||
}
|
||||
|
||||
QPDFOutlineDocumentHelper::~QPDFOutlineDocumentHelper()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
QPDFOutlineDocumentHelper::hasOutlines()
|
||||
{
|
||||
return ! this->m->outlines.empty();
|
||||
}
|
||||
|
||||
std::list<QPDFOutlineObjectHelper>
|
||||
QPDFOutlineDocumentHelper::getTopLevelOutlines()
|
||||
{
|
||||
return this->m->outlines;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFOutlineDocumentHelper::initializeByPage()
|
||||
{
|
||||
std::list<QPDFOutlineObjectHelper> queue;
|
||||
queue.insert(queue.end(), this->m->outlines.begin(), this->m->outlines.end());
|
||||
|
||||
while (! queue.empty())
|
||||
{
|
||||
QPDFOutlineObjectHelper oh = queue.front();
|
||||
queue.pop_front();
|
||||
this->m->by_page[oh.getDestPage().getObjGen()].push_back(oh);
|
||||
std::list<QPDFOutlineObjectHelper> kids = oh.getKids();
|
||||
queue.insert(queue.end(), kids.begin(), kids.end());
|
||||
}
|
||||
}
|
||||
|
||||
std::list<QPDFOutlineObjectHelper>
|
||||
QPDFOutlineDocumentHelper::getOutlinesForPage(QPDFObjGen const& og)
|
||||
{
|
||||
if (this->m->by_page.empty())
|
||||
{
|
||||
initializeByPage();
|
||||
}
|
||||
std::list<QPDFOutlineObjectHelper> result;
|
||||
if (this->m->by_page.count(og))
|
||||
{
|
||||
result = this->m->by_page[og];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFOutlineDocumentHelper::resolveNamedDest(QPDFObjectHandle name)
|
||||
{
|
||||
QPDFObjectHandle result;
|
||||
if (name.isName())
|
||||
{
|
||||
if (! this->m->dest_dict.isInitialized())
|
||||
{
|
||||
this->m->dest_dict = this->qpdf.getRoot().getKey("/Dests");
|
||||
}
|
||||
if (this->m->dest_dict.isDictionary())
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFOutlineDocumentHelper name named dest");
|
||||
result = this->m->dest_dict.getKey(name.getName());
|
||||
}
|
||||
}
|
||||
else if (name.isString())
|
||||
{
|
||||
if (0 == this->m->names_dest.getPointer())
|
||||
{
|
||||
QPDFObjectHandle names = this->qpdf.getRoot().getKey("/Names");
|
||||
if (names.isDictionary())
|
||||
{
|
||||
QPDFObjectHandle dests = names.getKey("/Dests");
|
||||
if (dests.isDictionary())
|
||||
{
|
||||
this->m->names_dest =
|
||||
new QPDFNameTreeObjectHelper(dests);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this->m->names_dest.getPointer())
|
||||
{
|
||||
if (this->m->names_dest->findObject(name.getUTF8Value(), result))
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFOutlineDocumentHelper string named dest");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! result.isInitialized())
|
||||
{
|
||||
result = QPDFObjectHandle::newNull();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
QPDFOutlineDocumentHelper::checkSeen(QPDFObjGen const& og)
|
||||
{
|
||||
if (this->m->seen.count(og) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
this->m->seen.insert(og);
|
||||
return false;
|
||||
}
|
117
libqpdf/QPDFOutlineObjectHelper.cc
Normal file
117
libqpdf/QPDFOutlineObjectHelper.cc
Normal file
@ -0,0 +1,117 @@
|
||||
#include <qpdf/QPDFOutlineObjectHelper.hh>
|
||||
#include <qpdf/QPDFOutlineDocumentHelper.hh>
|
||||
#include <qpdf/QTC.hh>
|
||||
|
||||
QPDFOutlineObjectHelper::Members::~Members()
|
||||
{
|
||||
}
|
||||
|
||||
QPDFOutlineObjectHelper::Members::Members(QPDFOutlineDocumentHelper& dh) :
|
||||
dh(dh)
|
||||
{
|
||||
}
|
||||
|
||||
QPDFOutlineObjectHelper::QPDFOutlineObjectHelper(
|
||||
QPDFObjectHandle oh, QPDFOutlineDocumentHelper& dh, int depth) :
|
||||
QPDFObjectHelper(oh),
|
||||
m(new Members(dh))
|
||||
{
|
||||
if (depth > 50)
|
||||
{
|
||||
// Not exercised in test suite, but was tested manually by
|
||||
// temporarily changing max depth to 1.
|
||||
return;
|
||||
}
|
||||
if (QPDFOutlineDocumentHelper::Accessor::checkSeen(
|
||||
this->m->dh, this->oh.getObjGen()))
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFOutlineObjectHelper loop");
|
||||
return;
|
||||
}
|
||||
|
||||
QPDFObjectHandle cur = oh.getKey("/First");
|
||||
while (! cur.isNull())
|
||||
{
|
||||
QPDFOutlineObjectHelper new_ooh(cur, dh, 1 + depth);
|
||||
new_ooh.m->parent = new QPDFOutlineObjectHelper(*this);
|
||||
this->m->kids.push_back(new_ooh);
|
||||
cur = cur.getKey("/Next");
|
||||
}
|
||||
}
|
||||
|
||||
PointerHolder<QPDFOutlineObjectHelper>
|
||||
QPDFOutlineObjectHelper::getParent()
|
||||
{
|
||||
return this->m->parent;
|
||||
}
|
||||
|
||||
std::list<QPDFOutlineObjectHelper>
|
||||
QPDFOutlineObjectHelper::getKids()
|
||||
{
|
||||
return this->m->kids;
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFOutlineObjectHelper::getDest()
|
||||
{
|
||||
QPDFObjectHandle dest;
|
||||
QPDFObjectHandle A;
|
||||
if (this->oh.hasKey("/Dest"))
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFOutlineObjectHelper direct dest");
|
||||
dest = this->oh.getKey("/Dest");
|
||||
}
|
||||
else if ((A = this->oh.getKey("/A")).isDictionary() &&
|
||||
A.getKey("/S").isName() &&
|
||||
(A.getKey("/S").getName() == "/GoTo") &&
|
||||
A.hasKey("/D"))
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFOutlineObjectHelper action dest");
|
||||
dest = A.getKey("/D");
|
||||
}
|
||||
if (! dest.isInitialized())
|
||||
{
|
||||
dest = QPDFObjectHandle::newNull();
|
||||
}
|
||||
|
||||
if (dest.isName() || dest.isString())
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFOutlineObjectHelper named dest");
|
||||
dest = this->m->dh.resolveNamedDest(dest);
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFOutlineObjectHelper::getDestPage()
|
||||
{
|
||||
QPDFObjectHandle dest = getDest();
|
||||
if ((dest.isArray()) && (dest.getArrayNItems() > 0))
|
||||
{
|
||||
return dest.getArrayItem(0);
|
||||
}
|
||||
return QPDFObjectHandle::newNull();
|
||||
}
|
||||
|
||||
int
|
||||
QPDFOutlineObjectHelper::getCount()
|
||||
{
|
||||
int count = 0;
|
||||
if (this->oh.hasKey("/Count"))
|
||||
{
|
||||
count = this->oh.getKey("/Count").getIntValue();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::string
|
||||
QPDFOutlineObjectHelper::getTitle()
|
||||
{
|
||||
std::string result;
|
||||
if (this->oh.hasKey("/Title"))
|
||||
{
|
||||
result = this->oh.getKey("/Title").getUTF8Value();
|
||||
}
|
||||
return result;
|
||||
}
|
@ -46,6 +46,8 @@ SRCS_libqpdf = \
|
||||
libqpdf/QPDFObjGen.cc \
|
||||
libqpdf/QPDFObject.cc \
|
||||
libqpdf/QPDFObjectHandle.cc \
|
||||
libqpdf/QPDFOutlineDocumentHelper.cc \
|
||||
libqpdf/QPDFOutlineObjectHelper.cc \
|
||||
libqpdf/QPDFPageDocumentHelper.cc \
|
||||
libqpdf/QPDFPageLabelDocumentHelper.cc \
|
||||
libqpdf/QPDFPageObjectHelper.cc \
|
||||
|
@ -361,3 +361,9 @@ qpdf keep files open n 0
|
||||
qpdf keep files open y 0
|
||||
qpdf don't disable keep files open 0
|
||||
QPDFPageLabelDocumentHelper skip first 0
|
||||
QPDFOutlineObjectHelper direct dest 0
|
||||
QPDFOutlineObjectHelper action dest 0
|
||||
QPDFOutlineObjectHelper named dest 0
|
||||
QPDFOutlineDocumentHelper name named dest 0
|
||||
QPDFOutlineDocumentHelper string named dest 0
|
||||
QPDFOutlineObjectHelper loop 0
|
||||
|
@ -256,6 +256,24 @@ $td->runtest("no page labels",
|
||||
{$td->FILE => "no-page-labels.out", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Outlines ---");
|
||||
my @outline_files = (
|
||||
'page-labels-and-outlines',
|
||||
'outlines-with-actions',
|
||||
'outlines-with-old-root-dests',
|
||||
'outlines-with-loop',
|
||||
);
|
||||
$n_tests += scalar(@outline_files);
|
||||
foreach my $f (@outline_files)
|
||||
{
|
||||
$td->runtest("outlines: $f",
|
||||
{$td->COMMAND => "test_driver 49 $f.pdf"},
|
||||
{$td->FILE => "$f.out", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
}
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Page API Tests ---");
|
||||
|
12
qpdf/qtest/qpdf/outlines-with-actions.out
Normal file
12
qpdf/qtest/qpdf/outlines-with-actions.out
Normal file
@ -0,0 +1,12 @@
|
||||
page 0: Merschqaberschq (A) 1.2.2 -> 0: /XYZ null null null -> [ 6 0 R /XYZ null null null ]
|
||||
page 1: Gabeebeebee (name) 1.2.1 -> 1: /FitR 66 714 180 770 -> [ 7 0 R /FitR 66 714 180 770 ]
|
||||
page 5: Potato 1 -> 5: /XYZ null null null -> [ 11 0 R /XYZ null null null ]
|
||||
page 11: Mern 1.1 -> 11: /Fit -> [ 17 0 R /Fit ]
|
||||
page 12: Biherbadem 1.1.1 -> 12: /FitV 100 -> [ 18 0 R /FitV 100 ]
|
||||
page 12: Gawehwehweh 1.1.2 -> 12: /XYZ null null null -> [ 18 0 R /XYZ null null null ]
|
||||
page 13: Squash ÷πʬ÷ 1.2 -> 13: /FitH 792 -> [ 19 0 R /FitH 792 ]
|
||||
page 15: Salad 2 -> 15: /XYZ 66 756 3 -> [ 21 0 R /XYZ 66 756 3 ]
|
||||
page 18: Glarpenspliel (A, name) 1.1.1.1 -> 18: /XYZ null null null -> [ 24 0 R /XYZ null null null ]
|
||||
page 19: Hagoogamagoogle 1.1.1.2 -> 19: /XYZ null null null -> [ 25 0 R /XYZ null null null ]
|
||||
page 22: Jawarnianbvarwash 1.1.2.1 -> 22: /XYZ null null null -> [ 28 0 R /XYZ null null null ]
|
||||
test 49 done
|
1575
qpdf/qtest/qpdf/outlines-with-actions.pdf
Normal file
1575
qpdf/qtest/qpdf/outlines-with-actions.pdf
Normal file
File diff suppressed because it is too large
Load Diff
11
qpdf/qtest/qpdf/outlines-with-loop.out
Normal file
11
qpdf/qtest/qpdf/outlines-with-loop.out
Normal file
@ -0,0 +1,11 @@
|
||||
page 5: Potato 1 -> 5: /XYZ null null null -> [ 11 0 R /XYZ null null null ]
|
||||
page 5: Potato 1 -> 5: /XYZ null null null -> [ 11 0 R /XYZ null null null ]
|
||||
page 11: Mern 1.1 -> 11: /Fit -> [ 17 0 R /Fit ]
|
||||
page 12: Biherbadem 1.1.1 -> 12: /FitV 100 -> [ 18 0 R /FitV 100 ]
|
||||
page 12: Gawehwehweh 1.1.2 -> 12: /XYZ null null null -> [ 18 0 R /XYZ null null null ]
|
||||
page 15: Salad 2 -> 15: /XYZ 66 756 3 -> [ 21 0 R /XYZ 66 756 3 ]
|
||||
page 15: Salad 2 -> 15: /XYZ 66 756 3 -> [ 21 0 R /XYZ 66 756 3 ]
|
||||
page 18: Glarpenspliel (A, name) 1.1.1.1 -> 18: /XYZ null null null -> [ 24 0 R /XYZ null null null ]
|
||||
page 19: Hagoogamagoogle 1.1.1.2 -> 19: /XYZ null null null -> [ 25 0 R /XYZ null null null ]
|
||||
page 22: Jawarnianbvarwash 1.1.2.1 -> 22: /XYZ null null null -> [ 28 0 R /XYZ null null null ]
|
||||
test 49 done
|
1575
qpdf/qtest/qpdf/outlines-with-loop.pdf
Normal file
1575
qpdf/qtest/qpdf/outlines-with-loop.pdf
Normal file
File diff suppressed because it is too large
Load Diff
12
qpdf/qtest/qpdf/outlines-with-old-root-dests.out
Normal file
12
qpdf/qtest/qpdf/outlines-with-old-root-dests.out
Normal file
@ -0,0 +1,12 @@
|
||||
page 0: •Merschqaberschq (A) 1.2.2 -> 0: /XYZ null null null -> [ 6 0 R /XYZ null null null ]
|
||||
page 1: •Gabeebeebee (name) 1.2.1 -> 1: /FitR 66 714 180 770 -> [ 7 0 R /FitR 66 714 180 770 ]
|
||||
page 5: •Potato 1 -> 5: /XYZ null null null -> [ 11 0 R /XYZ null null null ]
|
||||
page 11: •Mern 1.1 -> 11: /Fit -> [ 17 0 R /Fit ]
|
||||
page 12: •Biherbadem 1.1.1 -> 12: /FitV 100 -> [ 18 0 R /FitV 100 ]
|
||||
page 12: •Gawehwehweh 1.1.2 -> 12: /XYZ null null null -> [ 18 0 R /XYZ null null null ]
|
||||
page 13: •Squash ÷πʬ÷ 1.2 -> 13: /FitH 792 -> [ 19 0 R /FitH 792 ]
|
||||
page 15: •Salad 2 -> 15: /XYZ 66 756 3 -> [ 21 0 R /XYZ 66 756 3 ]
|
||||
page 18: •Glarpenspliel (A, name) 1.1.1.1 -> 18: /XYZ null null null -> [ 24 0 R /XYZ null null null ]
|
||||
page 19: •Hagoogamagoogle 1.1.1.2 -> 19: /XYZ null null null -> [ 25 0 R /XYZ null null null ]
|
||||
page 22: •Jawarnianbvarwash 1.1.2.1 -> 22: /XYZ null null null -> [ 28 0 R /XYZ null null null ]
|
||||
test 49 done
|
1567
qpdf/qtest/qpdf/outlines-with-old-root-dests.pdf
Normal file
1567
qpdf/qtest/qpdf/outlines-with-old-root-dests.pdf
Normal file
File diff suppressed because it is too large
Load Diff
12
qpdf/qtest/qpdf/page-labels-and-outlines.out
Normal file
12
qpdf/qtest/qpdf/page-labels-and-outlines.out
Normal file
@ -0,0 +1,12 @@
|
||||
page 0: Trepsicle 1.2.2 -> 0: /XYZ null null null -> [ 3 0 R /XYZ null null null ]
|
||||
page 1: Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770 -> [ 4 0 R /FitR 66 714 180 770 ]
|
||||
page 5: Isís 1 -> 5: /XYZ null null null -> [ 8 0 R /XYZ null null null ]
|
||||
page 11: Amanda 1.1 -> 11: /Fit -> [ 14 0 R /Fit ]
|
||||
page 12: Isosicle 1.1.1 -> 12: /FitV 100 -> [ 15 0 R /FitV 100 ]
|
||||
page 12: Isosicle 1.1.2 -> 12: /XYZ null null null -> [ 15 0 R /XYZ null null null ]
|
||||
page 13: Sandy ÷Σανδι÷ 1.2 -> 13: /FitH 792 -> [ 16 0 R /FitH 792 ]
|
||||
page 15: Trepak 2 -> 15: /XYZ 66 756 3 -> [ 18 0 R /XYZ 66 756 3 ]
|
||||
page 18: Isosicle 1.1.1.1 -> 18: /XYZ null null null -> [ 21 0 R /XYZ null null null ]
|
||||
page 19: Isosicle 1.1.1.2 -> 19: /XYZ null null null -> [ 22 0 R /XYZ null null null ]
|
||||
page 22: Isosicle 1.1.2.1 -> 22: /XYZ null null null -> [ 25 0 R /XYZ null null null ]
|
||||
test 49 done
|
@ -9,6 +9,7 @@
|
||||
#include <qpdf/QPDFNumberTreeObjectHelper.hh>
|
||||
#include <qpdf/QPDFNameTreeObjectHelper.hh>
|
||||
#include <qpdf/QPDFPageLabelDocumentHelper.hh>
|
||||
#include <qpdf/QPDFOutlineDocumentHelper.hh>
|
||||
#include <qpdf/QUtil.hh>
|
||||
#include <qpdf/QTC.hh>
|
||||
#include <qpdf/Pl_StdioFile.hh>
|
||||
@ -1730,6 +1731,29 @@ void runtest(int n, char const* filename1, char const* arg2)
|
||||
assert(ntoh.findObject("07 sev\xe2\x80\xa2n", oh));
|
||||
assert("seven!" == oh.getStringValue());
|
||||
}
|
||||
else if (n == 49)
|
||||
{
|
||||
// Outlines
|
||||
std::vector<QPDFPageObjectHelper> pages =
|
||||
QPDFPageDocumentHelper(pdf).getAllPages();
|
||||
QPDFOutlineDocumentHelper odh(pdf);
|
||||
int pageno = 0;
|
||||
for (std::vector<QPDFPageObjectHelper>::iterator iter = pages.begin();
|
||||
iter != pages.end(); ++iter, ++pageno)
|
||||
{
|
||||
std::list<QPDFOutlineObjectHelper> outlines =
|
||||
odh.getOutlinesForPage((*iter).getObjectHandle().getObjGen());
|
||||
for (std::list<QPDFOutlineObjectHelper>::iterator oiter =
|
||||
outlines.begin();
|
||||
oiter != outlines.end(); ++oiter)
|
||||
{
|
||||
std::cout
|
||||
<< "page " << pageno << ": "
|
||||
<< (*oiter).getTitle() << " -> "
|
||||
<< (*oiter).getDest().unparseResolved() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(std::string("invalid test ") +
|
||||
|
Loading…
Reference in New Issue
Block a user