Commit 7bf08a22a5c4b73922026535f81a60854b98fba5
1 parent
0d42bc0b
Improve handling of root object `/Type` entries
- Automatically repair missing or invalid `/Type` entries by setting them to `/Catalog`, unless in inspection mode. - Update related documentation and warnings to reflect this behavior.
Showing
10 changed files
with
46 additions
and
15 deletions
libqpdf/QPDF.cc
| @@ -670,11 +670,15 @@ QPDF::getRoot() | @@ -670,11 +670,15 @@ QPDF::getRoot() | ||
| 670 | if (!Root) { | 670 | if (!Root) { |
| 671 | throw m->c.damagedPDF("", -1, "unable to find /Root dictionary"); | 671 | throw m->c.damagedPDF("", -1, "unable to find /Root dictionary"); |
| 672 | } | 672 | } |
| 673 | - // Check_mode is an interim solution to request #810 pending a more comprehensive review of the | ||
| 674 | - // approach to more extensive checks and warning levels. | ||
| 675 | - if (m->cf.check_mode() && Name(Root["/Type"]) != "/Catalog") { | ||
| 676 | - warn(m->c.damagedPDF("", -1, "catalog /Type entry missing or invalid")); | ||
| 677 | - Root.replace("/Type", Name("/Catalog")); | 673 | + if (!m->objects.root_checked()) { |
| 674 | + m->objects.root_checked(true); | ||
| 675 | + if (Name(Root["/Type"]) != "/Catalog") { | ||
| 676 | + warn(m->c.damagedPDF( | ||
| 677 | + "", -1, "Catalog: setting missing or invalid /Type entry to /Catalog")); | ||
| 678 | + if (!global::Options::inspection_mode()) { | ||
| 679 | + Root.replace("/Type", Name("/Catalog")); | ||
| 680 | + } | ||
| 681 | + } | ||
| 678 | } | 682 | } |
| 679 | return Root.oh(); | 683 | return Root.oh(); |
| 680 | } | 684 | } |
libqpdf/qpdf/QPDF_private.hh
| @@ -1022,6 +1022,19 @@ class QPDF::Doc::Objects: Common | @@ -1022,6 +1022,19 @@ class QPDF::Doc::Objects: Common | ||
| 1022 | { | 1022 | { |
| 1023 | return uncompressed_after_compressed_; | 1023 | return uncompressed_after_compressed_; |
| 1024 | } | 1024 | } |
| 1025 | + | ||
| 1026 | + bool | ||
| 1027 | + root_checked() const | ||
| 1028 | + { | ||
| 1029 | + return root_checked_; | ||
| 1030 | + } | ||
| 1031 | + | ||
| 1032 | + void | ||
| 1033 | + root_checked(bool val) | ||
| 1034 | + { | ||
| 1035 | + root_checked_ = val; | ||
| 1036 | + } | ||
| 1037 | + | ||
| 1025 | void parse(char const* password); | 1038 | void parse(char const* password); |
| 1026 | std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og); | 1039 | std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og); |
| 1027 | void inParse(bool); | 1040 | void inParse(bool); |
| @@ -1106,6 +1119,7 @@ class QPDF::Doc::Objects: Common | @@ -1106,6 +1119,7 @@ class QPDF::Doc::Objects: Common | ||
| 1106 | // Linearization data | 1119 | // Linearization data |
| 1107 | qpdf_offset_t first_xref_item_offset_{0}; // actual value from file | 1120 | qpdf_offset_t first_xref_item_offset_{0}; // actual value from file |
| 1108 | bool uncompressed_after_compressed_{false}; | 1121 | bool uncompressed_after_compressed_{false}; |
| 1122 | + bool root_checked_{false}; | ||
| 1109 | }; // class QPDF::Doc::Objects | 1123 | }; // class QPDF::Doc::Objects |
| 1110 | 1124 | ||
| 1111 | // This class is used to represent a PDF Pages tree. | 1125 | // This class is used to represent a PDF Pages tree. |
manual/release-notes.rst
| 1 | .. _ticket: https://issues.qpdf.org | 1 | .. _ticket: https://issues.qpdf.org |
| 2 | .. _shared null: https://wiki.qpdf.org/PDF-null-objects-vs-qpdf-null-objects | 2 | .. _shared null: https://wiki.qpdf.org/PDF-null-objects-vs-qpdf-null-objects |
| 3 | 3 | ||
| 4 | + | ||
| 4 | .. _release-notes: | 5 | .. _release-notes: |
| 5 | 6 | ||
| 6 | Release Notes | 7 | Release Notes |
| @@ -17,7 +18,7 @@ more detail. | @@ -17,7 +18,7 @@ more detail. | ||
| 17 | - Release changes | 18 | - Release changes |
| 18 | 19 | ||
| 19 | - Starting with version 12.3.0, we use | 20 | - Starting with version 12.3.0, we use |
| 20 | - `cosign<https://docs.sigstore.dev/cosign/>__`, rather than GPG, | 21 | + `cosign <https://docs.sigstore.dev/cosign/>`__, rather than GPG, |
| 21 | to sign releases. See the top-level README.md for instructions. | 22 | to sign releases. See the top-level README.md for instructions. |
| 22 | We will continue to use GPG for the 12.x series. Starting with | 23 | We will continue to use GPG for the 12.x series. Starting with |
| 23 | qpdf version 13, only cosign will be used. | 24 | qpdf version 13, only cosign will be used. |
| @@ -122,14 +123,8 @@ more detail. | @@ -122,14 +123,8 @@ more detail. | ||
| 122 | ``damaged_pdf`` error with message "unable to find /Root dictionary" | 123 | ``damaged_pdf`` error with message "unable to find /Root dictionary" |
| 123 | rather than an internal error. | 124 | rather than an internal error. |
| 124 | 125 | ||
| 125 | -.. _r12-3-0-deprecate: | ||
| 126 | - | ||
| 127 | - - The following are believed to be not in use and have been deprecated. | ||
| 128 | - If you are relying on them please open a ticket_. | ||
| 129 | - | ||
| 130 | - - QPDF::compute_encryption_key | ||
| 131 | - - All QPDF::EncryptionData methods. These methods are not exported in the | ||
| 132 | - shared library and are only useable in statically linked programs. | 126 | + - Invalid root object `/Type` entries are now unconditionally repaired [#inspect]_. |
| 127 | + Previously they were only repaired if the :qpdf:ref:`--check` option was used. | ||
| 133 | 128 | ||
| 134 | - Setting :qpdf:ref:`--compress-streams` to ``n`` or | 129 | - Setting :qpdf:ref:`--compress-streams` to ``n`` or |
| 135 | ``QPDFWriter::setCompressStreams(false)`` no longer automatically | 130 | ``QPDFWriter::setCompressStreams(false)`` no longer automatically |
| @@ -141,6 +136,18 @@ more detail. | @@ -141,6 +136,18 @@ more detail. | ||
| 141 | registered by calling ``QPDF::registerStreamFilter``. If you are | 136 | registered by calling ``QPDF::registerStreamFilter``. If you are |
| 142 | providing your own stream filters please open a ticket_. | 137 | providing your own stream filters please open a ticket_. |
| 143 | 138 | ||
| 139 | +.. _r12-3-0-deprecate: | ||
| 140 | + | ||
| 141 | + - The following are believed to be not in use and have been deprecated. | ||
| 142 | + If you are relying on them please open a ticket_. | ||
| 143 | + | ||
| 144 | + - QPDF::compute_encryption_key | ||
| 145 | + | ||
| 146 | + - All QPDF::EncryptionData methods. These methods are not exported in the | ||
| 147 | + shared library and are only useable in statically linked programs. | ||
| 148 | + | ||
| 149 | +.. [#inspect] not in :ref:`inspection-mode` | ||
| 150 | + | ||
| 144 | 12.2.0: May 4, 2025 | 151 | 12.2.0: May 4, 2025 |
| 145 | - Upcoming C++ Version Change | 152 | - Upcoming C++ Version Change |
| 146 | 153 |
qpdf/qtest/qpdf/bad-direct-root.out
| @@ -5,6 +5,7 @@ WARNING: bad-direct-root.pdf: Attempting to reconstruct cross-reference table | @@ -5,6 +5,7 @@ WARNING: bad-direct-root.pdf: Attempting to reconstruct cross-reference table | ||
| 5 | WARNING: bad-direct-root.pdf (trailer, offset 249): unknown token while reading object; treating as null | 5 | WARNING: bad-direct-root.pdf (trailer, offset 249): unknown token while reading object; treating as null |
| 6 | WARNING: bad-direct-root.pdf (trailer, offset 261): unknown token while reading object; treating as null | 6 | WARNING: bad-direct-root.pdf (trailer, offset 261): unknown token while reading object; treating as null |
| 7 | WARNING: bad-direct-root.pdf (trailer, offset 186): expected dictionary keys but found non-name objects; ignoring | 7 | WARNING: bad-direct-root.pdf (trailer, offset 186): expected dictionary keys but found non-name objects; ignoring |
| 8 | +WARNING: bad-direct-root.pdf: Catalog: setting missing or invalid /Type entry to /Catalog | ||
| 8 | WARNING: bad-direct-root.pdf (object 1 0, offset 65): expected endobj | 9 | WARNING: bad-direct-root.pdf (object 1 0, offset 65): expected endobj |
| 9 | WARNING: bad-direct-root.pdf (object 2 0, offset 114): unknown token while reading object; treating as null | 10 | WARNING: bad-direct-root.pdf (object 2 0, offset 114): unknown token while reading object; treating as null |
| 10 | WARNING: bad-direct-root.pdf (object 2 0, offset 122): invalid character (/) in hexstring | 11 | WARNING: bad-direct-root.pdf (object 2 0, offset 122): invalid character (/) in hexstring |
qpdf/qtest/qpdf/catalgg.out
qpdf/qtest/qpdf/fuzz-16214.out
| @@ -6,6 +6,7 @@ WARNING: fuzz-16214.pdf (xref stream, offset 116): Cross-reference stream data h | @@ -6,6 +6,7 @@ WARNING: fuzz-16214.pdf (xref stream, offset 116): Cross-reference stream data h | ||
| 6 | WARNING: fuzz-16214.pdf: reported number of objects (6) is not one plus the highest object number (35) | 6 | WARNING: fuzz-16214.pdf: reported number of objects (6) is not one plus the highest object number (35) |
| 7 | WARNING: fuzz-16214.pdf (object 14 0, offset 652): expected dictionary key but found non-name object; inserting key /QPDFFake1 | 7 | WARNING: fuzz-16214.pdf (object 14 0, offset 652): expected dictionary key but found non-name object; inserting key /QPDFFake1 |
| 8 | WARNING: fuzz-16214.pdf (object 14 0, offset 734): expected endobj | 8 | WARNING: fuzz-16214.pdf (object 14 0, offset 734): expected endobj |
| 9 | +WARNING: fuzz-16214.pdf: Catalog: setting missing or invalid /Type entry to /Catalog | ||
| 9 | WARNING: fuzz-16214.pdf: file is damaged | 10 | WARNING: fuzz-16214.pdf: file is damaged |
| 10 | WARNING: fuzz-16214.pdf (object 1 0, offset 7189): expected n n obj | 11 | WARNING: fuzz-16214.pdf (object 1 0, offset 7189): expected n n obj |
| 11 | WARNING: fuzz-16214.pdf: Attempting to reconstruct cross-reference table | 12 | WARNING: fuzz-16214.pdf: Attempting to reconstruct cross-reference table |
qpdf/qtest/qpdf/issue-119.out
qpdf/qtest/qpdf/issue-120.out
| 1 | WARNING: issue-120.pdf (xref stream, offset 712): self-referential object stream 3 | 1 | WARNING: issue-120.pdf (xref stream, offset 712): self-referential object stream 3 |
| 2 | +WARNING: issue-120.pdf: Catalog: setting missing or invalid /Type entry to /Catalog | ||
| 2 | qpdf: issue-120.pdf: unable to find page tree | 3 | qpdf: issue-120.pdf: unable to find page tree |
qpdf/qtest/qpdf/issue-143.out
| @@ -19,4 +19,5 @@ WARNING: issue-143.pdf object stream 1 (object 0 0, offset 11): object id is inv | @@ -19,4 +19,5 @@ WARNING: issue-143.pdf object stream 1 (object 0 0, offset 11): object id is inv | ||
| 19 | WARNING: issue-143.pdf object stream 1 (object 6 0, offset 21): offset 0 is invalid (must be larger than previous offset 0) | 19 | WARNING: issue-143.pdf object stream 1 (object 6 0, offset 21): offset 0 is invalid (must be larger than previous offset 0) |
| 20 | WARNING: issue-143.pdf object stream 1 (object 0 0, offset 23): object id is invalid | 20 | WARNING: issue-143.pdf object stream 1 (object 0 0, offset 23): object id is invalid |
| 21 | WARNING: issue-143.pdf object stream 1 (object 2 0, offset 33): expected dictionary key but found non-name object; inserting key /QPDFFake1 | 21 | WARNING: issue-143.pdf object stream 1 (object 2 0, offset 33): expected dictionary key but found non-name object; inserting key /QPDFFake1 |
| 22 | +WARNING: issue-143.pdf: Catalog: setting missing or invalid /Type entry to /Catalog | ||
| 22 | qpdf: issue-143.pdf: unable to find page tree | 23 | qpdf: issue-143.pdf: unable to find page tree |
qpdf/qtest/qpdf/issue-51.out
| @@ -3,4 +3,5 @@ WARNING: issue-51.pdf: reported number of objects (0) is not one plus the highes | @@ -3,4 +3,5 @@ WARNING: issue-51.pdf: reported number of objects (0) is not one plus the highes | ||
| 3 | WARNING: issue-51.pdf (object 7 0, offset 500): treating bad indirect reference (0 0 R) as null | 3 | WARNING: issue-51.pdf (object 7 0, offset 500): treating bad indirect reference (0 0 R) as null |
| 4 | WARNING: issue-51.pdf (object 7 0, offset 476): dictionary has duplicated key /0000; last occurrence overrides earlier ones | 4 | WARNING: issue-51.pdf (object 7 0, offset 476): dictionary has duplicated key /0000; last occurrence overrides earlier ones |
| 5 | WARNING: issue-51.pdf (object 7 0, offset 553): expected endobj | 5 | WARNING: issue-51.pdf (object 7 0, offset 553): expected endobj |
| 6 | +WARNING: issue-51.pdf: Catalog: setting missing or invalid /Type entry to /Catalog | ||
| 6 | issue-51.pdf: unable to find page tree | 7 | issue-51.pdf: unable to find page tree |