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 {
255 match bg_color {
256 Color::Rgb(r, g, b) => {
257 stream.extend(format!("{} {} {} rg\n", r, g, b).as_bytes());
258 }
259 Color::Gray(v) => {
260 stream.extend(format!("{} g\n", v).as_bytes());
261 }
262 Color::Cmyk(c, m, y, k) => {
263 stream.extend(format!("{} {} {} {} k\n", c, m, y, k).as_bytes());
264 }
265 }
266 stream.extend(format!("0 0 {} {} re f\n", width, height).as_bytes());
267 }
268
269 match self.appearance.border_color {
271 Color::Rgb(r, g, b) => {
272 stream.extend(format!("{} {} {} RG\n", r, g, b).as_bytes());
273 }
274 Color::Gray(v) => {
275 stream.extend(format!("{} G\n", v).as_bytes());
276 }
277 Color::Cmyk(c, m, y, k) => {
278 stream.extend(format!("{} {} {} {} K\n", c, m, y, k).as_bytes());
279 }
280 }
281 stream.extend(format!("{} w\n", self.appearance.border_width).as_bytes());
282 stream.extend(format!("0 0 {} {} re S\n", width, height).as_bytes());
283
284 stream.extend(b"BT\n");
286 stream.extend(
287 format!(
288 "/{} {} Tf\n",
289 self.appearance.font.pdf_name(),
290 self.appearance.font_size
291 )
292 .as_bytes(),
293 );
294 match self.appearance.text_color {
295 Color::Rgb(r, g, b) => {
296 stream.extend(format!("{} {} {} rg\n", r, g, b).as_bytes());
297 }
298 Color::Gray(v) => {
299 stream.extend(format!("{} g\n", v).as_bytes());
300 }
301 Color::Cmyk(c, m, y, k) => {
302 stream.extend(format!("{} {} {} {} k\n", c, m, y, k).as_bytes());
303 }
304 }
305
306 let mut y_pos = height - self.appearance.font_size - 5.0;
307 let x_pos = 5.0;
308
309 if self.is_signed() {
310 if let Some(ref signer) = self.signer {
312 if self.appearance.show_name {
313 let label = if self.appearance.show_labels {
314 "Digitally signed by: "
315 } else {
316 ""
317 };
318 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
319 stream.extend(format!("({}{}) Tj\n", label, signer.name).as_bytes());
320 y_pos -= self.appearance.font_size + 2.0;
321 }
322
323 if self.appearance.show_dn {
324 if let Some(ref dn) = signer.distinguished_name {
325 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
326 stream.extend(format!("(DN: {}) Tj\n", dn).as_bytes());
327 y_pos -= self.appearance.font_size + 2.0;
328 }
329 }
330 }
331
332 if self.appearance.show_date {
333 if let Some(ref sig_value) = self.signature_value {
334 let label = if self.appearance.show_labels {
335 "Date: "
336 } else {
337 ""
338 };
339 let date_str = sig_value
340 .timestamp
341 .format("%Y-%m-%d %H:%M:%S UTC")
342 .to_string();
343 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
344 stream.extend(format!("({}{}) Tj\n", label, date_str).as_bytes());
345 y_pos -= self.appearance.font_size + 2.0;
346 }
347 }
348
349 if self.appearance.show_reason {
350 if let Some(ref reason) = self.reason {
351 let label = if self.appearance.show_labels {
352 "Reason: "
353 } else {
354 ""
355 };
356 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
357 stream.extend(format!("({}{}) Tj\n", label, reason).as_bytes());
358 y_pos -= self.appearance.font_size + 2.0;
359 }
360 }
361
362 if self.appearance.show_location {
363 if let Some(ref location) = self.location {
364 let label = if self.appearance.show_labels {
365 "Location: "
366 } else {
367 ""
368 };
369 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
370 stream.extend(format!("({}{}) Tj\n", label, location).as_bytes());
371 }
372 }
373 } else {
374 stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
376 stream.extend(b"(Click to sign) Tj\n");
377 }
378
379 stream.extend(b"ET\n");
380
381 Ok(stream)
382 }
383
384 pub fn to_dict(&self) -> Dictionary {
386 let mut dict = Dictionary::new();
387
388 dict.set("Type", Object::Name("Annot".to_string()));
389 dict.set("Subtype", Object::Name("Widget".to_string()));
390 dict.set("FT", Object::Name("Sig".to_string()));
391 dict.set("T", Object::String(self.name.clone()));
392
393 let mut flags = 0;
395 if self.required {
396 flags |= 2; }
398 dict.set("Ff", Object::Integer(flags));
399
400 if self.is_signed() {
402 let mut sig_dict = Dictionary::new();
403 sig_dict.set("Type", Object::Name("Sig".to_string()));
404
405 if let Some(ref signer) = self.signer {
406 sig_dict.set("Name", Object::String(signer.name.clone()));
407 if let Some(ref email) = signer.email {
408 sig_dict.set("ContactInfo", Object::String(email.clone()));
409 }
410 }
411
412 if let Some(ref reason) = self.reason {
413 sig_dict.set("Reason", Object::String(reason.clone()));
414 }
415
416 if let Some(ref location) = self.location {
417 sig_dict.set("Location", Object::String(location.clone()));
418 }
419
420 if let Some(ref sig_value) = self.signature_value {
421 sig_dict.set(
422 "M",
423 Object::String(sig_value.timestamp.format("%Y%m%d%H%M%S%z").to_string()),
424 );
425 }
426
427 dict.set("V", Object::Dictionary(sig_dict));
428 }
429
430 if !self.lock_fields.is_empty() {
432 let mut lock_dict = Dictionary::new();
433 lock_dict.set("Type", Object::Name("SigFieldLock".to_string()));
434
435 let fields: Vec<Object> = self
436 .lock_fields
437 .iter()
438 .map(|f| Object::String(f.clone()))
439 .collect();
440 lock_dict.set("Fields", Object::Array(fields));
441
442 dict.set("Lock", Object::Dictionary(lock_dict));
443 }
444
445 dict
446 }
447}
448
449impl SignerInfo {
450 pub fn new(name: impl Into<String>) -> Self {
452 Self {
453 name: name.into(),
454 distinguished_name: None,
455 email: None,
456 organization: None,
457 organizational_unit: None,
458 }
459 }
460
461 pub fn with_email(mut self, email: impl Into<String>) -> Self {
463 self.email = Some(email.into());
464 self
465 }
466
467 pub fn with_organization(mut self, org: impl Into<String>) -> Self {
469 self.organization = Some(org.into());
470 self
471 }
472
473 pub fn build_dn(&mut self) {
475 let mut dn_parts = vec![format!("CN={}", self.name)];
476
477 if let Some(ref email) = self.email {
478 dn_parts.push(format!("emailAddress={}", email));
479 }
480
481 if let Some(ref org) = self.organization {
482 dn_parts.push(format!("O={}", org));
483 }
484
485 if let Some(ref ou) = self.organizational_unit {
486 dn_parts.push(format!("OU={}", ou));
487 }
488
489 self.distinguished_name = Some(dn_parts.join(", "));
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn test_signature_field_creation() {
499 let field = SignatureField::new("sig1");
500 assert_eq!(field.name, "sig1");
501 assert!(!field.is_signed());
502 assert!(!field.required);
503 }
504
505 #[test]
506 fn test_signer_info() {
507 let mut signer = SignerInfo::new("John Doe")
508 .with_email("john@example.com")
509 .with_organization("ACME Corp");
510
511 signer.build_dn();
512 assert!(signer.distinguished_name.is_some());
513 assert!(signer.distinguished_name.unwrap().contains("CN=John Doe"));
514 }
515
516 #[test]
517 fn test_sign_field() {
518 let mut field = SignatureField::new("sig1");
519 let signer = SignerInfo::new("Jane Smith");
520
521 assert!(field
522 .sign(signer.clone(), Some("Approval".to_string()))
523 .is_ok());
524 assert!(field.is_signed());
525 assert_eq!(field.reason, Some("Approval".to_string()));
526
527 assert!(field.sign(signer, None).is_err());
529 }
530
531 #[test]
532 fn test_signature_appearance() {
533 let field = SignatureField::new("sig1");
534 let appearance = field.generate_appearance(200.0, 50.0);
535
536 assert!(appearance.is_ok());
537 let stream = appearance.unwrap();
538 assert!(!stream.is_empty());
539 }
540
541 #[test]
542 fn test_lock_fields() {
543 let field = SignatureField::new("sig1")
544 .lock_fields_after_signing(vec!["field1".to_string(), "field2".to_string()]);
545
546 assert_eq!(field.lock_fields.len(), 2);
547 }
548
549 #[test]
550 fn test_required_field() {
551 let field = SignatureField::new("sig1").required();
552 assert!(field.required);
553
554 let dict = field.to_dict();
555 if let Some(Object::Integer(flags)) = dict.get("Ff") {
557 assert_eq!(flags & 2, 2);
558 }
559 }
560}