Commit e7a85545639d6d09923abb8fb300dda5889b110b

Authored by Jay Berkenbilt
1 parent 1562d34c

QPDFPageObjectHelper::getPageImages: support form XObjects

include/qpdf/QPDFPageObjectHelper.hh
... ... @@ -32,6 +32,10 @@
32 32  
33 33 class QPDFPageObjectHelper: public QPDFObjectHelper
34 34 {
  35 + // This is a helper class for page objects, but as of qpdf 10.1,
  36 + // many of the methods also work for form XObjects. When this is
  37 + // the case, it is noted in the comment.
  38 +
35 39 public:
36 40 QPDF_DLL
37 41 QPDFPageObjectHelper(QPDFObjectHandle);
... ... @@ -40,14 +44,15 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
40 44 {
41 45 }
42 46  
43   - // Return the effective value of this attribute for the page. If
44   - // the requested attribute is not present on the page but is
  47 + // Works with pages and form XObjects. Return the effective value
  48 + // of this attribute for the page/form XObject. For pages, if the
  49 + // requested attribute is not present on the page but is
45 50 // inheritable, look up through the page's ancestors in the page
46 51 // tree. If copy_if_shared is true, then this method will replace
47 52 // the attribute with a shallow copy if it is in indirect or
48 53 // inherited and return the copy. You should do this if you are
49 54 // going to modify the returned object and want the modifications
50   - // to apply to the current page only.
  55 + // to apply to the current page/form XObject only.
51 56 QPDF_DLL
52 57 QPDFObjectHandle
53 58 getAttribute(std::string const& name, bool copy_if_shared);
... ... @@ -70,7 +75,8 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
70 75 // Returns an empty map if there are no images or no resources.
71 76 // Prior to qpdf 8.4.0, this function did not support inherited
72 77 // resources, but it does now. Return value is a map from XObject
73   - // name to the image object, which is always a stream.
  78 + // name to the image object, which is always a stream. Works with
  79 + // form XObjects as well as pages.
74 80 QPDF_DLL
75 81 std::map<std::string, QPDFObjectHandle> getPageImages();
76 82  
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -1320,35 +1320,7 @@ QPDFObjectHandle::getGeneration() const
1320 1320 std::map<std::string, QPDFObjectHandle>
1321 1321 QPDFObjectHandle::getPageImages()
1322 1322 {
1323   - std::map<std::string, QPDFObjectHandle> result;
1324   - QPDFObjectHandle resources =
1325   - QPDFPageObjectHelper(*this).getAttribute("/Resources", false);
1326   - if (resources.isDictionary())
1327   - {
1328   - if (resources.hasKey("/XObject"))
1329   - {
1330   - QPDFObjectHandle xobject = resources.getKey("/XObject");
1331   - std::set<std::string> keys = xobject.getKeys();
1332   - for (std::set<std::string>::iterator iter = keys.begin();
1333   - iter != keys.end(); ++iter)
1334   - {
1335   - std::string key = (*iter);
1336   - QPDFObjectHandle value = xobject.getKey(key);
1337   - if (value.isStream())
1338   - {
1339   - QPDFObjectHandle dict = value.getDict();
1340   - if (dict.hasKey("/Subtype") &&
1341   - (dict.getKey("/Subtype").getName() == "/Image") &&
1342   - (! dict.hasKey("/ImageMask")))
1343   - {
1344   - result[key] = value;
1345   - }
1346   - }
1347   - }
1348   - }
1349   - }
1350   -
1351   - return result;
  1323 + return QPDFPageObjectHelper(*this).getPageImages();
1352 1324 }
1353 1325  
1354 1326 std::vector<QPDFObjectHandle>
... ...
libqpdf/QPDFPageObjectHelper.cc
... ... @@ -314,33 +314,46 @@ QPDFObjectHandle
314 314 QPDFPageObjectHelper::getAttribute(std::string const& name,
315 315 bool copy_if_shared)
316 316 {
317   - bool inheritable = ((name == "/MediaBox") || (name == "/CropBox") ||
318   - (name == "/Resources") || (name == "/Rotate"));
319   -
320   - QPDFObjectHandle node = this->oh;
321   - QPDFObjectHandle result(node.getKey(name));
322   - std::set<QPDFObjGen> seen;
  317 + QPDFObjectHandle result;
  318 + QPDFObjectHandle dict;
  319 + bool is_form_xobject = this->oh.isFormXObject();
323 320 bool inherited = false;
324   - while (inheritable && result.isNull() && node.hasKey("/Parent"))
  321 + if (is_form_xobject)
325 322 {
326   - seen.insert(node.getObjGen());
327   - node = node.getKey("/Parent");
328   - if (seen.count(node.getObjGen()))
329   - {
330   - break;
331   - }
  323 + dict = this->oh.getDict();
  324 + result = dict.getKey(name);
  325 + }
  326 + else
  327 + {
  328 + dict = this->oh;
  329 + bool inheritable = ((name == "/MediaBox") || (name == "/CropBox") ||
  330 + (name == "/Resources") || (name == "/Rotate"));
  331 +
  332 + QPDFObjectHandle node = dict;
332 333 result = node.getKey(name);
333   - if (! result.isNull())
  334 + std::set<QPDFObjGen> seen;
  335 + while (inheritable && result.isNull() && node.hasKey("/Parent"))
334 336 {
335   - QTC::TC("qpdf", "QPDFPageObjectHelper non-trivial inheritance");
336   - inherited = true;
  337 + seen.insert(node.getObjGen());
  338 + node = node.getKey("/Parent");
  339 + if (seen.count(node.getObjGen()))
  340 + {
  341 + break;
  342 + }
  343 + result = node.getKey(name);
  344 + if (! result.isNull())
  345 + {
  346 + QTC::TC("qpdf", "QPDFPageObjectHelper non-trivial inheritance");
  347 + inherited = true;
  348 + }
337 349 }
338 350 }
339 351 if (copy_if_shared && (inherited || result.isIndirect()))
340 352 {
341   - QTC::TC("qpdf", "QPDFPageObjectHelper copy shared attribute");
  353 + QTC::TC("qpdf", "QPDFPageObjectHelper copy shared attribute",
  354 + is_form_xobject ? 0 : 1);
342 355 result = result.shallowCopy();
343   - this->oh.replaceKey(name, result);
  356 + dict.replaceKey(name, result);
344 357 }
345 358 return result;
346 359 }
... ... @@ -376,7 +389,34 @@ QPDFPageObjectHelper::getMediaBox(bool copy_if_shared)
376 389 std::map<std::string, QPDFObjectHandle>
377 390 QPDFPageObjectHelper::getPageImages()
378 391 {
379   - return this->oh.getPageImages();
  392 + std::map<std::string, QPDFObjectHandle> result;
  393 + QPDFObjectHandle resources = getAttribute("/Resources", false);
  394 + if (resources.isDictionary())
  395 + {
  396 + if (resources.hasKey("/XObject"))
  397 + {
  398 + QPDFObjectHandle xobject = resources.getKey("/XObject");
  399 + std::set<std::string> keys = xobject.getKeys();
  400 + for (std::set<std::string>::iterator iter = keys.begin();
  401 + iter != keys.end(); ++iter)
  402 + {
  403 + std::string key = (*iter);
  404 + QPDFObjectHandle value = xobject.getKey(key);
  405 + if (value.isStream())
  406 + {
  407 + QPDFObjectHandle dict = value.getDict();
  408 + if (dict.hasKey("/Subtype") &&
  409 + (dict.getKey("/Subtype").getName() == "/Image") &&
  410 + (! dict.hasKey("/ImageMask")))
  411 + {
  412 + result[key] = value;
  413 + }
  414 + }
  415 + }
  416 + }
  417 + }
  418 +
  419 + return result;
380 420 }
381 421  
382 422 void
... ... @@ -571,13 +611,8 @@ QPDFPageObjectHelper::removeUnreferencedResourcesHelper(
571 611 removeUnreferencedResourcesHelper(
572 612 resource.getDict(), seen,
573 613 [&resource]() {
574   - auto result = resource.getDict().getKey("/Resources");
575   - if (result.isDictionary())
576   - {
577   - result = result.shallowCopy();
578   - resource.getDict().replaceKey("/Resources", result);
579   - }
580   - return result;
  614 + return QPDFPageObjectHelper(resource)
  615 + .getAttribute("/Resources", true);
581 616 },
582 617 [&resource](QPDFObjectHandle::TokenFilter* f) {
583 618 resource.filterAsContents(f);
... ...
qpdf/qpdf.testcov
... ... @@ -422,7 +422,7 @@ QPDFFormFieldObjectHelper create AP from scratch 0
422 422 QPDFFormFieldObjectHelper replaced BMC at EOF 0
423 423 QPDFFormFieldObjectHelper fallback Tf 0
424 424 QPDFPageObjectHelper non-trivial inheritance 0
425   -QPDFPageObjectHelper copy shared attribute 0
  425 +QPDFPageObjectHelper copy shared attribute 1
426 426 qpdf from_nr from repeat_nr 0
427 427 QPDF resolve duplicated page object 0
428 428 QPDF handle direct page object 0
... ...