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:
parent
b1a5b7e5d4
commit
7dc197ef88
@ -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 };
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -70,7 +70,7 @@ QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) :
|
||||
|
||||
QPDFWriter::~QPDFWriter()
|
||||
{
|
||||
if (file)
|
||||
if (file && close_file)
|
||||
{
|
||||
fclose(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
|
||||
|
@ -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 ---");
|
||||
|
264
qpdf/qtest/qpdf/test14-in.pdf
Normal file
264
qpdf/qtest/qpdf/test14-in.pdf
Normal 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
|
105
qpdf/qtest/qpdf/test14-out.pdf
Normal file
105
qpdf/qtest/qpdf/test14-out.pdf
Normal 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
|
6
qpdf/qtest/qpdf/test14.out
Normal file
6
qpdf/qtest/qpdf/test14.out
Normal 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
|
@ -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 ") +
|
||||
|
Loading…
Reference in New Issue
Block a user