Commit 8aab68386a860433fb681f8cf69b5511696fcf15
1 parent
cf0e3422
Fix appearance stream handling in `QPDFFormFieldObjectHelper::generateTextAppear…
…ance`: copy stream data if shared references exceed threshold, ensuring safe updates.
Showing
2 changed files
with
14 additions
and
11 deletions
libqpdf/QPDFFormFieldObjectHelper.cc
| @@ -915,19 +915,26 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) | @@ -915,19 +915,26 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) | ||
| 915 | } | 915 | } |
| 916 | 916 | ||
| 917 | if (AS.obj_sp().use_count() > 3) { | 917 | if (AS.obj_sp().use_count() > 3) { |
| 918 | - // The following check ensures that we only update the appearance stream if it is not | ||
| 919 | - // shared. The threshold of 3 is based on the current implementation details: | 918 | + // Ensures that the appearance stream is not shared by copying it if the threshold of 3 is |
| 919 | + // exceeded. The threshold is based on the current implementation details: | ||
| 920 | // - One reference from the local variable AS | 920 | // - One reference from the local variable AS |
| 921 | // - One reference from the appearance dictionary (/AP) | 921 | // - One reference from the appearance dictionary (/AP) |
| 922 | // - One reference from the object table | 922 | // - One reference from the object table |
| 923 | // If use_count() is greater than 3, it means the appearance stream is shared elsewhere, | 923 | // If use_count() is greater than 3, it means the appearance stream is shared elsewhere, |
| 924 | // and updating it could have unintended side effects. This threshold may need to be updated | 924 | // and updating it could have unintended side effects. This threshold may need to be updated |
| 925 | // if the internal reference counting changes in the future. | 925 | // if the internal reference counting changes in the future. |
| 926 | - // The long-term solution will we to replace appearance streams at the point of flattening | ||
| 927 | - // annotations rather than attaching token filters that modify the streams at time of | ||
| 928 | - // writing. | ||
| 929 | - aoh.warn("unable to generate text appearance from shared appearance stream for update"); | ||
| 930 | - return; | 926 | + // |
| 927 | + // There is currently no explicit CI test for this code> I has been manually tested bu | ||
| 928 | + // running it through CI with a threshold of 0, unconditionally copying streams. | ||
| 929 | + auto data = AS.getStreamData(qpdf_dl_all); | ||
| 930 | + AS = AS.copy(); | ||
| 931 | + AS.replaceStreamData(std::move(data), Null::temp(), Null::temp()); | ||
| 932 | + if (Dictionary AP = aoh.getAppearanceDictionary()) { | ||
| 933 | + AP.replace("/N", AS); | ||
| 934 | + } else { | ||
| 935 | + aoh.replace("/AP", Dictionary({{"/N", AS}})); | ||
| 936 | + // aoh is a dictionary, so insertion will succeed. No need to check by retrieving it. | ||
| 937 | + } | ||
| 931 | } | 938 | } |
| 932 | QPDFObjectHandle bbox_obj = AS.getDict()["/BBox"]; | 939 | QPDFObjectHandle bbox_obj = AS.getDict()["/BBox"]; |
| 933 | if (!bbox_obj.isRectangle()) { | 940 | if (!bbox_obj.isRectangle()) { |
manual/release-notes.rst
| @@ -115,10 +115,6 @@ more detail. | @@ -115,10 +115,6 @@ more detail. | ||
| 115 | - There has been significant internal refactoring affecting most parts of | 115 | - There has been significant internal refactoring affecting most parts of |
| 116 | qpdf's code base. | 116 | qpdf's code base. |
| 117 | 117 | ||
| 118 | - - When flattening widget annotations further checks have been added to detect | ||
| 119 | - when qpdf cannot reliably generate the necessary appearance streams. As in | ||
| 120 | - other such cases a warning is issued and the annotation remains unflattened. | ||
| 121 | - | ||
| 122 | - By default, streams with more than 25 filters are now treated as unfilterable. | 118 | - By default, streams with more than 25 filters are now treated as unfilterable. |
| 123 | A large number of filters typically occur in damaged or specially constructed | 119 | A large number of filters typically occur in damaged or specially constructed |
| 124 | files and can cause excessive use of resources and/or stack overflows. The | 120 | files and can cause excessive use of resources and/or stack overflows. The |