Setting the file. One moment.
Extract Form Field Info · PDF · anthropics/skills · Skills Docs
ContentsBack to the top of the page scripts/ extract_form_field_info.py
Python · 122 lines · 4 KB
14 components.append(field_name)
15 annotation = annotation.get( '/Parent' )
16 return "." .join( reversed (components)) if components else None
17
18
19 def make_field_dict (field, field_id):
20 field_dict = { "field_id" : field_id}
21 ft = field.get( '/FT' )
22 if ft == "/Tx" :
23 field_dict[ "type" ] = "text"
24 elif ft == "/Btn" :
25 field_dict[ "type" ] = "checkbox"
26 states = field.get( "/_States_" , [])
27 if len (states) == 2 :
28 if "/Off" in states:
29 field_dict[ "checked_value" ] = states[ 0 ] if states[ 0 ] != "/Off" else states[ 1 ]
30 field_dict[ "unchecked_value" ] = "/Off"
31 else :
32 print ( f "Unexpected state values for checkbox `$ { field_id } `. Its checked and unchecked values may not be correct; if you're trying to check it, visually verify the results." )
33 field_dict[ "checked_value" ] = states[ 0 ]
34 field_dict[ "unchecked_value" ] = states[ 1 ]
35 elif ft == "/Ch" :
36 field_dict[ "type" ] = "choice"
37 states = field.get( "/_States_" , [])
38 field_dict[ "choice_options" ] = [{
39 "value" : state[ 0 ],
40 "text" : state[ 1 ],
41 } for state in states]
42 else :
43 field_dict[ "type" ] = f "unknown ( { ft } )"
44 return field_dict
45
46
47 def get_field_info (reader: PdfReader):
48 fields = reader.get_fields()
49
50 field_info_by_id = {}
51 possible_radio_names = set ()
52
53 for field_id, field in fields.items():
54 if field.get( "/Kids" ):
55 if field.get( "/FT" ) == "/Btn" :
56 possible_radio_names.add(field_id)
57 continue
58 field_info_by_id[field_id] = make_field_dict(field, field_id)
59
60
61 radio_fields_by_id = {}
62
63 for page_index, page in enumerate (reader.pages):
64 annotations = page.get( '/Annots' , [])
65 for ann in annotations:
66 field_id = get_full_annotation_field_id(ann)
67 if field_id in field_info_by_id:
68 field_info_by_id[field_id][ "page" ] = page_index + 1
69 field_info_by_id[field_id][ "rect" ] = ann.get( '/Rect' )
70 elif field_id in possible_radio_names:
71 try :
72 on_values = [v for v in ann[ "/AP" ][ "/N" ] if v != "/Off" ]
73 except KeyError :
74 continue
75 if len (on_values) == 1 :
76 rect = ann.get( "/Rect" )
77 if field_id not in radio_fields_by_id:
78 radio_fields_by_id[field_id] = {
79 "field_id" : field_id,
80 "type" : "radio_group" ,
81 "page" : page_index + 1 ,
82 "radio_options" : [],
83 }
84 radio_fields_by_id[field_id][ "radio_options" ].append({
85 "value" : on_values[ 0 ],
86 "rect" : rect,
87 })
88
89 fields_with_location = []
90 for field_info in field_info_by_id.values():
91 if "page" in field_info:
92 fields_with_location.append(field_info)
93 else :
94 print ( f "Unable to determine location for field id: { field_info.get( 'field_id' ) } , ignoring" )
95
96 def sort_key (f):
97 if "radio_options" in f:
98 rect = f[ "radio_options" ][ 0 ][ "rect" ] or [ 0 , 0 , 0 , 0 ]
99 else :
100 rect = f.get( "rect" ) or [ 0 , 0 , 0 , 0 ]
101 adjusted_position = [ - rect[ 1 ], rect[ 0 ]]
102 return [f.get( "page" ), adjusted_position]
103
104 sorted_fields = fields_with_location + list (radio_fields_by_id.values())
105 sorted_fields.sort( key = sort_key)
106
107 return sorted_fields
108
109
110 def write_field_info (pdf_path: str , json_output_path: str ):
111 reader = PdfReader(pdf_path)
112 field_info = get_field_info(reader)
113 with open (json_output_path, "w" ) as f:
114 json.dump(field_info, f, indent = 2 )
115 print ( f "Wrote { len (field_info) } fields to { json_output_path } " )
116
117
118 if __name__ == "__main__" :
119 if len (sys.argv) != 3 :
120 print ( "Usage: extract_form_field_info.py [input pdf] [output json]" )
121 sys.exit( 1 )
122 write_field_info(sys.argv[ 1 ], sys.argv[ 2 ])