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 890 void
891 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 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 905 {{"/BBox", QPDFObjectHandle::newFromRectangle(bbox)},
899 906 {"/Resources", Dictionary({{"/ProcSet", Array({Name("/PDF"), Name("/Text")})}})},
900 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 909 if (auto ap = AP()) {
905 910 ap.replace("/N", AS);
906 911 } else {
907 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 919 aoh.warn("unable to get normal appearance stream for update");
912 920 return;
913 921 }
... ... @@ -927,7 +935,7 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh)
927 935 aoh.warn("unable to generate text appearance from shared appearance stream for update");
928 936 return;
929 937 }
930   - QPDFObjectHandle bbox_obj = AS.getDict().getKey("/BBox");
  938 + QPDFObjectHandle bbox_obj = AS.getDict()["/BBox"];
931 939 if (!bbox_obj.isRectangle()) {
932 940 aoh.warn("unable to get appearance stream bounding box");
933 941 return;
... ... @@ -958,7 +966,7 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh)
958 966 if (resources) {
959 967 if (resources.indirect()) {
960 968 resources = resources.qpdf()->makeIndirectObject(resources.copy());
961   - AS.getDict().replaceKey("/Resources", resources);
  969 + AS.getDict().replace("/Resources", resources);
962 970 }
963 971 // Use mergeResources to force /Font to be local
964 972 QPDFObjectHandle res = resources;
... ...