mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-02 22:50:20 +00:00
Add QPDFNameTreeObjectHelper
This commit is contained in:
parent
d2f3975948
commit
0776c00129
@ -1,5 +1,9 @@
|
|||||||
2018-12-18 Jay Berkenbilt <ejb@ql.org>
|
2018-12-18 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* Add QPDFNameTreeObjectHelper class. This class provides useful
|
||||||
|
methods for dealing with name trees, which are discussed in
|
||||||
|
section 7.9.6 of the PDF spec (ISO-32000).
|
||||||
|
|
||||||
* Preserve page labels when merging and splitting files. Prior
|
* Preserve page labels when merging and splitting files. Prior
|
||||||
versions of qpdf simply preserved the page label information from
|
versions of qpdf simply preserved the page label information from
|
||||||
the first file, which usually wouldn't make any sense in the
|
the first file, which usually wouldn't make any sense in the
|
||||||
|
83
include/qpdf/QPDFNameTreeObjectHelper.hh
Normal file
83
include/qpdf/QPDFNameTreeObjectHelper.hh
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// 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 QPDFNAMETREEOBJECTHELPER_HH
|
||||||
|
#define QPDFNAMETREEOBJECTHELPER_HH
|
||||||
|
|
||||||
|
#include <qpdf/QPDFObjectHelper.hh>
|
||||||
|
#include <qpdf/QPDFObjGen.hh>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <qpdf/DLL.h>
|
||||||
|
|
||||||
|
// This is an object helper for name trees. See section 7.9.6 in the
|
||||||
|
// PDF spec (ISO 32000) for a description of name trees. This
|
||||||
|
// implementation disregards stated limits and sequencing and simply
|
||||||
|
// builds a map from string object. If the array of values does not
|
||||||
|
// contain a string where expected, this implementation silently skips
|
||||||
|
// forward until it finds a string. When looking up items in the name
|
||||||
|
// tree, use UTF-8 strings. All names are normalized for lookup
|
||||||
|
// purposes.
|
||||||
|
|
||||||
|
class QPDFNameTreeObjectHelper: public QPDFObjectHelper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QPDF_DLL
|
||||||
|
QPDFNameTreeObjectHelper(QPDFObjectHandle);
|
||||||
|
QPDF_DLL
|
||||||
|
virtual ~QPDFNameTreeObjectHelper();
|
||||||
|
|
||||||
|
// Return whether the number tree has an explicit entry for this
|
||||||
|
// number.
|
||||||
|
QPDF_DLL
|
||||||
|
bool hasName(std::string const& utf8);
|
||||||
|
|
||||||
|
// Find an object by name. If found, returns true and initializes
|
||||||
|
// oh.
|
||||||
|
QPDF_DLL
|
||||||
|
bool findObject(std::string const& utf8, QPDFObjectHandle& oh);
|
||||||
|
|
||||||
|
QPDF_DLL
|
||||||
|
std::map<std::string, QPDFObjectHandle> getAsMap() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Members
|
||||||
|
{
|
||||||
|
friend class QPDFNameTreeObjectHelper;
|
||||||
|
|
||||||
|
public:
|
||||||
|
QPDF_DLL
|
||||||
|
~Members();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Members();
|
||||||
|
Members(Members const&);
|
||||||
|
|
||||||
|
std::map<std::string, QPDFObjectHandle> entries;
|
||||||
|
std::set<QPDFObjGen> seen;
|
||||||
|
};
|
||||||
|
|
||||||
|
void updateMap(QPDFObjectHandle oh);
|
||||||
|
|
||||||
|
PointerHolder<Members> m;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QPDFNAMETREEOBJECTHELPER_HH
|
82
libqpdf/QPDFNameTreeObjectHelper.cc
Normal file
82
libqpdf/QPDFNameTreeObjectHelper.cc
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include <qpdf/QPDFNameTreeObjectHelper.hh>
|
||||||
|
|
||||||
|
QPDFNameTreeObjectHelper::Members::~Members()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFNameTreeObjectHelper::Members::Members()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh) :
|
||||||
|
QPDFObjectHelper(oh),
|
||||||
|
m(new Members())
|
||||||
|
{
|
||||||
|
updateMap(oh);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFNameTreeObjectHelper::updateMap(QPDFObjectHandle oh)
|
||||||
|
{
|
||||||
|
if (this->m->seen.count(oh.getObjGen()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->m->seen.insert(oh.getObjGen());
|
||||||
|
QPDFObjectHandle names = oh.getKey("/Names");
|
||||||
|
if (names.isArray())
|
||||||
|
{
|
||||||
|
size_t nitems = names.getArrayNItems();
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < nitems - 1)
|
||||||
|
{
|
||||||
|
QPDFObjectHandle name = names.getArrayItem(i);
|
||||||
|
if (name.isString())
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
QPDFObjectHandle obj = names.getArrayItem(i);
|
||||||
|
this->m->entries[name.getUTF8Value()] = obj;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QPDFObjectHandle kids = oh.getKey("/Kids");
|
||||||
|
if (kids.isArray())
|
||||||
|
{
|
||||||
|
size_t nitems = kids.getArrayNItems();
|
||||||
|
for (size_t i = 0; i < nitems; ++i)
|
||||||
|
{
|
||||||
|
updateMap(kids.getArrayItem(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFNameTreeObjectHelper::hasName(std::string const& name)
|
||||||
|
{
|
||||||
|
return this->m->entries.count(name) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFNameTreeObjectHelper::findObject(
|
||||||
|
std::string const& name, QPDFObjectHandle& oh)
|
||||||
|
{
|
||||||
|
std::map<std::string, QPDFObjectHandle>::iterator i =
|
||||||
|
this->m->entries.find(name);
|
||||||
|
if (i == this->m->entries.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
oh = (*i).second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, QPDFObjectHandle>
|
||||||
|
QPDFNameTreeObjectHelper::getAsMap() const
|
||||||
|
{
|
||||||
|
return this->m->entries;
|
||||||
|
}
|
@ -40,6 +40,7 @@ SRCS_libqpdf = \
|
|||||||
libqpdf/QPDFAnnotationObjectHelper.cc \
|
libqpdf/QPDFAnnotationObjectHelper.cc \
|
||||||
libqpdf/QPDFExc.cc \
|
libqpdf/QPDFExc.cc \
|
||||||
libqpdf/QPDFFormFieldObjectHelper.cc \
|
libqpdf/QPDFFormFieldObjectHelper.cc \
|
||||||
|
libqpdf/QPDFNameTreeObjectHelper.cc \
|
||||||
libqpdf/QPDFNumberTreeObjectHelper.cc \
|
libqpdf/QPDFNumberTreeObjectHelper.cc \
|
||||||
libqpdf/QPDFObjGen.cc \
|
libqpdf/QPDFObjGen.cc \
|
||||||
libqpdf/QPDFObject.cc \
|
libqpdf/QPDFObject.cc \
|
||||||
|
@ -226,13 +226,17 @@ foreach my $input (@ext_inputs)
|
|||||||
}
|
}
|
||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Number Trees ---");
|
$td->notify("--- Number and Name Trees ---");
|
||||||
$n_tests += 1;
|
$n_tests += 2;
|
||||||
|
|
||||||
$td->runtest("number trees",
|
$td->runtest("number trees",
|
||||||
{$td->COMMAND => "test_driver 46 number-tree.pdf"},
|
{$td->COMMAND => "test_driver 46 number-tree.pdf"},
|
||||||
{$td->FILE => "number-tree.out", $td->EXIT_STATUS => 0},
|
{$td->FILE => "number-tree.out", $td->EXIT_STATUS => 0},
|
||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("name trees",
|
||||||
|
{$td->COMMAND => "test_driver 48 name-tree.pdf"},
|
||||||
|
{$td->FILE => "name-tree.out", $td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
|
10
qpdf/qtest/qpdf/name-tree.out
Normal file
10
qpdf/qtest/qpdf/name-tree.out
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
01 one -> one!
|
||||||
|
06 σιχ -> six!
|
||||||
|
07 sev•n -> seven!
|
||||||
|
11 elephant -> elephant?
|
||||||
|
12 twelve -> twelve!
|
||||||
|
15 fifteen -> fifteen!
|
||||||
|
20 twenty -> twenty.
|
||||||
|
22 twenty-two -> twenty-two!
|
||||||
|
29 twenty-nine -> twenty-nine!
|
||||||
|
test 48 done
|
166
qpdf/qtest/qpdf/name-tree.pdf
Normal file
166
qpdf/qtest/qpdf/name-tree.pdf
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
%PDF-1.3
|
||||||
|
%¿÷¢þ
|
||||||
|
%QDF-1.0
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/Pages 2 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/Count 1
|
||||||
|
/Kids [
|
||||||
|
3 0 R
|
||||||
|
]
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
%% Page 1
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 4 0 R
|
||||||
|
/MediaBox [
|
||||||
|
0
|
||||||
|
0
|
||||||
|
612
|
||||||
|
792
|
||||||
|
]
|
||||||
|
/Parent 2 0 R
|
||||||
|
/Resources <<
|
||||||
|
/Font <<
|
||||||
|
/F1 6 0 R
|
||||||
|
>>
|
||||||
|
/ProcSet 7 0 R
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
%% Contents for page 1
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/Length 5 0 R
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
BT
|
||||||
|
/F1 24 Tf
|
||||||
|
72 720 Td
|
||||||
|
(Potato) Tj
|
||||||
|
ET
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
44
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica
|
||||||
|
/Encoding /WinAnsiEncoding
|
||||||
|
/Name /F1
|
||||||
|
/Subtype /Type1
|
||||||
|
/Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
[
|
||||||
|
/PDF
|
||||||
|
/Text
|
||||||
|
]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Kids [
|
||||||
|
9 0 R
|
||||||
|
10 0 R
|
||||||
|
]
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<<
|
||||||
|
/Kids [
|
||||||
|
11 0 R
|
||||||
|
12 0 R
|
||||||
|
]
|
||||||
|
/Limits [
|
||||||
|
0
|
||||||
|
19
|
||||||
|
]
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<<
|
||||||
|
/Limits [
|
||||||
|
20
|
||||||
|
29
|
||||||
|
]
|
||||||
|
/Names [
|
||||||
|
(20 twenty) (twenty.)
|
||||||
|
(22 twenty-two) (twenty-two!)
|
||||||
|
(29 twenty-nine) (twenty-nine!)
|
||||||
|
]
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
11 0 obj
|
||||||
|
<<
|
||||||
|
/Limits [
|
||||||
|
(01 one)
|
||||||
|
<feff0030003700200073006500762022006e>
|
||||||
|
]
|
||||||
|
/Names [
|
||||||
|
(01 one) (one!)
|
||||||
|
<feff00300036002003C303B903C7> (six!)
|
||||||
|
(07 sev€n) (seven!)
|
||||||
|
]
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
12 0 obj
|
||||||
|
<<
|
||||||
|
/Limits [
|
||||||
|
(11 elephant)
|
||||||
|
(15 fifteen)
|
||||||
|
]
|
||||||
|
/Names [
|
||||||
|
(11 elephant) (elephant?)
|
||||||
|
(12 twelve) (twelve!)
|
||||||
|
(15 fifteen) (fifteen!)
|
||||||
|
]
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000025 00000 n
|
||||||
|
0000000079 00000 n
|
||||||
|
0000000161 00000 n
|
||||||
|
0000000376 00000 n
|
||||||
|
0000000475 00000 n
|
||||||
|
0000000494 00000 n
|
||||||
|
0000000612 00000 n
|
||||||
|
0000000647 00000 n
|
||||||
|
0000000704 00000 n
|
||||||
|
0000000791 00000 n
|
||||||
|
0000000955 00000 n
|
||||||
|
0000001151 00000 n
|
||||||
|
trailer <<
|
||||||
|
/Root 1 0 R
|
||||||
|
/QTest 8 0 R
|
||||||
|
/Size 13
|
||||||
|
/ID [<2c3b7a6ec7fc61db8a5db4eebf57f540><2c3b7a6ec7fc61db8a5db4eebf57f540>]
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1325
|
||||||
|
%%EOF
|
@ -7,6 +7,7 @@
|
|||||||
#include <qpdf/QPDFPageObjectHelper.hh>
|
#include <qpdf/QPDFPageObjectHelper.hh>
|
||||||
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
|
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
|
||||||
#include <qpdf/QPDFNumberTreeObjectHelper.hh>
|
#include <qpdf/QPDFNumberTreeObjectHelper.hh>
|
||||||
|
#include <qpdf/QPDFNameTreeObjectHelper.hh>
|
||||||
#include <qpdf/QPDFPageLabelDocumentHelper.hh>
|
#include <qpdf/QPDFPageLabelDocumentHelper.hh>
|
||||||
#include <qpdf/QUtil.hh>
|
#include <qpdf/QUtil.hh>
|
||||||
#include <qpdf/QTC.hh>
|
#include <qpdf/QTC.hh>
|
||||||
@ -1706,6 +1707,29 @@ void runtest(int n, char const* filename1, char const* arg2)
|
|||||||
<< labels.at(i+1).unparse() << std::endl;
|
<< labels.at(i+1).unparse() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (n == 48)
|
||||||
|
{
|
||||||
|
// Test name tree. This test is crafted to work with
|
||||||
|
// name-tree.pdf
|
||||||
|
QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest");
|
||||||
|
QPDFNameTreeObjectHelper ntoh(qtest);
|
||||||
|
std::map<std::string, QPDFObjectHandle> ntoh_map = ntoh.getAsMap();
|
||||||
|
for (std::map<std::string, QPDFObjectHandle>::iterator iter =
|
||||||
|
ntoh_map.begin();
|
||||||
|
iter != ntoh_map.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::cout << (*iter).first << " -> "
|
||||||
|
<< (*iter).second.getStringValue()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
assert(ntoh.hasName("11 elephant"));
|
||||||
|
assert(ntoh.hasName("07 sev\xe2\x80\xa2n"));
|
||||||
|
assert(! ntoh.hasName("potato"));
|
||||||
|
QPDFObjectHandle oh;
|
||||||
|
assert(! ntoh.findObject("potato", oh));
|
||||||
|
assert(ntoh.findObject("07 sev\xe2\x80\xa2n", oh));
|
||||||
|
assert("seven!" == oh.getStringValue());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw std::runtime_error(std::string("invalid test ") +
|
throw std::runtime_error(std::string("invalid test ") +
|
||||||
|
Loading…
Reference in New Issue
Block a user