2
1
mirror of https://github.com/qpdf/qpdf.git synced 2025-01-03 07:12:28 +00:00

implement replace and swap

This commit is contained in:
Jay Berkenbilt 2011-08-10 12:42:48 -04:00
parent b1a5b7e5d4
commit 7dc197ef88
9 changed files with 506 additions and 2 deletions

View File

@ -128,6 +128,36 @@ class QPDF
QPDF_DLL
QPDFObjectHandle getObjectByID(int objid, int generation);
// Replace the object with the given object id with the given
// object. The object handle passed in must be a direct object,
// though it may contain references to other indirect objects
// within it. Calling this method can have somewhat confusing
// results. Any existing QPDFObjectHandle instances that point to
// the old object and that have been resolved (which happens
// automatically if you access them in any way) will continue to
// point to the old object even though that object will no longer
// be associated with the PDF file. Note that replacing an object
// with QPDFObjectHandle::newNull() effectively removes the object
// from the file since a non-existent object is treated as a null
// object.
QPDF_DLL
void replaceObject(int objid, int generation, QPDFObjectHandle);
// Swap two objects given by ID. Calling this method can have
// confusing results. After swapping two objects, existing
// QPDFObjectHandle instances that reference them will still
// reference the same underlying objects, at which point those
// existing QPDFObjectHandle instances will have incorrect
// information about the object and generation number of those
// objects. While this does not necessarily cause a problem, it
// can certainly be confusing. It is therefore recommended that
// you replace any existing QPDFObjectHandle instances that point
// to the swapped objects with new ones, possibly by calling
// getObjectByID.
QPDF_DLL
void swapObjects(int objid1, int generation1,
int objid2, int generation2);
// Encryption support
enum encryption_method_e { e_none, e_unknown, e_rc4, e_aes };

View File

