use serde::{Deserialize, Serialize};
use crate::{NormaxisPdfError, Result};
#[derive(Debug, Clone)]
pub struct SignatureOptions {
pub reason: String,
pub location: String,
pub reserved_bytes: usize,
}
impl Default for SignatureOptions {
fn default() -> Self {
Self {
reason: String::from("Assinado digitalmente"),
location: String::from("Portugal"),
reserved_bytes: 8192,
}
}
}
pub struct PreparedPdf {
pub(crate) bytes: Vec<u8>,
pub(crate) contents_start: usize,
pub(crate) reserved_bytes: usize,
}
impl PreparedPdf {
pub fn raw_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn byte_range(&self) -> (u64, u64, u64, u64) {
let a = self.contents_start as u64;
let b = a + 1 + self.reserved_bytes as u64 * 2 + 1; (0, a, b, self.bytes.len() as u64 - b)
}
pub fn bytes_to_sign(&self) -> Vec<u8> {
let (_, r1_len, r2_start, r2_len) = self.byte_range();
let r1 = &self.bytes[..r1_len as usize];
let r2 = &self.bytes[r2_start as usize..r2_start as usize + r2_len as usize];
[r1, r2].concat()
}
pub fn embed_signature(mut self, pkcs7_der: &[u8]) -> Result<Vec<u8>> {
if pkcs7_der.len() > self.reserved_bytes {
return Err(NormaxisPdfError::RenderError(format!(
"PKCS#7 blob ({} B) exceeds reserved space ({} B)",
pkcs7_der.len(),
self.reserved_bytes,
)));
}
let hex: String = pkcs7_der.iter().map(|b| format!("{b:02x}")).collect();
let pos = self.contents_start + 1; self.bytes[pos..pos + hex.len()].copy_from_slice(hex.as_bytes());
let fill_end = pos + self.reserved_bytes * 2;
self.bytes[pos + hex.len()..fill_end].fill(b'0');
Ok(self.bytes)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignatureField {
pub x_mm: f64,
pub y_mm: f64,
pub width_mm: f64,
pub height_mm: f64,
pub page: u32,
pub label: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SignatureConfig {
pub field: Option<SignatureField>,
pub reason: Option<String>,
pub location: Option<String>,
pub reserved_bytes: Option<usize>,
}
impl SignatureConfig {
pub fn to_options(&self) -> SignatureOptions {
SignatureOptions {
reason: self.reason.clone().unwrap_or_else(|| "Assinado digitalmente".into()),
location: self.location.clone().unwrap_or_else(|| "Portugal".into()),
reserved_bytes: self.reserved_bytes.unwrap_or(8192),
}
}
}
pub fn sign_pdf(prepared: PreparedPdf, _config: &SignatureConfig, pkcs7_der: &[u8]) -> Result<Vec<u8>> {
prepared.embed_signature(pkcs7_der)
}
pub(crate) fn extract_prepared(
mut bytes: Vec<u8>,
reserved_bytes: usize,
) -> Result<PreparedPdf> {
let needle: &[u8] = b"/Contents <8080";
let search_pos = bytes
.windows(needle.len())
.position(|w| w == needle)
.ok_or_else(|| {
NormaxisPdfError::RenderError(
"signature preparation failed: /Contents placeholder not found".into(),
)
})?;
let contents_start = search_pos + b"/Contents ".len();
let close_pos = contents_start + 1 + reserved_bytes * 2;
if close_pos >= bytes.len() || bytes[close_pos] != b'>' {
return Err(NormaxisPdfError::RenderError(
"signature preparation failed: /Contents closing '>' not at expected position".into(),
));
}
let a = contents_start as u64; let b = a + 1 + reserved_bytes as u64 * 2 + 1; let range2_len = bytes.len() as u64 - b;
let br_pattern: &[u8] = b"[0 1111111111 1222222222 1333333333]";
let br_pos = bytes
.windows(br_pattern.len())
.position(|w| w == br_pattern)
.ok_or_else(|| {
NormaxisPdfError::RenderError(
"signature preparation failed: /ByteRange placeholder not found".into(),
)
})?;
let patch = format!("[0 {:<10} {:<10} {:<10}]", a, b, range2_len);
debug_assert_eq!(
patch.len(),
br_pattern.len(),
"ByteRange patch length mismatch"
);
bytes[br_pos..br_pos + br_pattern.len()].copy_from_slice(patch.as_bytes());
Ok(PreparedPdf {
bytes,
contents_start,
reserved_bytes,
})
}