From 63d1dcb414ad92cae857da636f242b34acac394b Mon Sep 17 00:00:00 2001 From: m-holger Date: Sun, 20 Nov 2022 12:30:05 +0000 Subject: Split QPDFObjectHandle::shallowCopyInternal and copyObject Have separate versions for unsafeShallowCopy, shallowCopy and makeDirect. --- include/qpdf/QPDFObjectHandle.hh | 13 ++- libqpdf/QPDFObjectHandle.cc | 173 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 180 insertions(+), 6 deletions(-) diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 3610422e..b186d4f8 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -1628,12 +1628,23 @@ class QPDFObjectHandle void objectWarning(std::string const& warning); void assertType(char const* type_name, bool istype); bool dereference(); + void copyObject1( + std::set& visited, + bool cross_indirect, + bool first_level_only, + bool stop_at_streams); + void shallowCopyInternal1(QPDFObjectHandle& oh, bool first_level_only); + void copyObject2( + std::set& visited, + bool cross_indirect, + bool first_level_only, + bool stop_at_streams); + void shallowCopyInternal2(QPDFObjectHandle& oh, bool first_level_only); void copyObject( std::set& visited, bool cross_indirect, bool first_level_only, bool stop_at_streams); - void shallowCopyInternal(QPDFObjectHandle& oh, bool first_level_only); void disconnect(); void setParsedOffset(qpdf_offset_t offset); void parseContentStream_internal( diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 02b40c4f..3938fcd2 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -2201,32 +2201,195 @@ QPDFObjectHandle QPDFObjectHandle::shallowCopy() { QPDFObjectHandle result; - shallowCopyInternal(result, false); + shallowCopyInternal1(result, false); return result; } +void +QPDFObjectHandle::shallowCopyInternal1( + QPDFObjectHandle& new_obj, bool first_level_only) +{ + assertInitialized(); + + if (isStream()) { + QTC::TC("qpdf", "QPDFObjectHandle ERR shallow copy stream"); + throw std::runtime_error("attempt to make a shallow copy of a stream"); + } + new_obj = QPDFObjectHandle(obj->copy(true)); + + std::set visited; + new_obj.copyObject1(visited, false, first_level_only, false); +} + +void +QPDFObjectHandle::copyObject1( + std::set& visited, + bool cross_indirect, + bool first_level_only, + bool stop_at_streams) +{ + assertInitialized(); + + if (isStream()) { + if (stop_at_streams) { + return; + } + throw std::runtime_error( + "attempt to make a stream into a direct object"); + } + + auto cur_og = getObjGen(); + if (cur_og.getObj() != 0) { + if (visited.count(cur_og)) { + throw std::runtime_error( + "loop detected while converting object from " + "indirect to direct"); + } + visited.insert(cur_og); + } + + if (isReserved()) { + throw std::logic_error("QPDFObjectHandle: attempting to make a" + " reserved object handle direct"); + } + + std::shared_ptr new_obj; + + if (isBool() || isInteger() || isName() || isNull() || isReal() || + isString()) { + new_obj = obj->copy(true); + } else if (isArray()) { + std::vector items; + auto array = asArray(); + int n = array->getNItems(); + for (int i = 0; i < n; ++i) { + items.push_back(array->getItem(i)); + if ((!first_level_only) && + (cross_indirect || (!items.back().isIndirect()))) { + items.back().copyObject1( + visited, cross_indirect, first_level_only, stop_at_streams); + } + } + new_obj = QPDF_Array::create(items); + } else if (isDictionary()) { + std::map items; + auto dict = asDictionary(); + for (auto const& key: getKeys()) { + items[key] = dict->getKey(key); + if ((!first_level_only) && + (cross_indirect || (!items[key].isIndirect()))) { + items[key].copyObject1( + visited, cross_indirect, first_level_only, stop_at_streams); + } + } + new_obj = QPDF_Dictionary::create(items); + } else { + throw std::logic_error("QPDFObjectHandle::makeDirectInternal: " + "unknown object type"); + } + + this->obj = new_obj; + + if (cur_og.getObj()) { + visited.erase(cur_og); + } +} + QPDFObjectHandle QPDFObjectHandle::unsafeShallowCopy() { QPDFObjectHandle result; - shallowCopyInternal(result, true); + shallowCopyInternal2(result, true); return result; } void -QPDFObjectHandle::shallowCopyInternal( +QPDFObjectHandle::shallowCopyInternal2( QPDFObjectHandle& new_obj, bool first_level_only) { assertInitialized(); if (isStream()) { - QTC::TC("qpdf", "QPDFObjectHandle ERR shallow copy stream"); throw std::runtime_error("attempt to make a shallow copy of a stream"); } new_obj = QPDFObjectHandle(obj->copy(true)); std::set visited; - new_obj.copyObject(visited, false, first_level_only, false); + new_obj.copyObject2(visited, false, first_level_only, false); +} + +void +QPDFObjectHandle::copyObject2( + std::set& visited, + bool cross_indirect, + bool first_level_only, + bool stop_at_streams) +{ + assertInitialized(); + + if (isStream()) { + if (stop_at_streams) { + return; + } + throw std::runtime_error( + "attempt to make a stream into a direct object"); + } + + auto cur_og = getObjGen(); + if (cur_og.getObj() != 0) { + if (visited.count(cur_og)) { + throw std::runtime_error( + "loop detected while converting object from " + "indirect to direct"); + } + visited.insert(cur_og); + } + + if (isReserved()) { + throw std::logic_error("QPDFObjectHandle: attempting to make a" + " reserved object handle direct"); + } + + std::shared_ptr new_obj; + + if (isBool() || isInteger() || isName() || isNull() || isReal() || + isString()) { + new_obj = obj->copy(true); + } else if (isArray()) { + std::vector items; + auto array = asArray(); + int n = array->getNItems(); + for (int i = 0; i < n; ++i) { + items.push_back(array->getItem(i)); + if ((!first_level_only) && + (cross_indirect || (!items.back().isIndirect()))) { + items.back().copyObject2( + visited, cross_indirect, first_level_only, stop_at_streams); + } + } + new_obj = QPDF_Array::create(items); + } else if (isDictionary()) { + std::map items; + auto dict = asDictionary(); + for (auto const& key: getKeys()) { + items[key] = dict->getKey(key); + if ((!first_level_only) && + (cross_indirect || (!items[key].isIndirect()))) { + items[key].copyObject2( + visited, cross_indirect, first_level_only, stop_at_streams); + } + } + new_obj = QPDF_Dictionary::create(items); + } else { + throw std::logic_error("QPDFObjectHandle::makeDirectInternal: " + "unknown object type"); + } + + this->obj = new_obj; + + if (cur_og.getObj()) { + visited.erase(cur_og); + } } void -- cgit v1.2.3-54-g00ecf