Commit ec6784411d4f9fbf99a03f8e092e38fd3816049a

Authored by Jay Berkenbilt
Committed by GitHub
2 parents fc656816 ac32dbc3

Merge pull request #1028 from m-holger/i1003

Maintain links to foreign pages when copying foreign objects (fixes #1003)
include/qpdf/QPDF.hh
@@ -351,6 +351,8 @@ class QPDF @@ -351,6 +351,8 @@ class QPDF
351 // QPDF with QPDFWriter if it has any reserved objects in it. 351 // QPDF with QPDFWriter if it has any reserved objects in it.
352 QPDF_DLL 352 QPDF_DLL
353 QPDFObjectHandle newReserved(); 353 QPDFObjectHandle newReserved();
  354 + QPDF_DLL
  355 + QPDFObjectHandle newIndirectNull();
354 356
355 // Install this object handle as an indirect object and return an indirect reference to it. 357 // Install this object handle as an indirect object and return an indirect reference to it.
356 QPDF_DLL 358 QPDF_DLL
@@ -391,8 +393,8 @@ class QPDF @@ -391,8 +393,8 @@ class QPDF
391 void swapObjects(int objid1, int generation1, int objid2, int generation2); 393 void swapObjects(int objid1, int generation1, int objid2, int generation2);
392 394
393 // Replace a reserved object. This is a wrapper around replaceObject but it guarantees that the 395 // Replace a reserved object. This is a wrapper around replaceObject but it guarantees that the
394 - // underlying object is a reserved object. After this call, reserved will be a reference to  
395 - // replacement. 396 + // underlying object is a reserved object or a null object. After this call, reserved will
  397 + // be a reference to replacement.
396 QPDF_DLL 398 QPDF_DLL
397 void replaceReserved(QPDFObjectHandle reserved, QPDFObjectHandle replacement); 399 void replaceReserved(QPDFObjectHandle reserved, QPDFObjectHandle replacement);
398 400
libqpdf/QPDF.cc
@@ -1861,6 +1861,12 @@ QPDF::newReserved() @@ -1861,6 +1861,12 @@ QPDF::newReserved()
1861 } 1861 }
1862 1862
1863 QPDFObjectHandle 1863 QPDFObjectHandle
  1864 +QPDF::newIndirectNull()
  1865 +{
  1866 + return makeIndirectFromQPDFObject(QPDF_Null::create());
  1867 +}
  1868 +
  1869 +QPDFObjectHandle
1864 QPDF::newStream() 1870 QPDF::newStream()
1865 { 1871 {
1866 return makeIndirectFromQPDFObject( 1872 return makeIndirectFromQPDFObject(
@@ -1948,7 +1954,10 @@ void @@ -1948,7 +1954,10 @@ void
1948 QPDF::replaceReserved(QPDFObjectHandle reserved, QPDFObjectHandle replacement) 1954 QPDF::replaceReserved(QPDFObjectHandle reserved, QPDFObjectHandle replacement)
1949 { 1955 {
1950 QTC::TC("qpdf", "QPDF replaceReserved"); 1956 QTC::TC("qpdf", "QPDF replaceReserved");
1951 - reserved.assertReserved(); 1957 + auto tc = reserved.getTypeCode();
  1958 + if (!(tc == ::ot_reserved || tc == ::ot_null)) {
  1959 + throw std::logic_error("replaceReserved called with non-reserverd object");
  1960 + }
1952 replaceObject(reserved.getObjGen(), replacement); 1961 replaceObject(reserved.getObjGen(), replacement);
1953 } 1962 }
1954 1963
@@ -2012,8 +2021,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) @@ -2012,8 +2021,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
2012 reserveObjects(foreign, obj_copier, true); 2021 reserveObjects(foreign, obj_copier, true);
2013 2022
2014 if (!obj_copier.visiting.empty()) { 2023 if (!obj_copier.visiting.empty()) {
2015 - throw std::logic_error("obj_copier.visiting is not empty"  
2016 - " after reserving objects"); 2024 + throw std::logic_error("obj_copier.visiting is not empty after reserving objects");
2017 } 2025 }
2018 2026
2019 // Copy any new objects and replace the reservations. 2027 // Copy any new objects and replace the reservations.
@@ -2026,7 +2034,13 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) @@ -2026,7 +2034,13 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
2026 } 2034 }
2027 obj_copier.to_copy.clear(); 2035 obj_copier.to_copy.clear();
2028 2036
2029 - return obj_copier.object_map[foreign.getObjGen()]; 2037 + auto& result = obj_copier.object_map[foreign.getObjGen()];
  2038 + if (!result.isInitialized()) {
  2039 + result = QPDFObjectHandle::newNull();
  2040 + warn(damagedPDF("Unexpected reference to /Pages object while copying foreign object. "
  2041 + "Replacing with Null object."));
  2042 + }
  2043 + return result;
