Commit 8aab68386a860433fb681f8cf69b5511696fcf15

Authored by m-holger
1 parent cf0e3422

Fix appearance stream handling in `QPDFFormFieldObjectHelper::generateTextAppear…

…ance`: copy stream data if shared references exceed threshold, ensuring safe updates.
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