Commit e7a85545639d6d09923abb8fb300dda5889b110b
1 parent
1562d34c
QPDFPageObjectHelper::getPageImages: support form XObjects
Showing
4 changed files
with
73 additions
and
60 deletions
include/qpdf/QPDFPageObjectHelper.hh
| @@ -32,6 +32,10 @@ | @@ -32,6 +32,10 @@ | ||
| 32 | 32 | ||
| 33 | class QPDFPageObjectHelper: public QPDFObjectHelper | 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 | public: | 39 | public: |
| 36 | QPDF_DLL | 40 | QPDF_DLL |
| 37 | QPDFPageObjectHelper(QPDFObjectHandle); | 41 | QPDFPageObjectHelper(QPDFObjectHandle); |
| @@ -40,14 +44,15 @@ class QPDFPageObjectHelper: public QPDFObjectHelper | @@ -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 | // inheritable, look up through the page's ancestors in the page | 50 | // inheritable, look up through the page's ancestors in the page |
| 46 | // tree. If copy_if_shared is true, then this method will replace | 51 | // tree. If copy_if_shared is true, then this method will replace |
| 47 | // the attribute with a shallow copy if it is in indirect or | 52 | // the attribute with a shallow copy if it is in indirect or |
| 48 | // inherited and return the copy. You should do this if you are | 53 | // inherited and return the copy. You should do this if you are |
| 49 | // going to modify the returned object and want the modifications | 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 | QPDF_DLL | 56 | QPDF_DLL |
| 52 | QPDFObjectHandle | 57 | QPDFObjectHandle |
| 53 | getAttribute(std::string const& name, bool copy_if_shared); | 58 | getAttribute(std::string const& name, bool copy_if_shared); |
| @@ -70,7 +75,8 @@ class QPDFPageObjectHelper: public QPDFObjectHelper | @@ -70,7 +75,8 @@ class QPDFPageObjectHelper: public QPDFObjectHelper | ||
| 70 | // Returns an empty map if there are no images or no resources. | 75 | // Returns an empty map if there are no images or no resources. |
| 71 | // Prior to qpdf 8.4.0, this function did not support inherited | 76 | // Prior to qpdf 8.4.0, this function did not support inherited |
| 72 | // resources, but it does now. Return value is a map from XObject | 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 | QPDF_DLL | 80 | QPDF_DLL |
| 75 | std::map<std::string, QPDFObjectHandle> getPageImages(); | 81 | std::map<std::string, QPDFObjectHandle> getPageImages(); |
| 76 | 82 |
libqpdf/QPDFObjectHandle.cc
| @@ -1320,35 +1320,7 @@ QPDFObjectHandle::getGeneration() const | @@ -1320,35 +1320,7 @@ QPDFObjectHandle::getGeneration() const | ||
| 1320 | std::map<std::string, QPDFObjectHandle> | 1320 | std::map<std::string, QPDFObjectHandle> |
| 1321 | QPDFObjectHandle::getPageImages() | 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 | std::vector<QPDFObjectHandle> | 1326 | std::vector<QPDFObjectHandle> |
libqpdf/QPDFPageObjectHelper.cc
| @@ -314,33 +314,46 @@ QPDFObjectHandle | @@ -314,33 +314,46 @@ QPDFObjectHandle | ||
| 314 | QPDFPageObjectHelper::getAttribute(std::string const& name, | 314 | QPDFPageObjectHelper::getAttribute(std::string const& name, |
| 315 | bool copy_if_shared) | 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 | bool inherited = false; | 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 | result = node.getKey(name); | 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 | if (copy_if_shared && (inherited || result.isIndirect())) | 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 | result = result.shallowCopy(); | 355 | result = result.shallowCopy(); |
| 343 | - this->oh.replaceKey(name, result); | 356 | + dict.replaceKey(name, result); |
| 344 | } | 357 | } |
| 345 | return result; | 358 | return result; |
| 346 | } | 359 | } |
| @@ -376,7 +389,34 @@ QPDFPageObjectHelper::getMediaBox(bool copy_if_shared) | @@ -376,7 +389,34 @@ QPDFPageObjectHelper::getMediaBox(bool copy_if_shared) | ||
| 376 | std::map<std::string, QPDFObjectHandle> | 389 | std::map<std::string, QPDFObjectHandle> |
| 377 | QPDFPageObjectHelper::getPageImages() | 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 | void | 422 | void |
| @@ -571,13 +611,8 @@ QPDFPageObjectHelper::removeUnreferencedResourcesHelper( | @@ -571,13 +611,8 @@ QPDFPageObjectHelper::removeUnreferencedResourcesHelper( | ||
| 571 | removeUnreferencedResourcesHelper( | 611 | removeUnreferencedResourcesHelper( |
| 572 | resource.getDict(), seen, | 612 | resource.getDict(), seen, |
| 573 | [&resource]() { | 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 | [&resource](QPDFObjectHandle::TokenFilter* f) { | 617 | [&resource](QPDFObjectHandle::TokenFilter* f) { |
| 583 | resource.filterAsContents(f); | 618 | resource.filterAsContents(f); |
qpdf/qpdf.testcov
| @@ -422,7 +422,7 @@ QPDFFormFieldObjectHelper create AP from scratch 0 | @@ -422,7 +422,7 @@ QPDFFormFieldObjectHelper create AP from scratch 0 | ||
| 422 | QPDFFormFieldObjectHelper replaced BMC at EOF 0 | 422 | QPDFFormFieldObjectHelper replaced BMC at EOF 0 |
| 423 | QPDFFormFieldObjectHelper fallback Tf 0 | 423 | QPDFFormFieldObjectHelper fallback Tf 0 |
| 424 | QPDFPageObjectHelper non-trivial inheritance 0 | 424 | QPDFPageObjectHelper non-trivial inheritance 0 |
| 425 | -QPDFPageObjectHelper copy shared attribute 0 | 425 | +QPDFPageObjectHelper copy shared attribute 1 |
| 426 | qpdf from_nr from repeat_nr 0 | 426 | qpdf from_nr from repeat_nr 0 |
| 427 | QPDF resolve duplicated page object 0 | 427 | QPDF resolve duplicated page object 0 |
| 428 | QPDF handle direct page object 0 | 428 | QPDF handle direct page object 0 |