Commit 361b387b7a13c32f8bae3d45e68a6f5b2fa3592c
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.
Showing
1 changed file
with
18 additions
and
10 deletions
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; | ... | ... |