printwell-pdf 0.1.3

PDF manipulation features (forms, signing) for Printwell
Documentation
//! PDF encryption support.
//!
//! This module provides password protection and encryption for PDF documents.
//!
//! **Note:** This feature requires a commercial license.
//! Purchase at: <https://printwell.dev/pricing>

use crate::{EncryptionError, Result};
use typed_builder::TypedBuilder;

/// PDF permission flags (32-bit)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Permissions(u32);

impl Permissions {
    /// Print the document
    pub const PRINT: Self = Self(1 << 2);
    /// Modify the document contents
    pub const MODIFY: Self = Self(1 << 3);
    /// Copy or extract text and graphics
    pub const COPY: Self = Self(1 << 4);
    /// Add or modify annotations
    pub const ANNOTATE: Self = Self(1 << 5);
    /// Fill in form fields
    pub const FILL_FORMS: Self = Self(1 << 8);
    /// Extract text for accessibility
    pub const EXTRACT_ACCESSIBILITY: Self = Self(1 << 9);
    /// Assemble document (insert, rotate, delete pages)
    pub const ASSEMBLE: Self = Self(1 << 10);
    /// Print in high quality
    pub const PRINT_HIGH_QUALITY: Self = Self(1 << 11);

    /// All permissions enabled
    pub const ALL: Self = Self(0xFFFF_FFFC);

    /// No permissions (maximum restriction)
    pub const NONE: Self = Self(0xFFFF_F0C0);

    /// Create permissions from raw value
    #[must_use]
    pub const fn from_bits(bits: u32) -> Self {
        Self(bits)
    }

    /// Get raw permission bits
    #[must_use]
    pub const fn bits(&self) -> u32 {
        self.0
    }
}

impl std::ops::BitOr for Permissions {
    type Output = Self;
    fn bitor(self, rhs: Self) -> Self {
        Self(self.0 | rhs.0)
    }
}

impl std::ops::BitAnd for Permissions {
    type Output = Self;
    fn bitand(self, rhs: Self) -> Self {
        Self(self.0 & rhs.0)
    }
}

/// Encryption algorithm to use
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum EncryptionAlgorithm {
    /// AES-256 encryption (recommended, PDF 2.0)
    #[default]
    Aes256,
    /// AES-128 encryption (PDF 1.6+)
    Aes128,
    /// RC4 128-bit (legacy compatibility, PDF 1.4+)
    Rc4_128,
}

/// Options for PDF encryption
#[derive(Debug, Clone, TypedBuilder)]
pub struct EncryptionOptions {
    /// Owner password (required) - allows changing permissions
    #[builder(setter(into))]
    pub owner_password: String,

    /// User password (optional) - required to open document
    #[builder(default, setter(into, strip_option))]
    pub user_password: Option<String>,

    /// Permissions to grant
    #[builder(default = Permissions::ALL)]
    pub permissions: Permissions,

    /// Encryption algorithm
    #[builder(default)]
    pub algorithm: EncryptionAlgorithm,

    /// Encrypt metadata (default: true)
    #[builder(default = true)]
    pub encrypt_metadata: bool,
}

/// Result of PDF encryption
#[derive(Debug)]
pub struct EncryptedPdf {
    /// The encrypted PDF data
    pub data: Vec<u8>,
}

impl EncryptedPdf {
    /// Write to file
    ///
    /// # Errors
    ///
    /// Returns an error if the file cannot be written.
    pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
        std::fs::write(path, &self.data)?;
        Ok(())
    }

    /// Get the encrypted PDF bytes
    #[must_use]
    pub fn as_bytes(&self) -> &[u8] {
        &self.data
    }

    /// Consume and return the encrypted PDF bytes
    #[must_use]
    pub fn into_bytes(self) -> Vec<u8> {
        self.data
    }
}

/// Encrypt a PDF document with password protection.
///
/// **Note:** This feature requires a commercial license.
///
/// # Errors
///
/// Always returns an error as this feature requires a commercial license.
pub fn encrypt_pdf(_pdf_data: &[u8], _options: &EncryptionOptions) -> Result<EncryptedPdf> {
    Err(EncryptionError::RequiresLicense.into())
}

/// Decrypt a password-protected PDF document.
///
/// **Note:** This feature requires a commercial license.
///
/// # Errors
///
/// Always returns an error as this feature requires a commercial license.
pub fn decrypt_pdf(_pdf_data: &[u8], _password: &str) -> Result<Vec<u8>> {
    Err(EncryptionError::RequiresLicense.into())
}