@@ -148,6 +148,13 @@ def _serialize_redactions(value: list[_PdfRedactionVariant]) -> str:
148148 return json .dumps (payload , separators = ("," , ":" ))
149149
150150
151+ def _serialize_signature_configuration (
152+ value : _PdfSignatureConfigurationModel ,
153+ ) -> str :
154+ payload = value .model_dump (mode = "json" , exclude_none = True )
155+ return json .dumps (payload , separators = ("," , ":" ))
156+
157+
151158def _allowed_mime_types (
152159 allowed_mime_types : str , * more_allowed_mime_types : str , error_msg : str | None
153160) -> Callable [[Any ], Any ]:
@@ -580,6 +587,34 @@ class PdfPresetRedactionModel(BaseModel):
580587 value : PdfRedactionPreset
581588
582589
590+ class _PdfSignaturePointModel (BaseModel ):
591+ x : str | int | float
592+ y : str | int | float
593+
594+
595+ class _PdfSignatureLocationModel (BaseModel ):
596+ bottom_left : _PdfSignaturePointModel
597+ top_right : _PdfSignaturePointModel
598+ page : str | int
599+
600+
601+ class _PdfSignatureDisplayModel (BaseModel ):
602+ include_distinguished_name : bool | None = None
603+ include_datetime : bool | None = None
604+ contact : str | None = None
605+ location : str | None = None
606+ name : str | None = None
607+ reason : str | None = None
608+
609+
610+ class _PdfSignatureConfigurationModel (BaseModel ):
611+ type : Literal ["new" ]
612+ name : str | None = None
613+ logo_opacity : Annotated [float | None , Field (ge = 0 , le = 1 , default = None )] = None
614+ location : _PdfSignatureLocationModel | None = None
615+ display : _PdfSignatureDisplayModel | None = None
616+
617+
583618_PdfRedactionVariant = Annotated [
584619 PdfLiteralRedactionModel | PdfRegexRedactionModel | PdfPresetRedactionModel ,
585620 Field (discriminator = "type" ),
@@ -963,6 +998,158 @@ class PdfFlattenFormsPayload(BaseModel):
963998 ] = None
964999
9651000
1001+ class PdfSignPayload (BaseModel ):
1002+ """Adapt caller options into a pdfRest-ready sign request payload."""
1003+
1004+ files : Annotated [
1005+ list [PdfRestFile ],
1006+ Field (
1007+ min_length = 1 ,
1008+ max_length = 1 ,
1009+ validation_alias = AliasChoices ("file" , "files" ),
1010+ serialization_alias = "id" ,
1011+ ),
1012+ BeforeValidator (_ensure_list ),
1013+ AfterValidator (
1014+ _allowed_mime_types ("application/pdf" , error_msg = "Must be a PDF file" )
1015+ ),
1016+ PlainSerializer (_serialize_as_first_file_id ),
1017+ ]
1018+ signature_configuration : Annotated [
1019+ _PdfSignatureConfigurationModel ,
1020+ Field (serialization_alias = "signature_configuration" ),
1021+ PlainSerializer (_serialize_signature_configuration ),
1022+ ]
1023+ pfx_credential : Annotated [
1024+ list [PdfRestFile ] | None ,
1025+ Field (
1026+ default = None ,
1027+ min_length = 1 ,
1028+ max_length = 1 ,
1029+ validation_alias = AliasChoices ("pfx" , "pfx_credential" ),
1030+ serialization_alias = "pfx_credential_id" ,
1031+ ),
1032+ BeforeValidator (_ensure_list ),
1033+ BeforeValidator (
1034+ _allowed_mime_types (
1035+ "application/x-pkcs12" ,
1036+ "application/pkcs12" ,
1037+ "application/octet-stream" ,
1038+ error_msg = "PFX credentials must be a .pfx or .p12 file" ,
1039+ )
1040+ ),
1041+ PlainSerializer (_serialize_as_first_file_id ),
1042+ ] = None
1043+ pfx_passphrase : Annotated [
1044+ list [PdfRestFile ] | None ,
1045+ Field (
1046+ default = None ,
1047+ min_length = 1 ,
1048+ max_length = 1 ,
1049+ validation_alias = AliasChoices ("pfx_passphrase" , "passphrase" ),
1050+ serialization_alias = "pfx_passphrase_id" ,
1051+ ),
1052+ BeforeValidator (_ensure_list ),
1053+ BeforeValidator (
1054+ _allowed_mime_types (
1055+ "text/plain" ,
1056+ "application/octet-stream" ,
1057+ error_msg = "PFX passphrase must be a text file" ,
1058+ )
1059+ ),
1060+ PlainSerializer (_serialize_as_first_file_id ),
1061+ ] = None
1062+ certificate : Annotated [
1063+ list [PdfRestFile ] | None ,
1064+ Field (
1065+ default = None ,
1066+ min_length = 1 ,
1067+ max_length = 1 ,
1068+ validation_alias = AliasChoices ("certificate" , "cert" ),
1069+ serialization_alias = "certificate_id" ,
1070+ ),
1071+ BeforeValidator (_ensure_list ),
1072+ BeforeValidator (
1073+ _allowed_mime_types (
1074+ "application/pkix-cert" ,
1075+ "application/x-x509-ca-cert" ,
1076+ "application/x-pem-file" ,
1077+ "application/octet-stream" ,
1078+ error_msg = "Certificate must be a .pem or .der file" ,
1079+ )
1080+ ),
1081+ PlainSerializer (_serialize_as_first_file_id ),
1082+ ] = None
1083+ private_key : Annotated [
1084+ list [PdfRestFile ] | None ,
1085+ Field (
1086+ default = None ,
1087+ min_length = 1 ,
1088+ max_length = 1 ,
1089+ validation_alias = AliasChoices ("private_key" , "key" ),
1090+ serialization_alias = "private_key_id" ,
1091+ ),
1092+ BeforeValidator (_ensure_list ),
1093+ BeforeValidator (
1094+ _allowed_mime_types (
1095+ "application/pkcs8" ,
1096+ "application/x-pem-file" ,
1097+ "application/octet-stream" ,
1098+ error_msg = "Private key must be a .pem or .der file" ,
1099+ )
1100+ ),
1101+ PlainSerializer (_serialize_as_first_file_id ),
1102+ ] = None
1103+ logo : Annotated [
1104+ list [PdfRestFile ] | None ,
1105+ Field (
1106+ default = None ,
1107+ min_length = 1 ,
1108+ max_length = 1 ,
1109+ validation_alias = AliasChoices ("logo" , "logos" ),
1110+ serialization_alias = "logo_id" ,
1111+ ),
1112+ BeforeValidator (_ensure_list ),
1113+ BeforeValidator (
1114+ _allowed_mime_types (
1115+ "image/jpeg" ,
1116+ "image/png" ,
1117+ "image/tiff" ,
1118+ "image/bmp" ,
1119+ error_msg = "Logo must be an image file" ,
1120+ )
1121+ ),
1122+ PlainSerializer (_serialize_as_first_file_id ),
1123+ ] = None
1124+ output : Annotated [
1125+ str | None ,
1126+ Field (serialization_alias = "output" , min_length = 1 , default = None ),
1127+ AfterValidator (_validate_output_prefix ),
1128+ ] = None
1129+
1130+ @model_validator (mode = "after" )
1131+ def _validate_credentials (self ) -> PdfSignPayload :
1132+ has_pfx = self .pfx_credential is not None or self .pfx_passphrase is not None
1133+ has_pem = self .certificate is not None or self .private_key is not None
1134+
1135+ if has_pfx and has_pem :
1136+ msg = "Provide either PFX credentials (pfx + passphrase) or certificate/private_key, not both."
1137+ raise ValueError (msg )
1138+ if has_pfx :
1139+ if not self .pfx_credential or not self .pfx_passphrase :
1140+ msg = "Both pfx and passphrase are required when supplying PFX credentials."
1141+ raise ValueError (msg )
1142+ elif has_pem :
1143+ if not self .certificate or not self .private_key :
1144+ msg = "Both certificate and private_key are required when supplying PEM/DER credentials."
1145+ raise ValueError (msg )
1146+ else :
1147+ msg = "Either PFX credentials (pfx + passphrase) or certificate/private_key credentials are required."
1148+ raise ValueError (msg )
1149+
1150+ return self
1151+
1152+
9661153class PdfCompressPayload (BaseModel ):
9671154 """Adapt caller options into a pdfRest-ready compress request payload."""
9681155
0 commit comments