mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-03 07:12:28 +00:00
Add concept of reserved objects
QPDFObjectHandle::{new,is,assert}Reserved, QPDF::replaceReserved provide a mechanism to add objects to a PDF file when there are circular references. This is a prerequisite to copying objects from one PDF to another.
This commit is contained in:
parent
af64668ad1
commit
8a217eb3a2
@ -1,3 +1,12 @@
|
|||||||
|
2012-07-08 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* Add QPDFObjectHandle::newReserved to create a reserved object
|
||||||
|
and QPDF::replaceReserved to replace it with a real object.
|
||||||
|
QPDFObjectHandle::newReserved reserves an object ID in a QPDF
|
||||||
|
object and ensures that any references to it remain unresolved.
|
||||||
|
When QPDF::replaceReserved is later called, previous references to
|
||||||
|
the reserved object will properly resolve to the replaced object.
|
||||||
|
|
||||||
2012-07-07 Jay Berkenbilt <ejb@ql.org>
|
2012-07-07 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
* NOTE: BREAKING API CHANGE. Remove previously required length
|
* NOTE: BREAKING API CHANGE. Remove previously required length
|
||||||
|
@ -161,7 +161,8 @@ class QPDF
|
|||||||
// be associated with the PDF file. Note that replacing an object
|
// be associated with the PDF file. Note that replacing an object
|
||||||
// with QPDFObjectHandle::newNull() effectively removes the object
|
// with QPDFObjectHandle::newNull() effectively removes the object
|
||||||
// from the file since a non-existent object is treated as a null
|
// from the file since a non-existent object is treated as a null
|
||||||
// object.
|
// object. To replace a reserved object, call replaceReserved
|
||||||
|
// instead.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void replaceObject(int objid, int generation, QPDFObjectHandle);
|
void replaceObject(int objid, int generation, QPDFObjectHandle);
|
||||||
|
|
||||||
@ -180,6 +181,15 @@ class QPDF
|
|||||||
void swapObjects(int objid1, int generation1,
|
void swapObjects(int objid1, int generation1,
|
||||||
int objid2, int generation2);
|
int objid2, int generation2);
|
||||||
|
|
||||||
|
// Replace a reserved object. This is a wrapper around
|
||||||
|
// replaceObject but it guarantees that the underlying object is a
|
||||||
|
// reserved object. After this call, reserved will be a reference
|
||||||
|
// to replacement.
|
||||||
|
QPDF_DLL
|
||||||
|
void
|
||||||
|
replaceReserved(QPDFObjectHandle reserved,
|
||||||
|
QPDFObjectHandle replacement);
|
||||||
|
|
||||||
// Encryption support
|
// Encryption support
|
||||||
|
|
||||||
enum encryption_method_e { e_none, e_unknown, e_rc4, e_aes };
|
enum encryption_method_e { e_none, e_unknown, e_rc4, e_aes };
|
||||||
|
@ -81,6 +81,8 @@ class QPDFObjectHandle
|
|||||||
bool isDictionary();
|
bool isDictionary();
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
bool isStream();
|
bool isStream();
|
||||||
|
QPDF_DLL
|
||||||
|
bool isReserved();
|
||||||
|
|
||||||
// This returns true in addition to the query for the specific
|
// This returns true in addition to the query for the specific
|
||||||
// type for indirect objects.
|
// type for indirect objects.
|
||||||
@ -148,6 +150,24 @@ class QPDFObjectHandle
|
|||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
static QPDFObjectHandle newStream(QPDF* qpdf, std::string const& data);
|
static QPDFObjectHandle newStream(QPDF* qpdf, std::string const& data);
|
||||||
|
|
||||||
|
// A reserved object is a special sentinel used for qpdf to
|
||||||
|
// reserve a spot for an object that is going to be added to the
|
||||||
|
// QPDF object. Normally you don't have to use this type since
|
||||||
|
// you can just call QPDF::makeIndirectObject. However, in some
|
||||||
|
// cases, if you have to create objects with circular references,
|
||||||
|
// you may need to create a reserved object so that you can have a
|
||||||
|
// reference to it and then replace the object later. Reserved
|
||||||
|
// objects have the special property that they can't be resolved
|
||||||
|
// to direct objects. This makes it possible to replace a
|
||||||
|
// reserved object with a new object while preserving existing
|
||||||
|
// references to them. When you are ready to replace a reserved
|
||||||
|
// object with its replacement, use QPDF::replaceReserved for this
|
||||||
|
// purpose rather than the more general QPDF::replaceObject. It
|
||||||
|
// is an error to try to write a QPDF with QPDFWriter if it has
|
||||||
|
// any reserved objects in it.
|
||||||
|
QPDF_DLL
|
||||||
|
static QPDFObjectHandle newReserved(QPDF* qpdf);
|
||||||
|
|
||||||
// Accessor methods. If an accessor method that is valid for only
|
// Accessor methods. If an accessor method that is valid for only
|
||||||
// a particular object type is called on an object of the wrong
|
// a particular object type is called on an object of the wrong
|
||||||
// type, an exception is thrown.
|
// type, an exception is thrown.
|
||||||
@ -430,6 +450,8 @@ class QPDFObjectHandle
|
|||||||
void assertDictionary();
|
void assertDictionary();
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void assertStream();
|
void assertStream();
|
||||||
|
QPDF_DLL
|
||||||
|
void assertReserved();
|
||||||
|
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void assertScalar();
|
void assertScalar();
|
||||||
@ -459,6 +481,7 @@ class QPDFObjectHandle
|
|||||||
int objid; // 0 for direct object
|
int objid; // 0 for direct object
|
||||||
int generation;
|
int generation;
|
||||||
PointerHolder<QPDFObject> obj;
|
PointerHolder<QPDFObject> obj;
|
||||||
|
bool reserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __QPDFOBJECTHANDLE_HH__
|
#endif // __QPDFOBJECTHANDLE_HH__
|
||||||
|
@ -2056,6 +2056,18 @@ QPDF::replaceObject(int objid, int generation, QPDFObjectHandle oh)
|
|||||||
ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
|
ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDF::replaceReserved(QPDFObjectHandle reserved,
|
||||||
|
QPDFObjectHandle replacement)
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDF replaceReserved");
|
||||||
|
reserved.assertReserved();
|
||||||
|
replaceObject(reserved.getObjectID(),
|
||||||
|
reserved.getGeneration(),
|
||||||
|
replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
QPDF::swapObjects(int objid1, int generation1, int objid2, int generation2)
|
QPDF::swapObjects(int objid1, int generation1, int objid2, int generation2)
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <qpdf/QPDF_Array.hh>
|
#include <qpdf/QPDF_Array.hh>
|
||||||
#include <qpdf/QPDF_Dictionary.hh>
|
#include <qpdf/QPDF_Dictionary.hh>
|
||||||
#include <qpdf/QPDF_Stream.hh>
|
#include <qpdf/QPDF_Stream.hh>
|
||||||
|
#include <qpdf/QPDF_Reserved.hh>
|
||||||
|
|
||||||
#include <qpdf/QTC.hh>
|
#include <qpdf/QTC.hh>
|
||||||
#include <qpdf/QUtil.hh>
|
#include <qpdf/QUtil.hh>
|
||||||
@ -20,7 +21,8 @@
|
|||||||
QPDFObjectHandle::QPDFObjectHandle() :
|
QPDFObjectHandle::QPDFObjectHandle() :
|
||||||
initialized(false),
|
initialized(false),
|
||||||
objid(0),
|
objid(0),
|
||||||
generation(0)
|
generation(0),
|
||||||
|
reserved(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +30,8 @@ QPDFObjectHandle::QPDFObjectHandle(QPDF* qpdf, int objid, int generation) :
|
|||||||
initialized(true),
|
initialized(true),
|
||||||
qpdf(qpdf),
|
qpdf(qpdf),
|
||||||
objid(objid),
|
objid(objid),
|
||||||
generation(generation)
|
generation(generation),
|
||||||
|
reserved(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +40,8 @@ QPDFObjectHandle::QPDFObjectHandle(QPDFObject* data) :
|
|||||||
qpdf(0),
|
qpdf(0),
|
||||||
objid(0),
|
objid(0),
|
||||||
generation(0),
|
generation(0),
|
||||||
obj(data)
|
obj(data),
|
||||||
|
reserved(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +169,14 @@ QPDFObjectHandle::isStream()
|
|||||||
return QPDFObjectTypeAccessor<QPDF_Stream>::check(obj.getPointer());
|
return QPDFObjectTypeAccessor<QPDF_Stream>::check(obj.getPointer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isReserved()
|
||||||
|
{
|
||||||
|
// dereference will clear reserved if this has been replaced
|
||||||
|
dereference();
|
||||||
|
return this->reserved;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
QPDFObjectHandle::isIndirect()
|
QPDFObjectHandle::isIndirect()
|
||||||
{
|
{
|
||||||
@ -568,6 +580,11 @@ QPDFObjectHandle::unparse()
|
|||||||
std::string
|
std::string
|
||||||
QPDFObjectHandle::unparseResolved()
|
QPDFObjectHandle::unparseResolved()
|
||||||
{
|
{
|
||||||
|
if (this->reserved)
|
||||||
|
{
|
||||||
|
throw std::logic_error(
|
||||||
|
"QPDFObjectHandle: attempting to unparse a reserved object");
|
||||||
|
}
|
||||||
dereference();
|
dereference();
|
||||||
return this->obj->unparse();
|
return this->obj->unparse();
|
||||||
}
|
}
|
||||||
@ -689,6 +706,19 @@ QPDFObjectHandle::newStream(QPDF* qpdf, std::string const& data)
|
|||||||
return QPDFObjectHandle::newStream(qpdf, b);
|
return QPDFObjectHandle::newStream(qpdf, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newReserved(QPDF* qpdf)
|
||||||
|
{
|
||||||
|
// Reserve a spot for this object by assigning it an object
|
||||||
|
// number, but then return an unresolved handle to the object.
|
||||||
|
QPDFObjectHandle reserved = qpdf->makeIndirectObject(
|
||||||
|
QPDFObjectHandle(new QPDF_Reserved()));
|
||||||
|
QPDFObjectHandle result =
|
||||||
|
newIndirect(qpdf, reserved.objid, reserved.generation);
|
||||||
|
result.reserved = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QPDFObjectHandle
|
QPDFObjectHandle
|
||||||
QPDFObjectHandle::shallowCopy()
|
QPDFObjectHandle::shallowCopy()
|
||||||
{
|
{
|
||||||
@ -746,6 +776,13 @@ QPDFObjectHandle::makeDirectInternal(std::set<int>& visited)
|
|||||||
visited.insert(cur_objid);
|
visited.insert(cur_objid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isReserved())
|
||||||
|
{
|
||||||
|
throw std::logic_error(
|
||||||
|
"QPDFObjectHandle: attempting to make a"
|
||||||
|
" reserved object handle direct");
|
||||||
|
}
|
||||||
|
|
||||||
dereference();
|
dereference();
|
||||||
this->objid = 0;
|
this->objid = 0;
|
||||||
this->generation = 0;
|
this->generation = 0;
|
||||||
@ -902,6 +939,12 @@ QPDFObjectHandle::assertStream()
|
|||||||
assertType("Stream", isStream());
|
assertType("Stream", isStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::assertReserved()
|
||||||
|
{
|
||||||
|
assertType("Reserved", isReserved());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
QPDFObjectHandle::assertScalar()
|
QPDFObjectHandle::assertScalar()
|
||||||
{
|
{
|
||||||
@ -929,12 +972,21 @@ QPDFObjectHandle::dereference()
|
|||||||
{
|
{
|
||||||
if (this->obj.getPointer() == 0)
|
if (this->obj.getPointer() == 0)
|
||||||
{
|
{
|
||||||
this->obj = QPDF::Resolver::resolve(
|
PointerHolder<QPDFObject> obj = QPDF::Resolver::resolve(
|
||||||
this->qpdf, this->objid, this->generation);
|
this->qpdf, this->objid, this->generation);
|
||||||
if (this->obj.getPointer() == 0)
|
if (obj.getPointer() == 0)
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "QPDFObjectHandle indirect to unknown");
|
QTC::TC("qpdf", "QPDFObjectHandle indirect to unknown");
|
||||||
this->obj = new QPDF_Null();
|
this->obj = new QPDF_Null();
|
||||||
}
|
}
|
||||||
|
else if (dynamic_cast<QPDF_Reserved*>(obj.getPointer()))
|
||||||
|
{
|
||||||
|
// Do not resolve
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->reserved = false;
|
||||||
|
this->obj = obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
libqpdf/QPDF_Reserved.cc
Normal file
13
libqpdf/QPDF_Reserved.cc
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <qpdf/QPDF_Reserved.hh>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
QPDF_Reserved::~QPDF_Reserved()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Reserved::unparse()
|
||||||
|
{
|
||||||
|
throw std::logic_error("attempt to unparse QPDF_Reserved");
|
||||||
|
return "";
|
||||||
|
}
|
@ -39,6 +39,7 @@ SRCS_libqpdf = \
|
|||||||
libqpdf/QPDF_Name.cc \
|
libqpdf/QPDF_Name.cc \
|
||||||
libqpdf/QPDF_Null.cc \
|
libqpdf/QPDF_Null.cc \
|
||||||
libqpdf/QPDF_Real.cc \
|
libqpdf/QPDF_Real.cc \
|
||||||
|
libqpdf/QPDF_Reserved.cc \
|
||||||
libqpdf/QPDF_Stream.cc \
|
libqpdf/QPDF_Stream.cc \
|
||||||
libqpdf/QPDF_String.cc \
|
libqpdf/QPDF_String.cc \
|
||||||
libqpdf/QPDF_encryption.cc \
|
libqpdf/QPDF_encryption.cc \
|
||||||
|
13
libqpdf/qpdf/QPDF_Reserved.hh
Normal file
13
libqpdf/qpdf/QPDF_Reserved.hh
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef __QPDF_RESERVED_HH__
|
||||||
|
#define __QPDF_RESERVED_HH__
|
||||||
|
|
||||||
|
#include <qpdf/QPDFObject.hh>
|
||||||
|
|
||||||
|
class QPDF_Reserved: public QPDFObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~QPDF_Reserved();
|
||||||
|
virtual std::string unparse();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QPDF_RESERVED_HH__
|
@ -217,3 +217,4 @@ QPDFObjectHandle newStream with string 0
|
|||||||
QPDF unknown key not inherited 0
|
QPDF unknown key not inherited 0
|
||||||
QPDF_Stream provider length not provided 0
|
QPDF_Stream provider length not provided 0
|
||||||
QPDF_Stream unknown stream length 0
|
QPDF_Stream unknown stream length 0
|
||||||
|
QPDF replaceReserved 0
|
||||||
|
@ -149,7 +149,7 @@ $td->runtest("remove page we don't have",
|
|||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Miscellaneous Tests ---");
|
$td->notify("--- Miscellaneous Tests ---");
|
||||||
$n_tests += 41;
|
$n_tests += 43;
|
||||||
|
|
||||||
$td->runtest("qpdf version",
|
$td->runtest("qpdf version",
|
||||||
{$td->COMMAND => "qpdf --version"},
|
{$td->COMMAND => "qpdf --version"},
|
||||||
@ -358,6 +358,13 @@ $td->runtest("warn for unknown key in Pages",
|
|||||||
{$td->COMMAND => "test_driver 23 lin-special.pdf"},
|
{$td->COMMAND => "test_driver 23 lin-special.pdf"},
|
||||||
{$td->FILE => "pages-warning.out", $td->EXIT_STATUS => 0},
|
{$td->FILE => "pages-warning.out", $td->EXIT_STATUS => 0},
|
||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("reserved objects",
|
||||||
|
{$td->COMMAND => "test_driver 24 minimal.pdf"},
|
||||||
|
{$td->FILE => "reserved-objects.out", $td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("check output",
|
||||||
|
{$td->FILE => "a.pdf"},
|
||||||
|
{$td->FILE => "reserved-objects.pdf"});
|
||||||
|
|
||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
|
8
qpdf/qtest/qpdf/reserved-objects.out
Normal file
8
qpdf/qtest/qpdf/reserved-objects.out
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
res1 is still reserved after checking if array
|
||||||
|
res1 is no longer reserved
|
||||||
|
res1 is an array
|
||||||
|
logic error: QPDFObjectHandle: attempting to unparse a reserved object
|
||||||
|
logic error: QPDFObjectHandle: attempting to make a reserved object handle direct
|
||||||
|
res2 is an array
|
||||||
|
circular access and lazy resolution worked
|
||||||
|
test 24 done
|
48
qpdf/qtest/qpdf/reserved-objects.pdf
Normal file
48
qpdf/qtest/qpdf/reserved-objects.pdf
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
%PDF-1.3
|
||||||
|
%¿÷¢þ
|
||||||
|
1 0 obj
|
||||||
|
<< /Pages 4 0 R /Type /Catalog >>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
[ 3 0 R 1 ]
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
[ 2 0 R 2 ]
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<< /Count 1 /Kids [ 5 0 R ] /Type /Pages >>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<< /Contents 6 0 R /MediaBox [ 0 0 612 792 ] /Parent 4 0 R /Resources << /Font << /F1 7 0 R >> /ProcSet 8 0 R >> /Type /Page >>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 44 >>
|
||||||
|
stream
|
||||||
|
BT
|
||||||
|
/F1 24 Tf
|
||||||
|
72 720 Td
|
||||||
|
(Potato) Tj
|
||||||
|
ET
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
[ /PDF /Text ]
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 9
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000015 00000 n
|
||||||
|
0000000064 00000 n
|
||||||
|
0000000091 00000 n
|
||||||
|
0000000118 00000 n
|
||||||
|
0000000177 00000 n
|
||||||
|
0000000320 00000 n
|
||||||
|
0000000413 00000 n
|
||||||
|
0000000520 00000 n
|
||||||
|
trailer << /Root 1 0 R /Size 9 Array1 2 0 R Array2 3 0 R /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >>
|
||||||
|
startxref
|
||||||
|
550
|
||||||
|
%%EOF
|
@ -840,6 +840,82 @@ void runtest(int n, char const* filename)
|
|||||||
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
|
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
|
||||||
pdf.removePage(pages.back());
|
pdf.removePage(pages.back());
|
||||||
}
|
}
|
||||||
|
else if (n == 24)
|
||||||
|
{
|
||||||
|
// Test behavior of reserved objects
|
||||||
|
QPDFObjectHandle res1 = QPDFObjectHandle::newReserved(&pdf);
|
||||||
|
QPDFObjectHandle res2 = QPDFObjectHandle::newReserved(&pdf);
|
||||||
|
QPDFObjectHandle trailer = pdf.getTrailer();
|
||||||
|
trailer.replaceKey("Array1", res1);
|
||||||
|
trailer.replaceKey("Array2", res2);
|
||||||
|
|
||||||
|
QPDFObjectHandle array1 = QPDFObjectHandle::newArray();
|
||||||
|
QPDFObjectHandle array2 = QPDFObjectHandle::newArray();
|
||||||
|
array1.appendItem(res2);
|
||||||
|
array1.appendItem(QPDFObjectHandle::newInteger(1));
|
||||||
|
array2.appendItem(res1);
|
||||||
|
array2.appendItem(QPDFObjectHandle::newInteger(2));
|
||||||
|
// Make sure trying to ask questions about a reserved object
|
||||||
|
// doesn't break it.
|
||||||
|
if (res1.isArray())
|
||||||
|
{
|
||||||
|
std::cout << "oops -- res1 is an array" << std::endl;
|
||||||
|
}
|
||||||
|
if (res1.isReserved())
|
||||||
|
{
|
||||||
|
std::cout << "res1 is still reserved after checking if array"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
pdf.replaceReserved(res1, array1);
|
||||||
|
if (res1.isReserved())
|
||||||
|
{
|
||||||
|
std::cout << "oops -- res1 is still reserved" << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "res1 is no longer reserved" << std::endl;
|
||||||
|
}
|
||||||
|
res1.assertArray();
|
||||||
|
std::cout << "res1 is an array" << std::endl;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
res2.unparseResolved();
|
||||||
|
std::cout << "oops -- didn't throw" << std::endl;
|
||||||
|
}
|
||||||
|
catch (std::logic_error e)
|
||||||
|
{
|
||||||
|
std::cout << "logic error: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
res2.makeDirect();
|
||||||
|
std::cout << "oops -- didn't throw" << std::endl;
|
||||||
|
}
|
||||||
|
catch (std::logic_error e)
|
||||||
|
{
|
||||||
|
std::cout << "logic error: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdf.replaceReserved(res2, array2);
|
||||||
|
|
||||||
|
res2.assertArray();
|
||||||
|
std::cout << "res2 is an array" << std::endl;
|
||||||
|
|
||||||
|
// Verify that the previously added reserved keys can be
|
||||||
|
// dereferenced properly now
|
||||||
|
int i1 = res1.getArrayItem(0).getArrayItem(1).getIntValue();
|
||||||
|
int i2 = res2.getArrayItem(0).getArrayItem(1).getIntValue();
|
||||||
|
if ((i1 == 2) && (i2 == 1))
|
||||||
|
{
|
||||||
|
std::cout << "circular access and lazy resolution worked" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFWriter w(pdf, "a.pdf");
|
||||||
|
w.setStaticID(true);
|
||||||
|
w.setStreamDataMode(qpdf_s_preserve);
|
||||||
|
w.write();
|
||||||
|
}
|
||||||
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