Commit 63d1dcb414ad92cae857da636f242b34acac394b

Authored by m-holger
Committed by Jay Berkenbilt
1 parent dbc5f07b

Split QPDFObjectHandle::shallowCopyInternal and copyObject

Have separate versions for unsafeShallowCopy, shallowCopy and makeDirect.
include/qpdf/QPDFObjectHandle.hh
... ... @@ -1628,12 +1628,23 @@ class QPDFObjectHandle
1628 1628 void objectWarning(std::string const& warning);
1629 1629 void assertType(char const* type_name, bool istype);
1630 1630 bool dereference();
  1631 + void copyObject1(
  1632 + std::set<QPDFObjGen>& visited,
  1633 + bool cross_indirect,
  1634 + bool first_level_only,
  1635 + bool stop_at_streams);
  1636 + void shallowCopyInternal1(QPDFObjectHandle& oh, bool first_level_only);
  1637 + void copyObject2(
  1638 + std::set<QPDFObjGen>& visited,
  1639 + bool cross_indirect,
  1640 + bool first_level_only,
  1641 + bool stop_at_streams);
  1642 + void shallowCopyInternal2(QPDFObjectHandle& oh, bool first_level_only);
1631 1643 void copyObject(
1632 1644 std::set<QPDFObjGen>& visited,
1633 1645 bool cross_indirect,
1634 1646 bool first_level_only,
1635 1647 bool stop_at_streams);
1636   - void shallowCopyInternal(QPDFObjectHandle& oh, bool first_level_only);
1637 1648 void disconnect();
1638 1649 void setParsedOffset(qpdf_offset_t offset);
1639 1650 void parseContentStream_internal(
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -2201,32 +2201,195 @@ QPDFObjectHandle
2201 2201 QPDFObjectHandle::shallowCopy()
2202 2202 {
2203 2203 QPDFObjectHandle result;
2204   - shallowCopyInternal(result, false);
  2204 + shallowCopyInternal1(result, false);
2205 2205 return result;
2206 2206 }
2207 2207  
  2208 +void
  2209 +QPDFObjectHandle::shallowCopyInternal1(
  2210 + QPDFObjectHandle& new_obj, bool first_level_only)
  2211 +{
  2212 + assertInitialized();
  2213 +
  2214 + if (isStream()) {
  2215 + QTC::TC("qpdf", "QPDFObjectHandle ERR shallow copy stream");
  2216 + throw std::runtime_error("attempt to make a shallow copy of a stream");
  2217 + }
  2218 + new_obj = QPDFObjectHandle(obj->copy(true));
  2219 +
  2220 + std::set<QPDFObjGen> visited;
  2221 + new_obj.copyObject1(visited, false, first_level_only, false);
  2222 +}
  2223 +
  2224 +void
  2225 +QPDFObjectHandle::copyObject1(
  2226 + std::set<QPDFObjGen>& visited,
  2227 + bool cross_indirect,
  2228 + bool first_level_only,
  2229 + bool stop_at_streams)
  2230 +{
  2231 + assertInitialized();
  2232 +
  2233 + if (isStream()) {
  2234 + if (stop_at_streams) {
  2235 + return;
  2236 + }
  2237 + throw std::runtime_error(
  2238 + "attempt to make a stream into a direct object");
  2239 + }
  2240 +
  2241 + auto cur_og = getObjGen();
  2242 + if (cur_og.getObj() != 0) {
  2243 + if (visited.count(cur_og)) {
  2244 + throw std::runtime_error(
  2245 + "loop detected while converting object from "
  2246 + "indirect to direct");
  2247 + }
  2248 + visited.insert(cur_og);
  2249 + }
  2250 +
  2251 + if (isReserved()) {
  2252 + throw std::logic_error("QPDFObjectHandle: attempting to make a"
  2253 + " reserved object handle direct");
  2254 + }
  2255 +
  2256 + std::shared_ptr<QPDFObject> new_obj;
  2257 +
  2258 + if (isBool() || isInteger() || isName() || isNull() || isReal() ||
  2259 + isString()) {
  2260 + new_obj = obj->copy(true);
  2261 + } else if (isArray()) {
  2262 + std::vector<QPDFObjectHandle> items;
  2263 + auto array = asArray();
  2264 + int n = array->getNItems();
  2265 + for (int i = 0; i < n; ++i) {
  2266 + items.push_back(array->getItem(i));
  2267 + if ((!first_level_only) &&
  2268 + (cross_indirect || (!items.back().isIndirect()))) {
  2269 + items.back().copyObject1(
  2270 + visited, cross_indirect, first_level_only, stop_at_streams);
  2271 + }
  2272 + }
  2273 + new_obj = QPDF_Array::create(items);
  2274 + } else if (isDictionary()) {
  2275 + std::map<std::string, QPDFObjectHandle> items;
  2276 + auto dict = asDictionary();
  2277 + for (auto const& key: getKeys()) {
  2278 + items[key] = dict->getKey(key);
  2279 + if ((!first_level_only) &&
  2280 + (cross_indirect || (!items[key].isIndirect()))) {
  2281 + items[key].copyObject1(
  2282 + visited, cross_indirect, first_level_only, stop_at_streams);
  2283 + }
  2284 + }
  2285 + new_obj = QPDF_Dictionary::create(items);
  2286 + } else {
  2287 + throw std::logic_error("QPDFObjectHandle::makeDirectInternal: "
  2288 + "unknown object type");
  2289 + }
  2290 +
  2291 + this->obj = new_obj;
  2292 +
  2293 + if (cur_og.getObj()) {
  2294 + visited.erase(cur_og);
  2295 + }
  2296 +}
  2297 +
2208 2298 QPDFObjectHandle
2209 2299 QPDFObjectHandle::unsafeShallowCopy()
2210 2300 {
2211 2301 QPDFObjectHandle result;
2212   - shallowCopyInternal(result, true);
  2302 + shallowCopyInternal2(result, true);
2213 2303 return result;
2214 2304 }
2215 2305  
2216 2306 void
2217   -QPDFObjectHandle::shallowCopyInternal(
  2307 +QPDFObjectHandle::shallowCopyInternal2(
2218 2308 QPDFObjectHandle& new_obj, bool first_level_only)
2219 2309 {
2220 2310 assertInitialized();
2221 2311  
2222 2312 if (isStream()) {
2223   - QTC::TC("qpdf", "QPDFObjectHandle ERR shallow copy stream");
2224 2313 throw std::runtime_error("attempt to make a shallow copy of a stream");
2225 2314 }
2226 2315 new_obj = QPDFObjectHandle(obj->copy(true));
2227 2316  
2228 2317 std::set<QPDFObjGen> visited;
2229   - new_obj.copyObject(visited, false, first_level_only, false);
  2318 + new_obj.copyObject2(visited, false, first_level_only, false);
  2319 +}
  2320 +
  2321 +void
  2322 +QPDFObjectHandle::copyObject2(
  2323 + std::set<QPDFObjGen>& visited,
  2324 + bool cross_indirect,
  2325 + bool first_level_only,
  2326 + bool stop_at_streams)
  2327 +{
  2328 + assertInitialized();
  2329 +
  2330 + if (isStream()) {
  2331 + if (stop_at_streams) {
  2332 + return;
  2333 + }
  2334 + throw std::runtime_error(
  2335 + "attempt to make a stream into a direct object");
  2336 + }
  2337 +
  2338 + auto cur_og = getObjGen();
  2339 + if (cur_og.getObj() != 0) {
  2340 + if (visited.count(cur_og)) {
  2341 + throw std::runtime_error(
  2342 + "loop detected while converting object from "
  2343 + "indirect to direct");
  2344 + }
  2345 + visited.insert(cur_og);
  2346 + }
  2347 +
  2348 + if (isReserved()) {
  2349 + throw std::logic_error("QPDFObjectHandle: attempting to make a"
  2350 + " reserved object handle direct");
  2351 + }
  2352 +
  2353 + std::shared_ptr<QPDFObject> new_obj;
  2354 +
  2355 + if (isBool() || isInteger() || isName() || isNull() || isReal() ||
  2356 + isString()) {
  2357 + new_obj = obj->copy(true);
  2358 + } else if (isArray()) {
  2359 + std::vector<QPDFObjectHandle> items;
  2360 + auto array = asArray();
  2361 + int n = array->getNItems();
  2362 + for (int i = 0; i < n; ++i) {
  2363 + items.push_back(array->getItem(i));
  2364 + if ((!first_level_only) &&
  2365 + (cross_indirect || (!items.back().isIndirect()))) {
  2366 + items.back().copyObject2(
  2367 + visited, cross_indirect, first_level_only, stop_at_streams);
  2368 + }
  2369 + }
  2370 + new_obj = QPDF_Array::create(items);
  2371 + } else if (isDictionary()) {
  2372 + std::map<std::string, QPDFObjectHandle> items;
  2373 + auto dict = asDictionary();
  2374 + for (auto const& key: getKeys()) {
  2375 + items[key] = dict->getKey(key);
  2376 + if ((!first_level_only) &&
  2377 + (cross_indirect || (!items[key].isIndirect()))) {
  2378 + items[key].copyObject2(
  2379 + visited, cross_indirect, first_level_only, stop_at_streams);
  2380 + }
  2381 + }
  2382 + new_obj = QPDF_Dictionary::create(items);
  2383 + } else {
  2384 + throw std::logic_error("QPDFObjectHandle::makeDirectInternal: "
  2385 + "unknown object type");
  2386 + }
  2387 +
  2388 + this->obj = new_obj;
  2389 +
  2390 + if (cur_og.getObj()) {
  2391 + visited.erase(cur_og);
  2392 + }
2230 2393 }
2231 2394  
2232 2395 void
... ...