2030 } 2044 }
2031 2045
2032 void 2046 void
@@ -2042,11 +2056,6 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) @@ -2042,11 +2056,6 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top)
2042 return; 2056 return;
2043 } 2057 }
2044 2058
2045 - if ((!top) && foreign.isPageObject()) {  
2046 - QTC::TC("qpdf", "QPDF not crossing page boundary");  
2047 - return;  
2048 - }  
2049 -  
2050 if (foreign.isIndirect()) { 2059 if (foreign.isIndirect()) {
2051 QPDFObjGen foreign_og(foreign.getObjGen()); 2060 QPDFObjGen foreign_og(foreign.getObjGen());
2052 if (obj_copier.object_map.count(foreign_og) > 0) { 2061 if (obj_copier.object_map.count(foreign_og) > 0) {
@@ -2061,8 +2070,14 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) @@ -2061,8 +2070,14 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top)
2061 } 2070 }
2062 QTC::TC("qpdf", "QPDF copy indirect"); 2071 QTC::TC("qpdf", "QPDF copy indirect");
2063 if (obj_copier.object_map.count(foreign_og) == 0) { 2072 if (obj_copier.object_map.count(foreign_og) == 0) {
  2073 + obj_copier.object_map[foreign_og] =
  2074 + foreign.isStream() ? newStream() : newIndirectNull();
  2075 + if ((!top) && foreign.isPageObject()) {
  2076 + QTC::TC("qpdf", "QPDF not crossing page boundary");
  2077 + obj_copier.visiting.erase(foreign);
  2078 + return;
  2079 + }
2064 obj_copier.to_copy.push_back(foreign); 2080 obj_copier.to_copy.push_back(foreign);
2065 - obj_copier.object_map[foreign_og] = foreign.isStream() ? newStream() : newReserved();  
2066 } 2081 }
2067 } 2082 }
2068 2083
@@ -2094,7 +2109,7 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_cop @@ -2094,7 +2109,7 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_cop
2094 QTC::TC("qpdf", "QPDF replace indirect"); 2109 QTC::TC("qpdf", "QPDF replace indirect");
2095 auto mapping = obj_copier.object_map.find(foreign.getObjGen()); 2110 auto mapping = obj_copier.object_map.find(foreign.getObjGen());
2096 if (mapping == obj_copier.object_map.end()) { 2111 if (mapping == obj_copier.object_map.end()) {
2097 - // This case would occur if this is a reference to a Page or Pages object that we didn't 2112 + // This case would occur if this is a reference to a Pages object that we didn't
2098 // traverse into. 2113 // traverse into.
2099 QTC::TC("qpdf", "QPDF replace foreign indirect with null"); 2114 QTC::TC("qpdf", "QPDF replace foreign indirect with null");
2100 result = QPDFObjectHandle::newNull(); 2115 result = QPDFObjectHandle::newNull();
libqpdf/QPDF_Dictionary.cc
@@ -121,8 +121,10 @@ QPDF_Dictionary::getAsMap() const @@ -121,8 +121,10 @@ QPDF_Dictionary::getAsMap() const
121 void 121 void
122 QPDF_Dictionary::replaceKey(std::string const& key, QPDFObjectHandle value) 122 QPDF_Dictionary::replaceKey(std::string const& key, QPDFObjectHandle value)
123 { 123 {
124 - if (value.isNull()) {  
125 - // The PDF spec doesn't distinguish between keys with null values and missing keys. 124 + if (value.isNull() && !value.isIndirect()) {
  125 + // The PDF spec doesn't distinguish between keys with null values and missing keys. Allow
  126 + // indirect nulls which are equivalent to a dangling reference, which is permitted by the
  127 + // spec.
126 removeKey(key); 128 removeKey(key);
127 } else { 129 } else {
128 // add or replace value 130 // add or replace value
qpdf/qtest/qpdf/test80b1.pdf
No preview for this file type
qpdf/qtest/qpdf/test80b2.pdf
No preview for this file type