use super::{Arc, EditError, Hidden, SignatureInfo, TrustStore, optimize}; /// A change to an existing page image. #[derive(Clone, Debug, PartialEq)] pub enum ImageEdit { /// Move and resize it to this box (user space). Move([f64; 4]), /// Quarter turns clockwise about its centre. Rotate(i32), /// Any angle in degrees clockwise about its centre (the unit is degrees, not quarter turns). RotateBy(f64), Flip { horizontal: bool, }, /// Show only this box of it (user space), the pixels staying where they are. Crop([f64; 4]), /// Draw this image file (PNG, JPEG, TIFF, GIF, BMP, JPEG 2000) in its place. Replace { name: String, bytes: Arc>, }, Delete, } /// Forms ▸ Merge data files into spreadsheet: the field values of each file (FDF, XFDF or a /// filled-in PDF form), as CSV with one row per file. pub fn merge_data_files(files: &[(String, Vec)]) -> Result { let rows = files.iter().map(|(name, bytes)| pdfcraft_xfdf::data_values(bytes).map_err(|e| format!("{name}: {e}"))).collect::, _>>()?; Ok(pdfcraft_xfdf::merge_csv(&rows)) } /// One file produced by a split: (1-based first page, last page, PDF bytes). pub type SplitPart = (usize, usize, Arc>); /// Stable identifier of an open document within a session. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct DocId(pub u64); /// The highest resolution a document that allows only low-resolution printing (Table 22, bit 12 /// clear) is printed at. pub(crate) const LOW_RESOLUTION_PRINT_DPI: f64 = 150.0; pub(crate) fn render_threads() -> usize { std::thread::available_parallelism().map(std::num::NonZeroUsize::get).unwrap_or(4).clamp(2, 8) - 1 } /// A document's working file captured for crash recovery. #[derive(Clone, Debug)] pub struct RecoverySnapshot { pub doc: DocId, pub name: String, pub path: Option, pub bytes: Arc>, /// The snapshot is encrypted (recovering it asks for the password again). pub encrypted: bool, } /// The recovered user password of an R2–R4 file as text for the renderer, which tries a password /// as its UTF-8 bytes and then in PDFDocEncoding: UTF-8 bytes (as some writers store passwords /// PDFDocEncoding can't hold, such as "şifre") stay UTF-8, and other bytes are read as /// PDFDocEncoding, so either way the renderer gets back exactly these bytes. pub(crate) fn renderer_password(bytes: &[u8]) -> String { match std::str::from_utf8(bytes) { Ok(text) => text.to_owned(), Err(_) => bytes.iter().map(|b| pdfcraft_cos::pdfdoc_char(*b)).collect(), } } /// The document as it may be written to a file (#3926, #3944): a copy without the values of text /// fields with the Password flag (or XFA `` fields in the datasets), or `cos` /// itself when it holds none. ISO 32000-2 §12.7.5.3, Table 231 (a note to the Password flag): /// "it is imperative that PDF processors never store the value of the text field in the PDF file /// if this flag is set". The open document keeps them in memory. pub(crate) fn writable(cos: &pdfcraft_cos::Document) -> Result, EditError> { let has_form_pw = pdfcraft_forms::has_password_values(cos); let has_xfa_pw = pdfcraft_xfa::has_password_values(cos); if !has_form_pw && !has_xfa_pw { return Ok(std::borrow::Cow::Borrowed(cos)); } let mut copy = cos.clone(); if has_form_pw { pdfcraft_forms::clear_password_values(&mut copy).map_err(|e| EditError::Write(e.to_string()))?; } if has_xfa_pw { pdfcraft_xfa::clear_password_values(&mut copy).map_err(|e| EditError::Write(e.to_string()))?; } Ok(std::borrow::Cow::Owned(copy)) } /// The last-resort guard (AGENTS.md §4): run `f`, turning a panic that escapes it into an error /// message, so one bad file or edit can't take the app and its other documents down. It is a /// safety net for bugs, not a substitute for returning errors. pub fn guard(f: impl FnOnce() -> T) -> Result { std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).map_err(|p| { p.downcast_ref::<&str>().map(|s| (*s).to_string()).or_else(|| p.downcast_ref::().cloned()).unwrap_or_else(|| "unknown error".into()) }) } /// Validate the signature fields of `cos` (written as `bytes`). pub(crate) fn signatures_of( cos: &pdfcraft_cos::Document, bytes: &[u8], trust: &TrustStore, cache: &pdfcraft_sign::DigestCache, ) -> Vec { pdfcraft_sign::pdf::list_cached(cos, bytes, trust, cache) } /// What Optimize PDF did. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct OptimizeReport { pub optimize: optimize::Report, /// Identical objects merged. pub merged: usize, /// Remove Hidden Information categories discarded, with counts. pub discarded: Vec<(Hidden, usize)>, }