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 | 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 | ... | ... |