Commit 293a2e52b3fbd6dac2c89dfb35a546cdc027eb1b

Authored by Jay Berkenbilt
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.
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