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 351 // QPDF with QPDFWriter if it has any reserved objects in it.
352 352 QPDF_DLL
353 353 QPDFObjectHandle newReserved();
  354 + QPDF_DLL
  355 + QPDFObjectHandle newIndirectNull();
354 356  
355 357 // Install this object handle as an indirect object and return an indirect reference to it.
356 358 QPDF_DLL
... ... @@ -391,8 +393,8 @@ class QPDF
391 393 void swapObjects(int objid1, int generation1, int objid2, int generation2);
392 394  
393 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 398 QPDF_DLL
397 399 void replaceReserved(QPDFObjectHandle reserved, QPDFObjectHandle replacement);
398 400  
... ...
libqpdf/QPDF.cc
... ... @@ -1861,6 +1861,12 @@ QPDF::newReserved()
1861 1861 }
1862 1862  
1863 1863 QPDFObjectHandle
  1864 +QPDF::newIndirectNull()
  1865 +{
  1866 + return makeIndirectFromQPDFObject(QPDF_Null::create());
  1867 +}
  1868 +
  1869 +QPDFObjectHandle
1864 1870 QPDF::newStream()
1865 1871 {
1866 1872 return makeIndirectFromQPDFObject(
... ... @@ -1948,7 +1954,10 @@ void
1948 1954 QPDF::replaceReserved(QPDFObjectHandle reserved, QPDFObjectHandle replacement)
1949 1955 {
1950 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 1961 replaceObject(reserved.getObjGen(), replacement);
1953 1962 }
1954 1963  
... ... @@ -2012,8 +2021,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
2012 2021 reserveObjects(foreign, obj_copier, true);
2013 2022  
2014 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 2027 // Copy any new objects and replace the reservations.
... ... @@ -2026,7 +2034,13 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
2026 2034 }
2027 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 2046 void
... ... @@ -2042,11 +2056,6 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top)
2042 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 2059 if (foreign.isIndirect()) {
2051 2060 QPDFObjGen foreign_og(foreign.getObjGen());
2052 2061 if (obj_copier.object_map.count(foreign_og) > 0) {
... ... @@ -2061,8 +2070,14 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top)
2061 2070 }
2062 2071 QTC::TC("qpdf", "QPDF copy indirect");
2063 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 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 2109 QTC::TC("qpdf", "QPDF replace indirect");
2095 2110 auto mapping = obj_copier.object_map.find(foreign.getObjGen());
2096 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 2113 // traverse into.
2099 2114 QTC::TC("qpdf", "QPDF replace foreign indirect with null");
2100 2115 result = QPDFObjectHandle::newNull();
... ...
libqpdf/QPDF_Dictionary.cc
... ... @@ -121,8 +121,10 @@ QPDF_Dictionary::getAsMap() const
121 121 void
122 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 128 removeKey(key);
127 129 } else {
128 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