1use crate::error::PdfError;
7use crate::graphics::Color;
8use crate::objects::{Dictionary, Object};
9use crate::text::Font;
10use chrono::{DateTime, Utc};
11
12#[derive(Debug, Clone)]
14pub struct SignatureField {
15 pub name: String,
17 pub signer: Option<SignerInfo>,
19 pub signature_value: Option<SignatureValue>,
21 pub lock_fields: Vec<String>,
23 pub required: bool,
25 pub reason: Option<String>,
27 pub location: Option<String>,
29 pub contact_info: Option<String>,
31 pub appearance: SignatureAppearance,
33}
34
35#[derive(Debug, Clone)]
37pub struct SignerInfo {
38 pub name: String,
40 pub distinguished_name: Option<String>,
42 pub email: Option<String>,
44 pub organization: Option<String>,
46 pub organizational_unit: Option<String>,
48}
49
50#[derive(Debug, Clone)]
52pub struct SignatureValue {
53 pub timestamp: DateTime<Utc>,
55 pub document_hash: Vec<u8>,
57 pub algorithm: SignatureAlgorithm,
59 pub certificates: Vec<Certificate>,
61 pub signature_bytes: Vec<u8>,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq)]
67pub enum SignatureAlgorithm {
68 RsaSha256,
70 RsaSha384,
72 RsaSha512,
74 EcdsaSha256,
76 DsaSha256,
78}
79
80#[derive(Debug, Clone)]
82pub struct Certificate {
83 pub subject: String,
85 pub issuer: String,
87 pub serial_number: String,
89 pub not_before: DateTime<Utc>,
91 pub not_after: DateTime<Utc>,
93 pub public_key_info: String,
95}
96
97#[derive(Debug, Clone)]
99pub struct SignatureAppearance {
100 pub show_name: bool,
102 pub show_date: bool,
104 pub show_reason: bool,
106 pub show_location: bool,
108 pub show_dn: bool,
110 pub show_labels: bool,
112 pub background_color: Option<Color>,
114 pub border_color: Color,
116 pub border_width: f64,
118 pub text_color: Color,
120 pub font: Font,
122 pub font_size: f64,
124 pub logo_data: Option<Vec<u8>>,
126}
127
128impl Default for SignatureAppearance {
129 fn default() -> Self {
130 Self {
131 show_name: true,
132 show_date: true,
133 show_reason: true,
134 show_location: false,
135 show_dn: false,
136 show_labels: true,
137 background_color: Some(Color::gray(0.95)),
138 border_color: Color::black(),
139 border_width: 1.0,
140 text_color: Color::black(),
141 font: Font::Helvetica,
142 font_size: 10.0,
143 logo_data: None,
144 }
145 }
146}
147
148impl SignatureField {
149 pub fn new(name: impl Into<String>) -> Self {
151 Self {
152 name: name.into(),
153 signer: None,
154 signature_value: None,
155 lock_fields: Vec::new(),
156 required: false,
157 reason: None,
158 location: None,
159 contact_info: None,
160 appearance: SignatureAppearance::default(),
161 }
162 }
163
164 pub fn with_signer(mut self, signer: SignerInfo) -> Self {
166 self.signer = Some(signer);
167 self
168 }
169
170 pub fn with_reason(mut self, reason: impl Into<String>) -> Self {
172 self.reason = Some(reason.into());
173 self
174 }
175
176 pub fn with_location(mut self, location: impl Into<String>) -> Self {
178 self.location = Some(location.into());
179 self
180 }
181
182 pub fn with_contact(mut self, contact: impl Into<String>) -> Self {
184 self.contact_info = Some(contact.into());
185 self
186 }
187
188 pub fn lock_fields_after_signing(mut self, fields: Vec<String>) -> Self {
190 self.lock_fields = fields;
191 self
192 }
193
194 pub fn required(mut self) -> Self {
196 self.required = true;
197 self
198 }
199
200 pub fn with_appearance(mut self, appearance: SignatureAppearance) -> Self {
202 self.appearance = appearance;
203 self
204 }
205
206 pub fn is_signed(&self) -> bool {
208 self.signature_value.is_some()
209 }
210
211 pub fn sign(&mut self, signer: SignerInfo, reason: Option<String>) -> Result<(), PdfError> {
213 if self.is_signed() {
214 return Err(PdfError::InvalidOperation(
215 "Field is already signed".to_string(),
216 ));
217 }
218
219 let signature_value = SignatureValue {
221 timestamp: Utc::now(),
222 document_hash: vec![0; 32], algorithm: SignatureAlgorithm::RsaSha256,
224 certificates: vec![],
225 signature_bytes: vec![0; 256], };
227
228 self.signer = Some(signer);
229 if let Some(r) = reason {
230 self.reason = Some(r);
231 }
232 self.signature_value = Some(signature_value);
233
234 Ok(())
235 }
236
237 pub fn verify(&self) -> Result<bool, PdfError> {
239 if !self.is_signed() {
240 return Ok(false);
241 }
242
243 Ok(true)
247 }
248
249 pub fn generate_appearance(&self, width: f64, height: f64) -> Result<Vec<u8>, PdfError> {
251 let mut stream = Vec::new();
252
253 if let Some(bg_color) = self.appearance.background_color {
256 crate::graphics::color::write_fill_color_bytes(&mut stream, bg_color);
257 stream.extend(format!("0 0 {} {} re f\n", width, height).as_bytes());
258 }
259
260 crate::graphics::color::write_stroke_color_bytes(&mut stream, self.appearance.border_color);
262 stream.extend(format!("{} w\n", self.appearance.border_width).as_bytes());
263 stream.extend(format!("0 0 {} {} re S\n", width, height).as_bytes());
264
265 stream.extend(b"BT\n");
267 stream.extend(
268 format!(
269 "/{} {} Tf\n",
270 self.appearance.font.pdf_name(),
271 self.appearance.font_size
272 )
273 .as_bytes(),
274 );
275 crate::graphics::color::write_fill_color_bytes(&mut stream, self.appearance.text_color);
276
277 let mut y_pos = height - self.appearance.font_size - 5.0;
278 let x_pos = 5.0;
279
280 if self.is_signed() {
281 if let Some(ref signer) = self.signer {
283 if self.appearance.show_name {
284 let label = if self.appearance.show_labels {
285 "Digitally signed by: "
286 } else {
287 ""
288 };
289 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
290 stream.extend(format!("({}{}) Tj\n", label, signer.name).as_bytes());
291 y_pos -= self.appearance.font_size + 2.0;
292 }
293
294 if self.appearance.show_dn {
295 if let Some(ref dn) = signer.distinguished_name {
296 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
297 stream.extend(format!("(DN: {}) Tj\n", dn).as_bytes());
298 y_pos -= self.appearance.font_size + 2.0;
299 }
300 }
301 }
302
303 if self.appearance.show_date {
304 if let Some(ref sig_value) = self.signature_value {
305 let label = if self.appearance.show_labels {
306 "Date: "
307 } else {
308 ""
309 };
310 let date_str = sig_value
311 .timestamp
312 .format("%Y-%m-%d %H:%M:%S UTC")
313 .to_string();
314 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
315 stream.extend(format!("({}{}) Tj\n", label, date_str).as_bytes());
316 y_pos -= self.appearance.font_size + 2.0;
317 }
318 }
319
320 if self.appearance.show_reason {
321 if let Some(ref reason) = self.reason {
322 let label = if self.appearance.show_labels {
323 "Reason: "
324 } else {
325 ""
326 };
327 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
328 stream.extend(format!("({}{}) Tj\n", label, reason).as_bytes());
329 y_pos -= self.appearance.font_size + 2.0;
330 }
331 }
332
333 if self.appearance.show_location {
334 if let Some(ref location) = self.location {
335 let label = if self.appearance.show_labels {
336 "Location: "
337 } else {
338 ""
339 };
340 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
341 stream.extend(format!("({}{}) Tj\n", label, location).as_bytes());
342 }
343 }
344 } else {
345 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
347 stream.extend(b"(Click to sign) Tj\n");
348 }
349
350 stream.extend(b"ET\n");
351
352 Ok(stream)
353 }
354
355 pub fn to_dict(&self) -> Dictionary {
357 let mut dict = Dictionary::new();
358
359 dict.set("Type", Object::Name("Annot".to_string()));
360 dict.set("Subtype", Object::Name("Widget".to_string()));
361 dict.set("FT", Object::Name("Sig".to_string()));
362 dict.set("T", Object::String(self.name.clone()));
363
364 let mut flags = 0;
366 if self.required {
367 flags |= 2; }
369 dict.set("Ff", Object::Integer(flags));
370
371 if self.is_signed() {
373 let mut sig_dict = Dictionary::new();
374 sig_dict.set("Type", Object::Name("Sig".to_string()));
375
376 if let Some(ref signer) = self.signer {
377 sig_dict.set("Name", Object::String(signer.name.clone()));
378 if let Some(ref email) = signer.email {
379 sig_dict.set("ContactInfo", Object::String(email.clone()));
380 }
381 }
382
383 if let Some(ref reason) = self.reason {
384 sig_dict.set("Reason", Object::String(reason.clone()));
385 }
386
387 if let Some(ref location) = self.location {
388 sig_dict.set("Location", Object::String(location.clone()));
389 }
390
391 if let Some(ref sig_value) = self.signature_value {
392 sig_dict.set(
393 "M",
394 Object::String(sig_value.timestamp.format("%Y%m%d%H%M%S%z").to_string()),
395 );
396 }
397
398 dict.set("V", Object::Dictionary(sig_dict));
399 }
400
401 if !self.lock_fields.is_empty() {
403 let mut lock_dict = Dictionary::new();
404 lock_dict.set("Type", Object::Name("SigFieldLock".to_string()));
405
406 let fields: Vec<Object> = self
407 .lock_fields
408 .iter()
409 .map(|f| Object::String(f.clone()))
410 .collect();
411 lock_dict.set("Fields", Object::Array(fields));
412
413 dict.set("Lock", Object::Dictionary(lock_dict));
414 }
415
416 dict
417 }
418}
419
420impl SignerInfo {
421 pub fn new(name: impl Into<String>) -> Self {
423 Self {
424 name: name.into(),
425 distinguished_name: None,
426 email: None,
427 organization: None,
428 organizational_unit: None,
429 }
430 }
431
432 pub fn with_email(mut self, email: impl Into<String>) -> Self {
434 self.email = Some(email.into());
435 self
436 }
437
438 pub fn with_organization(mut self, org: impl Into<String>) -> Self {
440 self.organization = Some(org.into());
441 self
442 }
443
444 pub fn build_dn(&mut self) {
446 let mut dn_parts = vec![format!("CN={}", self.name)];
447
448 if let Some(ref email) = self.email {
449 dn_parts.push(format!("emailAddress={}", email));
450 }
451
452 if let Some(ref org) = self.organization {
453 dn_parts.push(format!("O={}", org));
454 }
455
456 if let Some(ref ou) = self.organizational_unit {
457 dn_parts.push(format!("OU={}", ou));
458 }
459
460 self.distinguished_name = Some(dn_parts.join(", "));
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467
468 #[test]
469 fn test_signature_field_creation() {
470 let field = SignatureField::new("sig1");
471 assert_eq!(field.name, "sig1");
472 assert!(!field.is_signed());
473 assert!(!field.required);
474 }
475
476 #[test]
477 fn test_signer_info() {
478 let mut signer = SignerInfo::new("John Doe")
479 .with_email("john@example.com")
480 .with_organization("ACME Corp");
481
482 signer.build_dn();
483 assert!(signer.distinguished_name.is_some());
484 assert!(signer.distinguished_name.unwrap().contains("CN=John Doe"));
485 }
486
487 #[test]
488 fn test_sign_field() {
489 let mut field = SignatureField::new("sig1");
490 let signer = SignerInfo::new("Jane Smith");
491
492 assert!(field
493 .sign(signer.clone(), Some("Approval".to_string()))
494 .is_ok());
495 assert!(field.is_signed());
496 assert_eq!(field.reason, Some("Approval".to_string()));
497
498 assert!(field.sign(signer, None).is_err());
500 }
501
502 #[test]
503 fn test_signature_appearance() {
504 let field = SignatureField::new("sig1");
505 let appearance = field.generate_appearance(200.0, 50.0);
506
507 assert!(appearance.is_ok());
508 let stream = appearance.unwrap();
509 assert!(!stream.is_empty());
510 }
511
512 #[test]
513 fn test_lock_fields() {
514 let field = SignatureField::new("sig1")
515 .lock_fields_after_signing(vec!["field1".to_string(), "field2".to_string()]);
516
517 assert_eq!(field.lock_fields.len(), 2);
518 }
519
520 #[test]
521 fn test_required_field() {
522 let field = SignatureField::new("sig1").required();
523 assert!(field.required);
524
525 let dict = field.to_dict();
526 if let Some(Object::Integer(flags)) = dict.get("Ff") {
528 assert_eq!(flags & 2, 2);
529 }
530 }
531}