Commit 361b387b7a13c32f8bae3d45e68a6f5b2fa3592c

Authored by m-holger
1 parent c8f431de

Refactor `FormNode::generateTextAppearance` to improve appearance stream handlin…

…g: ensure checks for dictionary annotations, streamline appearance creation flow, and replace manual key access with methods.
libqpdf/QPDFFormFieldObjectHelper.cc
@@ -890,24 +890,32 @@ FormNode::getFontFromResource(QPDFObjectHandle resources, std::string const& nam @@ -890,24 +890,32 @@ FormNode::getFontFromResource(QPDFObjectHandle resources, std::string const& nam
890 void 890 void
891 FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) 891 FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh)
892 { 892 {
893 - QPDFObjectHandle AS = aoh.getAppearanceStream("/N");  
894 - if (AS.null()) {  
895 - QPDFObjectHandle::Rectangle rect = aoh.getRect(); 893 + no_ci_warn_if(
  894 + !Dictionary(aoh), // There is no guarantee that aoh is a dictionary
  895 + "cannot generate appearance for non-dictionary annotation" //
  896 + );
  897 + Stream AS = aoh.getAppearanceStream("/N"); // getAppearanceStream returns a stream or null.
  898 + if (!AS) {
  899 + QPDFObjectHandle::Rectangle rect = aoh.getRect(); // may silently be invalid / all zeros
896 QPDFObjectHandle::Rectangle bbox(0, 0, rect.urx - rect.llx, rect.ury - rect.lly); 900 QPDFObjectHandle::Rectangle bbox(0, 0, rect.urx - rect.llx, rect.ury - rect.lly);
897 - auto dict = Dictionary( 901 + auto* pdf = qpdf();
  902 + no_ci_stop_damaged_if(!pdf, "unable to get owning QPDF for appearance generation");
  903 + AS = pdf->newStream("/Tx BMC\nEMC\n");
  904 + AS.replaceDict(Dictionary(
898 {{"/BBox", QPDFObjectHandle::newFromRectangle(bbox)}, 905 {{"/BBox", QPDFObjectHandle::newFromRectangle(bbox)},
899 {"/Resources", Dictionary({{"/ProcSet", Array({Name("/PDF"), Name("/Text")})}})}, 906 {"/Resources", Dictionary({{"/ProcSet", Array({Name("/PDF"), Name("/Text")})}})},
900 {"/Type", Name("/XObject")}, 907 {"/Type", Name("/XObject")},
901 - {"/Subtype", Name("/Form")}});  
902 - AS = QPDFObjectHandle::newStream(oh().getOwningQPDF(), "/Tx BMC\nEMC\n");  
903 - AS.replaceDict(dict); 908 + {"/Subtype", Name("/Form")}}));
904 if (auto ap = AP()) { 909 if (auto ap = AP()) {
905 ap.replace("/N", AS); 910 ap.replace("/N", AS);
906 } else { 911 } else {
907 aoh.replace("/AP", Dictionary({{"/N", AS}})); 912 aoh.replace("/AP", Dictionary({{"/N", AS}}));
908 } 913 }
909 } 914 }
910 - if (!AS.isStream()) { 915 + if (!AS) {
  916 + // This could only have happened if aoh.getAppearanceStream("/N") did not return a stream.
  917 + // The only way creation of a new AS could have failed is if getOwningQPDF returned a
  918 + // nullptr, but this would throw a runtime error in newStream. So this should be impossible
911 aoh.warn("unable to get normal appearance stream for update"); 919 aoh.warn("unable to get normal appearance stream for update");
912 return; 920 return;
913 } 921 }
@@ -927,7 +935,7 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) @@ -927,7 +935,7 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh)
927 aoh.warn("unable to generate text appearance from shared appearance stream for update"); 935 aoh.warn("unable to generate text appearance from shared appearance stream for update");
928 return; 936 return;
929 } 937 }
930 - QPDFObjectHandle bbox_obj = AS.getDict().getKey("/BBox"); 938 + QPDFObjectHandle bbox_obj = AS.getDict()["/BBox"];
931 if (!bbox_obj.isRectangle()) { 939 if (!bbox_obj.isRectangle()) {
932 aoh.warn("unable to get appearance stream bounding box"); 940 aoh.warn("unable to get appearance stream bounding box");
933 return; 941 return;
@@ -958,7 +966,7 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) @@ -958,7 +966,7 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh)
958 if (resources) { 966 if (resources) {
959 if (resources.indirect()) { 967 if (resources.indirect()) {
960 resources = resources.qpdf()->makeIndirectObject(resources.copy()); 968 resources = resources.qpdf()->makeIndirectObject(resources.copy());
961 - AS.getDict().replaceKey("/Resources", resources); 969 + AS.getDict().replace("/Resources", resources);
962 } 970 }
963 // Use mergeResources to force /Font to be local 971 // Use mergeResources to force /Font to be local
964 QPDFObjectHandle res = resources; 972 QPDFObjectHandle res = resources;