@@ -238,7 +238,11 @@ def from_pikefield(cls, pike_field: PikeField) -> "FormField":
238238 )
239239
240240 def get_bbox (self ) -> BoundingBoxF :
241- if self .type == FieldType .TEXT or self .type == FieldType .AREA :
241+ if (
242+ self .type == FieldType .TEXT
243+ or self .type == FieldType .AREA
244+ or self .type == FieldType .SIGNATURE
245+ ):
242246 return (
243247 self .x ,
244248 self .y ,
@@ -340,6 +344,55 @@ def _create_only_fields(
340344 c .save ()
341345
342346
347+ def _normalize_signature_fields (pdf : Pdf , signature_field_names : Iterable [str ]) -> None :
348+ """Convert ReportLab text fields into PDF signature fields.
349+
350+ ReportLab does not expose an AcroForm signature widget API, so
351+ ``_create_only_fields`` creates signature placeholders as text fields. This
352+ function changes those generated widgets to ``/FT /Sig`` before they are
353+ copied into the destination PDF.
354+ """
355+ signature_names = {
356+ str (field_name ).strip ()
357+ for field_name in signature_field_names
358+ if str (field_name ).strip ()
359+ }
360+ if not signature_names or not hasattr (pdf .Root , "AcroForm" ):
361+ return
362+
363+ converted = 0
364+
365+ def _normalize_object (obj : Any ) -> bool :
366+ nonlocal converted
367+ if obj is None or not hasattr (obj , "get" ):
368+ return False
369+ field_name = str (obj .get ("/T" , "" ) or "" ).strip ()
370+ if field_name not in signature_names :
371+ return False
372+ if str (obj .get ("/FT" , "" )) != "/Sig" :
373+ converted += 1
374+ obj ["/FT" ] = pikepdf .Name ("/Sig" )
375+ for key in ("/V" , "/DV" , "/MaxLen" , "/Q" , "/DS" , "/RV" ):
376+ if key in obj :
377+ del obj [key ]
378+ return True
379+
380+ for field in pdf .Root .AcroForm .get ("/Fields" , []):
381+ if _normalize_object (field ):
382+ continue
383+ if hasattr (field , "Kids" ):
384+ for kid in field .Kids :
385+ if _normalize_object (kid ):
386+ kid ["/FT" ] = pikepdf .Name ("/Sig" )
387+
388+ if converted :
389+ try :
390+ existing_flags = int (pdf .Root .AcroForm .get ("/SigFlags" , 0 ) or 0 )
391+ except (TypeError , ValueError ):
392+ existing_flags = 0
393+ pdf .Root .AcroForm ["/SigFlags" ] = existing_flags | 3
394+
395+
343396def set_fields (
344397 in_file : Union [str , Path , BinaryIO ],
345398 out_file : Union [str , Path , BinaryIO ],
@@ -384,6 +437,7 @@ def set_fields(
384437 if not fields_per_page :
385438 # Nothing to do, lol
386439 return
440+ fields_per_page = [list (page_fields ) for page_fields in fields_per_page ]
387441 in_pdf = Pdf .open (in_file , allow_overwriting_input = overwrite )
388442 if hasattr (in_pdf .Root , "AcroForm" ) and not overwrite :
389443 print ("Not going to overwrite the existing AcroForm!" )
@@ -392,6 +446,13 @@ def set_fields(
392446 io_obj = io .BytesIO ()
393447 _create_only_fields (io_obj , fields_per_page )
394448 temp_pdf = Pdf .open (io_obj )
449+ signature_field_names = [
450+ field .name
451+ for page_fields in fields_per_page
452+ for field in page_fields
453+ if field .type == FieldType .SIGNATURE
454+ ]
455+ _normalize_signature_fields (temp_pdf , signature_field_names )
395456
396457 in_pdf = copy_pdf_fields (source_pdf = temp_pdf , destination_pdf = in_pdf )
397458 in_pdf .save (out_file )
0 commit comments