@ -1887,6 +1887,39 @@ QPDF::getObjectByID(int objid, int generation)
return QPDFObjectHandle::Factory::newIndirect(this, objid, generation);
}
void
QPDF::replaceObject(int objid, int generation, QPDFObjectHandle oh)
{
if (oh.isIndirect())
{
QTC::TC("qpdf", "QPDF replaceObject called with indirect object");
throw std::logic_error(
"QPDF::replaceObject called with indirect object handle");
}
// Force new object to appear in the cache
resolve(objid, generation);
// Replace the object in the object cache
ObjGen og(objid, generation);
this->obj_cache[og] =
ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
}
void
QPDF::swapObjects(int objid1, int generation1, int objid2, int generation2)
{
// Force objects to be loaded into cache; then swap them in the
// cache.
resolve(objid1, generation1);
resolve(objid2, generation2);
ObjGen og1(objid1, generation1);
ObjGen og2(objid2, generation2);
ObjCache t = this->obj_cache[og1];
this->obj_cache[og1] = this->obj_cache[og2];
this->obj_cache[og2] = t;
}
void
QPDF::trimTrailerForWrite()
{

View File

@ -70,7 +70,7 @@ QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) :
QPDFWriter::~QPDFWriter()
{
if (file)
if (file && close_file)
{
fclose(file);
}

View File

@ -192,3 +192,4 @@ QPDF stream without newline 0
QPDF stream with CR only 0
QPDF stream with CRNL 0
QPDF stream with NL only 0
QPDF replaceObject called with indirect object 0

View File

@ -111,7 +111,7 @@ $td->runtest("new stream",
show_ntests();
# ----------
$td->notify("--- Miscellaneous Tests ---");
$n_tests += 31;
$n_tests += 33;
$td->runtest("qpdf version",
{$td->COMMAND => "qpdf --version"},
@ -276,6 +276,15 @@ $td->runtest("check output",
{$td->FILE => "a.qdf"},
{$td->FILE => "stream-line-enders.qdf"});
$td->runtest("swap and replace",
{$td->COMMAND => "test_driver 14 test14-in.pdf"},
{$td->FILE => "test14.out",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("check output",
{$td->FILE => "a.pdf"},
{$td->FILE => "test14-out.pdf"});
show_ntests();
# ----------
$td->notify("--- Error Condition Tests ---");

View File

@ -0,0 +1,264 @@
%PDF-1.3
%¿÷¢þ
%QDF-1.0
1 0 obj
<<
/Pages 4 0 R
/Type /Catalog
>>
endobj
2 0 obj
[
/Array
]
endobj
3 0 obj
<<
/Dict 1
>>
endobj
4 0 obj
<<
/Count 4
/Kids [
5 0 R
6 0 R
7 0 R
8 0 R
]
/Type /Pages
>>
endobj
%% Page 1
5 0 obj
<<
/Contents 9 0 R
/MediaBox [
0
0
612
792
]
/Parent 4 0 R
/Resources <<
/Font <<
/F1 11 0 R
>>
/ProcSet 12 0 R
>>
/Type /Page
>>
endobj
%% Page 2
6 0 obj
<<
/Contents 13 0 R
/MediaBox [
0
0
612
792
]
/Parent 4 0 R
/Resources <<
/Font <<
/F1 11 0 R
>>
/ProcSet 15 0 R
>>
/Type /Page
>>
endobj
%% Page 3
7 0 obj
<<
/Contents 16 0 R
/MediaBox [
0
0
612
792
]
/Parent 4 0 R
/Resources <<
/Font <<
/F1 11 0 R
>>
/ProcSet 18 0 R
>>
/Type /Page
>>
endobj
%% Page 4
8 0 obj
<<
/Contents 19 0 R
/MediaBox [
0
0
612
792
]
/Parent 4 0 R
/Resources <<
/Font <<
/F1 11 0 R
>>
/ProcSet 21 0 R
>>
/Type /Page
>>
endobj
%% Contents for page 1
9 0 obj
<<
/Length 10 0 R
>>
stream
BT
/F1 24 Tf
72 720 Td
(Potato 1) Tj
ET
endstream
endobj
10 0 obj
46
endobj
11 0 obj
<<
/BaseFont /Helvetica
/Encoding /WinAnsiEncoding
/Name /F1
/Subtype /Type1
/Type /Font
>>
endobj
12 0 obj
[
/PDF
/Text
]
endobj
%% Contents for page 2
13 0 obj
<<
/Length 14 0 R
>>
stream
BT
/F1 24 Tf
72 720 Td
(Potato 2) Tj
ET
endstream
endobj
14 0 obj
46
endobj
15 0 obj
[
/PDF
/Text
]
endobj
%% Contents for page 3
16 0 obj
<<
/Length 17 0 R
>>
stream
BT
/F1 24 Tf
72 720 Td
(Potato 3) Tj
ET
endstream
endobj
17 0 obj
46
endobj
18 0 obj
[
/PDF
/Text
]
endobj
%% Contents for page 4
19 0 obj
<<
/Length 20 0 R
>>
stream
BT
/F1 24 Tf
72 720 Td
(Potato 4) Tj
ET
endstream
endobj
20 0 obj
46
endobj
21 0 obj
[
/PDF
/Text
]
endobj
xref
0 22
0000000000 65535 f
0000000025 00000 n
0000000079 00000 n
0000000108 00000 n
0000000140 00000 n
0000000252 00000 n
0000000456 00000 n
0000000661 00000 n
0000000866 00000 n
0000001084 00000 n
0000001186 00000 n
0000001206 00000 n
0000001325 00000 n
0000001384 00000 n
0000001487 00000 n
0000001507 00000 n
0000001566 00000 n
0000001669 00000 n
0000001689 00000 n
0000001748 00000 n
0000001851 00000 n
0000001871 00000 n
trailer <<
/QArray 2 0 R
/QDict 3 0 R
/Root 1 0 R
/Size 22
/ID [<20eb74876a3e8212c1b4fd43153860b0><1bb7a926da191c58f675435d77997d21>]
>>
startxref
1907
%%EOF

View File

@ -0,0 +1,105 @@
%PDF-1.3
%¿÷¢þ
1 0 obj
<< /Pages 4 0 R /Type /Catalog >>
endobj
2 0 obj
<< /NewDict 2 >>
endobj
3 0 obj
[ /Array ]
endobj
4 0 obj
<< /Count 4 /Kids [ 5 0 R 6 0 R 7 0 R 8 0 R ] /Type /Pages >>
endobj
5 0 obj
<< /Contents 9 0 R /MediaBox [ 0 0 612 792 ] /Parent 4 0 R /Resources << /Font << /F1 10 0 R >> /ProcSet 11 0 R >> /Type /Page >>
endobj
6 0 obj
<< /Contents 12 0 R /MediaBox [ 0 0 612 792 ] /Parent 4 0 R /Resources << /Font << /F1 10 0 R >> /ProcSet 13 0 R >> /Type /Page >>
endobj
7 0 obj
<< /Contents 14 0 R /MediaBox [ 0 0 612 792 ] /Parent 4 0 R /Resources << /Font << /F1 10 0 R >> /ProcSet 15 0 R >> /Type /Page >>
endobj
8 0 obj
<< /Contents 16 0 R /MediaBox [ 0 0 612 792 ] /Parent 4 0 R /Resources << /Font << /F1 10 0 R >> /ProcSet 17 0 R >> /Type /Page >>
endobj
9 0 obj
<< /Length 46 >>
stream
BT
/F1 24 Tf
72 720 Td
(Potato 1) Tj
ET
endstream
endobj
10 0 obj
<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >>
endobj
11 0 obj
[ /PDF /Text ]
endobj
12 0 obj
<< /Length 46 >>
stream
BT
/F1 24 Tf
72 720 Td
(Potato 3) Tj
ET
endstream
endobj
13 0 obj
[ /PDF /Text ]
endobj
14 0 obj
<< /Length 46 >>
stream
BT
/F1 24 Tf
72 720 Td
(Potato 2) Tj
ET
endstream
endobj
15 0 obj
[ /PDF /Text ]
endobj
16 0 obj
<< /Length 46 >>
stream
BT
/F1 24 Tf
72 720 Td
(Potato 4) Tj
ET
endstream
endobj
17 0 obj
[ /PDF /Text ]
endobj
xref
0 18
0000000000 65535 f
0000000015 00000 n
0000000064 00000 n
0000000096 00000 n
0000000122 00000 n
0000000199 00000 n
0000000344 00000 n
0000000490 00000 n
0000000636 00000 n
0000000782 00000 n
0000000877 00000 n
0000000985 00000 n
0000001016 00000 n
0000001112 00000 n
0000001143 00000 n
0000001239 00000 n
0000001270 00000 n
0000001366 00000 n
trailer << /QArray 2 0 R /QDict 3 0 R /Root 1 0 R /Size 18 /ID [<20eb74876a3e8212c1b4fd43153860b0><31415926535897932384626433832795>] >>
startxref
1397
%%EOF

View File

@ -0,0 +1,6 @@
caught logic error as expected
old dict: 1
old dict: 1
new dict: 2
swapped array: /Array
test 14 done

View File

@ -519,6 +519,62 @@ void runtest(int n, char const* filename)
<< "---error---" << std::endl
<< err.str();
}
else if (n == 14)
{
// Exercise swap and replace. This test case is designed for
// a specific file.
std::vector<QPDFObjectHandle> pages = pdf.getAllPages();
if (pages.size() != 4)
{
throw std::logic_error("test " + QUtil::int_to_string(n) +
" not called 4-page file");
}
// Swap pages 2 and 3
pdf.swapObjects(pages[1].getObjectID(), pages[1].getGeneration(),
pages[2].getObjectID(), pages[2].getGeneration());
// Replace object and swap objects
QPDFObjectHandle trailer = pdf.getTrailer();
QPDFObjectHandle qdict = trailer.getKey("/QDict");
QPDFObjectHandle qarray = trailer.getKey("/QArray");
// Force qdict but not qarray to resolve
qdict.isDictionary();
std::map<std::string, QPDFObjectHandle> dict_keys;
dict_keys["/NewDict"] = QPDFObjectHandle::newInteger(2);
QPDFObjectHandle new_dict = QPDFObjectHandle::newDictionary(dict_keys);
try
{
// Do it wrong first...
pdf.replaceObject(qdict.getObjectID(), qdict.getGeneration(),
qdict);
}
catch (std::logic_error)
{
std::cout << "caught logic error as expected" << std::endl;
}
pdf.replaceObject(qdict.getObjectID(), qdict.getGeneration(),
new_dict);
// Now qdict still points to the old dictionary
std::cout << "old dict: " << qdict.getKey("/Dict").getIntValue()
<< std::endl;
// Swap dict and array
pdf.swapObjects(qdict.getObjectID(), qdict.getGeneration(),
qarray.getObjectID(), qarray.getGeneration());
// Now qarray will resolve to new object but qdict is still
// the old object
std::cout << "old dict: " << qdict.getKey("/Dict").getIntValue()
<< std::endl;
std::cout << "new dict: " << qarray.getKey("/NewDict").getIntValue()
<< std::endl;
// Reread qdict, now pointing to an array
qdict = pdf.getObjectByID(qdict.getObjectID(), qdict.getGeneration());
std::cout << "swapped array: " << qdict.getArrayItem(0).getName()
<< std::endl;
QPDFWriter w(pdf, "a.pdf");
w.setStaticID(true);
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else
{
throw std::runtime_error(std::string("invalid test ") +