Commit abb53ac36913fe56c404bf1748a230300c9a5c4a
Committed by
Jay Berkenbilt
1 parent
7770a1b0
Limited inheritance to the attributes explicitly listed in the PDF spec
Previous versions of qpdf incorrectly passed arbitrary objects from /Pages objects down to individual pages in direct contradition with the PDF specification. These are now left in /Pages. When intermediate /Pages nodes are being discarded as when the /Pages tree is being flattened, a warning is issued when unknown keys are encountered.
Showing
4 changed files
with
34 additions
and
12 deletions
include/qpdf/QPDF.hh
| ... | ... | @@ -908,11 +908,12 @@ class QPDF |
| 908 | 908 | |
| 909 | 909 | // Methods to support optimization |
| 910 | 910 | |
| 911 | - void pushInheritedAttributesToPage(bool allow_changes); | |
| 911 | + void pushInheritedAttributesToPage(bool allow_changes, | |
| 912 | + bool warn_skipped_keys); | |
| 912 | 913 | void pushInheritedAttributesToPageInternal( |
| 913 | 914 | QPDFObjectHandle, |
| 914 | 915 | std::map<std::string, std::vector<QPDFObjectHandle> >&, |
| 915 | - bool allow_changes); | |
| 916 | + bool allow_changes, bool warn_skipped_keys); | |
| 916 | 917 | void updateObjectMaps(ObjUser const& ou, QPDFObjectHandle oh); |
| 917 | 918 | void updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh, |
| 918 | 919 | std::set<ObjGen>& visited, bool top); | ... | ... |
libqpdf/QPDF_optimization.cc
| ... | ... | @@ -167,7 +167,7 @@ QPDF::optimize(std::map<int, int> const& object_stream_data, |
| 167 | 167 | |
| 168 | 168 | // Traverse pages tree pushing all inherited resources down to the |
| 169 | 169 | // page level. |
| 170 | - pushInheritedAttributesToPage(allow_changes); | |
| 170 | + pushInheritedAttributesToPage(allow_changes, false); | |
| 171 | 171 | getAllPages(); |
| 172 | 172 | |
| 173 | 173 | // Traverse pages |
| ... | ... | @@ -224,11 +224,11 @@ void |
| 224 | 224 | QPDF::pushInheritedAttributesToPage() |
| 225 | 225 | { |
| 226 | 226 | // Public API should not have access to allow_changes. |
| 227 | - pushInheritedAttributesToPage(true); | |
| 227 | + pushInheritedAttributesToPage(true, false); | |
| 228 | 228 | } |
| 229 | 229 | |
| 230 | 230 | void |
| 231 | -QPDF::pushInheritedAttributesToPage(bool allow_changes) | |
| 231 | +QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys) | |
| 232 | 232 | { |
| 233 | 233 | // Traverse pages tree pushing all inherited resources down to the |
| 234 | 234 | // page level. |
| ... | ... | @@ -238,7 +238,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes) |
| 238 | 238 | std::map<std::string, std::vector<QPDFObjectHandle> > key_ancestors; |
| 239 | 239 | pushInheritedAttributesToPageInternal( |
| 240 | 240 | this->trailer.getKey("/Root").getKey("/Pages"), |
| 241 | - key_ancestors, allow_changes); | |
| 241 | + key_ancestors, allow_changes, warn_skipped_keys); | |
| 242 | 242 | assert(key_ancestors.empty()); |
| 243 | 243 | } |
| 244 | 244 | |
| ... | ... | @@ -246,7 +246,7 @@ void |
| 246 | 246 | QPDF::pushInheritedAttributesToPageInternal( |
| 247 | 247 | QPDFObjectHandle cur_pages, |
| 248 | 248 | std::map<std::string, std::vector<QPDFObjectHandle> >& key_ancestors, |
| 249 | - bool allow_changes) | |
| 249 | + bool allow_changes, bool warn_skipped_keys) | |
| 250 | 250 | { |
| 251 | 251 | // Extract the underlying dictionary object |
| 252 | 252 | std::string type = cur_pages.getKey("/Type").getName(); |
| ... | ... | @@ -264,8 +264,8 @@ QPDF::pushInheritedAttributesToPageInternal( |
| 264 | 264 | iter != keys.end(); ++iter) |
| 265 | 265 | { |
| 266 | 266 | std::string const& key = *iter; |
| 267 | - if (! ((key == "/Type") || (key == "/Parent") || | |
| 268 | - (key == "/Kids") || (key == "/Count"))) | |
| 267 | + if ( (key == "/MediaBox") || (key == "/CropBox") || | |
| 268 | + (key == "/Resources") || (key == "/Rotate") ) | |
| 269 | 269 | { |
| 270 | 270 | if (! allow_changes) |
| 271 | 271 | { |
| ... | ... | @@ -273,7 +273,7 @@ QPDF::pushInheritedAttributesToPageInternal( |
| 273 | 273 | this->last_object_description, |
| 274 | 274 | this->file->getLastOffset(), |
| 275 | 275 | "optimize detected an " |
| 276 | - "inheritable resource when called " | |
| 276 | + "inheritable attribute when called " | |
| 277 | 277 | "in no-change mode"); |
| 278 | 278 | } |
| 279 | 279 | |
| ... | ... | @@ -309,6 +309,25 @@ QPDF::pushInheritedAttributesToPageInternal( |
| 309 | 309 | // reattached at the page level. |
| 310 | 310 | cur_pages.removeKey(key); |
| 311 | 311 | } |
| 312 | + else if (! ((key == "/Type") || (key == "/Parent") || | |
| 313 | + (key == "/Kids") || (key == "/Count"))) | |
| 314 | + { | |
| 315 | + // Warn when flattening, but not if the key is at the top | |
| 316 | + // level (i.e. "/Parent" not set), as we don't change these; | |
| 317 | + // but flattening removes intermediate /Pages nodes. | |
| 318 | + if ( (warn_skipped_keys) && (cur_pages.hasKey("/Parent")) ) | |
| 319 | + { | |
| 320 | + QTC::TC("qpdf", "QPDF unknown key not inherited"); | |
| 321 | + setLastObjectDescription("Pages object", | |
| 322 | + cur_pages.getObjectID(), | |
| 323 | + cur_pages.getGeneration()); | |
| 324 | + warn(QPDFExc(qpdf_e_pages, this->file->getName(), | |
| 325 | + this->last_object_description, 0, | |
| 326 | + "Unknown key " + key + " in /Pages object" | |
| 327 | + " is being discarded as a result of" | |
| 328 | + " flattening the /Pages tree")); | |
| 329 | + } | |
| 330 | + } | |
| 312 | 331 | } |
| 313 | 332 | |
| 314 | 333 | // Visit descendant nodes. |
| ... | ... | @@ -317,7 +336,8 @@ QPDF::pushInheritedAttributesToPageInternal( |
| 317 | 336 | for (int i = 0; i < n; ++i) |
| 318 | 337 | { |
| 319 | 338 | pushInheritedAttributesToPageInternal( |
| 320 | - kids.getArrayItem(i), key_ancestors, allow_changes); | |
| 339 | + kids.getArrayItem(i), key_ancestors, | |
| 340 | + allow_changes, warn_skipped_keys); | |
| 321 | 341 | } |
| 322 | 342 | |
| 323 | 343 | // For each inheritable key, pop the stack. If the stack | ... | ... |
libqpdf/QPDF_pages.cc
| ... | ... | @@ -102,7 +102,7 @@ QPDF::flattenPagesTree() |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | 104 | // Push inherited objects down to the /Page level |
| 105 | - pushInheritedAttributesToPage(); | |
| 105 | + pushInheritedAttributesToPage(true, true); | |
| 106 | 106 | getAllPages(); |
| 107 | 107 | |
| 108 | 108 | QPDFObjectHandle pages = getRoot().getKey("/Pages"); | ... | ... |