Commit 293a2e52b3fbd6dac2c89dfb35a546cdc027eb1b
1 parent
3cfd6546
Disregard appearance state when irrelevant (fixes #949)
If /AP is a dictionary of streams rather than a dictionary of dictionaries, disregard /AS, which is supposed to point to a subkey of one of the dictionaries. This fix prevents qpdf's annotation flattening from discarding some annotations when /AS is erroneously set.
Showing
8 changed files
with
35 additions
and
11 deletions
ChangeLog
| @@ -5,6 +5,15 @@ | @@ -5,6 +5,15 @@ | ||
| 5 | reserved object fits better in the QPDF API. The old call just | 5 | reserved object fits better in the QPDF API. The old call just |
| 6 | delegates to the new one. | 6 | delegates to the new one. |
| 7 | 7 | ||
| 8 | +2023-05-13 Jay Berkenbilt <ejb@ql.org> | ||
| 9 | + | ||
| 10 | + * When an annotation dictionary's appearance dictionary (`/AP`) | ||
| 11 | + has a key that is a stream, disregard `/AS` (which is supposed to | ||
| 12 | + point to a subkey). This enables qpdf to not ignore annotations | ||
| 13 | + that have incorrect values for `/AS` when the appearance stream is | ||
| 14 | + directly in the `/AP` dictionary instead of in a subkey. Fixes | ||
| 15 | + #949. | ||
| 16 | + | ||
| 8 | 2023-04-02 Jay Berkenbilt <ejb@ql.org> | 17 | 2023-04-02 Jay Berkenbilt <ejb@ql.org> |
| 9 | * Allow QPDFJob's workflow to be split into a reading phase and a | 18 | * Allow QPDFJob's workflow to be split into a reading phase and a |
| 10 | writing phase to allow the caller to operate on the QPDF object | 19 | writing phase to allow the caller to operate on the QPDF object |
libqpdf/QPDFAnnotationObjectHelper.cc
| @@ -54,7 +54,14 @@ QPDFAnnotationObjectHelper::getAppearanceStream( | @@ -54,7 +54,14 @@ QPDFAnnotationObjectHelper::getAppearanceStream( | ||
| 54 | std::string desired_state = state.empty() ? getAppearanceState() : state; | 54 | std::string desired_state = state.empty() ? getAppearanceState() : state; |
| 55 | if (ap.isDictionary()) { | 55 | if (ap.isDictionary()) { |
| 56 | QPDFObjectHandle ap_sub = ap.getKey(which); | 56 | QPDFObjectHandle ap_sub = ap.getKey(which); |
| 57 | - if (ap_sub.isStream() && desired_state.empty()) { | 57 | + if (ap_sub.isStream()) { |
| 58 | + // According to the spec, Appearance State is supposed to | ||
| 59 | + // refer to a subkey of the appearance stream when /AP is | ||
| 60 | + // a dictionary, but files have been seen in the wild | ||
| 61 | + // where Appearance State is `/N` and `/AP` is a stream. | ||
| 62 | + // Therefore, if `which` points to a stream, disregard | ||
| 63 | + // state and just use the stream. See qpdf issue #949 for | ||
| 64 | + // details. | ||
| 58 | QTC::TC("qpdf", "QPDFAnnotationObjectHelper AP stream"); | 65 | QTC::TC("qpdf", "QPDFAnnotationObjectHelper AP stream"); |
| 59 | return ap_sub; | 66 | return ap_sub; |
| 60 | } | 67 | } |
manual/release-notes.rst
| @@ -26,6 +26,14 @@ For a detailed list of changes, please see the file | @@ -26,6 +26,14 @@ For a detailed list of changes, please see the file | ||
| 26 | - Add ``QPDF::newReserved`` as a better alternative to | 26 | - Add ``QPDF::newReserved`` as a better alternative to |
| 27 | ``QPDFObjectHandle::newReserved``. | 27 | ``QPDFObjectHandle::newReserved``. |
| 28 | 28 | ||
| 29 | + - Bug fixes | ||
| 30 | + | ||
| 31 | + - Ignore an annotation's appearance state when the annotation only | ||
| 32 | + has one appearance. This prevents qpdf's annotation flattening | ||
| 33 | + logic from throwing away appearances of annotations whose | ||
| 34 | + annotation state is set incorrectly, as has been seen in some | ||
| 35 | + PDF files. | ||
| 36 | + | ||
| 29 | 11.3.0: February 25, 2023 | 37 | 11.3.0: February 25, 2023 |
| 30 | - CLI Enhancements | 38 | - CLI Enhancements |
| 31 | 39 |
qpdf/qtest/qpdf/form-form-bad-fields-array.out
| @@ -178,7 +178,7 @@ Page: 11 0 R | @@ -178,7 +178,7 @@ Page: 11 0 R | ||
| 178 | Subtype: /Widget | 178 | Subtype: /Widget |
| 179 | Rect: [123.4, 692.1, 260.9, 706.7] | 179 | Rect: [123.4, 692.1, 260.9, 706.7] |
| 180 | Appearance stream (/N): 14 0 R | 180 | Appearance stream (/N): 14 0 R |
| 181 | - Appearance stream (/N, /3): null | 181 | + Appearance stream (/N, /3): 14 0 R |
| 182 | Annotation: 16 0 R | 182 | Annotation: 16 0 R |
| 183 | Field: 16 0 R | 183 | Field: 16 0 R |
| 184 | Subtype: /Widget | 184 | Subtype: /Widget |
| @@ -249,5 +249,5 @@ Page: 35 0 R | @@ -249,5 +249,5 @@ Page: 35 0 R | ||
| 249 | Subtype: /Widget | 249 | Subtype: /Widget |
| 250 | Rect: [113.6, 378.5, 351.3, 396.3] | 250 | Rect: [113.6, 378.5, 351.3, 396.3] |
| 251 | Appearance stream (/N): 36 0 R | 251 | Appearance stream (/N): 36 0 R |
| 252 | - Appearance stream (/N, /3): null | 252 | + Appearance stream (/N, /3): 36 0 R |
| 253 | test 43 done | 253 | test 43 done |
qpdf/qtest/qpdf/form-form-document-defaults.out
| @@ -166,7 +166,7 @@ Page: 11 0 R | @@ -166,7 +166,7 @@ Page: 11 0 R | ||
| 166 | Subtype: /Widget | 166 | Subtype: /Widget |
| 167 | Rect: [123.4, 692.1, 260.9, 706.7] | 167 | Rect: [123.4, 692.1, 260.9, 706.7] |
| 168 | Appearance stream (/N): 14 0 R | 168 | Appearance stream (/N): 14 0 R |
| 169 | - Appearance stream (/N, /3): null | 169 | + Appearance stream (/N, /3): 14 0 R |
| 170 | Annotation: 16 0 R | 170 | Annotation: 16 0 R |
| 171 | Field: 16 0 R | 171 | Field: 16 0 R |
| 172 | Subtype: /Widget | 172 | Subtype: /Widget |
| @@ -237,5 +237,5 @@ Page: 35 0 R | @@ -237,5 +237,5 @@ Page: 35 0 R | ||
| 237 | Subtype: /Widget | 237 | Subtype: /Widget |
| 238 | Rect: [113.6, 378.5, 351.3, 396.3] | 238 | Rect: [113.6, 378.5, 351.3, 396.3] |
| 239 | Appearance stream (/N): 36 0 R | 239 | Appearance stream (/N): 36 0 R |
| 240 | - Appearance stream (/N, /3): null | 240 | + Appearance stream (/N, /3): 36 0 R |
| 241 | test 43 done | 241 | test 43 done |
qpdf/qtest/qpdf/form-form-empty-from-odt.out
| @@ -166,7 +166,7 @@ Page: 11 0 R | @@ -166,7 +166,7 @@ Page: 11 0 R | ||
| 166 | Subtype: /Widget | 166 | Subtype: /Widget |
| 167 | Rect: [123.4, 692.1, 260.9, 706.7] | 167 | Rect: [123.4, 692.1, 260.9, 706.7] |
| 168 | Appearance stream (/N): 14 0 R | 168 | Appearance stream (/N): 14 0 R |
| 169 | - Appearance stream (/N, /3): null | 169 | + Appearance stream (/N, /3): 14 0 R |
| 170 | Annotation: 16 0 R | 170 | Annotation: 16 0 R |
| 171 | Field: 16 0 R | 171 | Field: 16 0 R |
| 172 | Subtype: /Widget | 172 | Subtype: /Widget |
| @@ -237,5 +237,5 @@ Page: 35 0 R | @@ -237,5 +237,5 @@ Page: 35 0 R | ||
| 237 | Subtype: /Widget | 237 | Subtype: /Widget |
| 238 | Rect: [113.6, 378.5, 351.3, 396.3] | 238 | Rect: [113.6, 378.5, 351.3, 396.3] |
| 239 | Appearance stream (/N): 36 0 R | 239 | Appearance stream (/N): 36 0 R |
| 240 | - Appearance stream (/N, /3): null | 240 | + Appearance stream (/N, /3): 36 0 R |
| 241 | test 43 done | 241 | test 43 done |
qpdf/qtest/qpdf/form-form-errors.out
| @@ -171,7 +171,7 @@ Page: 11 0 R | @@ -171,7 +171,7 @@ Page: 11 0 R | ||
| 171 | Subtype: /Widget | 171 | Subtype: /Widget |
| 172 | Rect: [123.4, 692.1, 260.9, 706.7] | 172 | Rect: [123.4, 692.1, 260.9, 706.7] |
| 173 | Appearance stream (/N): 14 0 R | 173 | Appearance stream (/N): 14 0 R |
| 174 | - Appearance stream (/N, /3): null | 174 | + Appearance stream (/N, /3): 14 0 R |
| 175 | Annotation: 16 0 R | 175 | Annotation: 16 0 R |
| 176 | Field: 16 0 R | 176 | Field: 16 0 R |
| 177 | Subtype: /Widget | 177 | Subtype: /Widget |
| @@ -242,5 +242,5 @@ Page: 35 0 R | @@ -242,5 +242,5 @@ Page: 35 0 R | ||
| 242 | Subtype: /Widget | 242 | Subtype: /Widget |
| 243 | Rect: [113.6, 378.5, 351.3, 396.3] | 243 | Rect: [113.6, 378.5, 351.3, 396.3] |
| 244 | Appearance stream (/N): 36 0 R | 244 | Appearance stream (/N): 36 0 R |
| 245 | - Appearance stream (/N, /3): null | 245 | + Appearance stream (/N, /3): 36 0 R |
| 246 | test 43 done | 246 | test 43 done |
qpdf/qtest/qpdf/form-form-mod1.out
| @@ -166,7 +166,7 @@ Page: 11 0 R | @@ -166,7 +166,7 @@ Page: 11 0 R | ||
| 166 | Subtype: /Widget | 166 | Subtype: /Widget |
| 167 | Rect: [123.4, 692.1, 260.9, 706.7] | 167 | Rect: [123.4, 692.1, 260.9, 706.7] |
| 168 | Appearance stream (/N): 14 0 R | 168 | Appearance stream (/N): 14 0 R |
| 169 | - Appearance stream (/N, /3): null | 169 | + Appearance stream (/N, /3): 14 0 R |
| 170 | Annotation: 16 0 R | 170 | Annotation: 16 0 R |
| 171 | Field: 16 0 R | 171 | Field: 16 0 R |
| 172 | Subtype: /Widget | 172 | Subtype: /Widget |
| @@ -237,5 +237,5 @@ Page: 35 0 R | @@ -237,5 +237,5 @@ Page: 35 0 R | ||
| 237 | Subtype: /Widget | 237 | Subtype: /Widget |
| 238 | Rect: [113.6, 378.5, 351.3, 396.3] | 238 | Rect: [113.6, 378.5, 351.3, 396.3] |
| 239 | Appearance stream (/N): 36 0 R | 239 | Appearance stream (/N): 36 0 R |
| 240 | - Appearance stream (/N, /3): null | 240 | + Appearance stream (/N, /3): 36 0 R |
| 241 | test 43 done | 241 | test 43 done |