@@ -208,6 +208,50 @@ def test_build_destination__short_array():
208208 assert dest ["/Type" ] == "/FitR"
209209
210210
211+ def _reader_with_button_field (field : DictionaryObject ) -> PdfReader :
212+ writer = PdfWriter ()
213+ writer .add_blank_page (width = 72 , height = 72 )
214+ reference = writer ._add_object (field )
215+ acroform = DictionaryObject ()
216+ acroform [NameObject ("/Fields" )] = ArrayObject ([reference ])
217+ writer ._root_object [NameObject ("/AcroForm" )] = writer ._add_object (acroform )
218+ stream = BytesIO ()
219+ writer .write (stream )
220+ stream .seek (0 )
221+ return PdfReader (stream )
222+
223+
224+ def test_build_field__checkbox_missing_normal_appearance ():
225+ # A checkbox whose /AP has no /N sub-dictionary must not crash get_fields().
226+ field = DictionaryObject ()
227+ field [NameObject ("/T" )] = TextStringObject ("CheckBox" )
228+ field [NameObject ("/FT" )] = NameObject ("/Btn" )
229+ field [NameObject ("/AP" )] = DictionaryObject ()
230+ fields = _reader_with_button_field (field ).get_fields ()
231+ assert "CheckBox" in fields
232+ assert "/_States_" not in fields ["CheckBox" ]
233+
234+ # A well-formed /AP /N still collects the appearance states.
235+ normal = DictionaryObject ()
236+ normal [NameObject ("/Yes" )] = NumberObject (1 )
237+ appearance = DictionaryObject ()
238+ appearance [NameObject ("/N" )] = normal
239+ field [NameObject ("/AP" )] = appearance
240+ fields = _reader_with_button_field (field ).get_fields ()
241+ assert all (state in fields ["CheckBox" ]["/_States_" ] for state in ("/Yes" , "/Off" ))
242+
243+
244+ def test_build_field__radio_kid_missing_appearance ():
245+ # A radio kid lacking /AP (or its /N) must not crash get_fields().
246+ field = DictionaryObject ()
247+ field [NameObject ("/T" )] = TextStringObject ("Radio" )
248+ field [NameObject ("/FT" )] = NameObject ("/Btn" )
249+ field [NameObject ("/Ff" )] = NumberObject (1 << 15 )
250+ field [NameObject ("/Kids" )] = ArrayObject ([DictionaryObject ()])
251+ fields = _reader_with_button_field (field ).get_fields ()
252+ assert list (fields ["Radio" ]["/_States_" ]) == []
253+
254+
211255@pytest .mark .enable_socket
212256def test_named_destinations__tree_is_null_object ():
213257 url = "https://github.com/user-attachments/files/20885216/test.pdf"
0 commit comments