//! Other files to combine with or copy pages from: opening them, and why one can't be used. use std::sync::Arc; use crate::{EditError, SourceProblem}; /// A file to combine: its name (the bookmark title), bytes, and page range (`None`: all). pub type CombineSource = (String, Arc>, Option); /// What a combine produced: the new PDF bytes, and the fields renamed to keep colliding names /// separate (old fully-qualified name → new one), in order across the sources. #[derive(Debug)] pub struct Combined { pub bytes: Arc>, pub renamed_fields: Vec<(String, String)>, } /// Parse another PDF to copy pages from. pub(crate) fn open_source(name: &str, bytes: &Arc>) -> Result { open_source_with(name, bytes, None) } /// [`open_source`], authenticating with `password` (user or owner) for an encrypted file. pub(crate) fn open_source_with(name: &str, bytes: &Arc>, password: Option<&str>) -> Result { source_document(bytes, password).map_err(|p| EditError::Source(format!("{name}: {p}"))) } /// Import the comments of `src` onto the pages of `doc` with the same numbers. pub(crate) fn import_pdf_comments(doc: &mut pdfcraft_cos::Document, src: &pdfcraft_cos::Document) -> Result<(), EditError> { if pdfcraft_organize::import_comments(doc, src)? == 0 { return Err(pdfcraft_xfdf::DataError::NothingImported.into()); } Ok(()) } /// Parse another PDF to import comments from, whatever its permissions allow. pub(crate) fn comment_source(bytes: &Arc>) -> Result { match std::panic::catch_unwind(|| pdfcraft_cos::Document::open(bytes.clone())) { Ok(Ok(d)) => Ok(d), Ok(Err(pdfcraft_cos::CosError::NeedsPassword)) => Err(SourceProblem::Password), Ok(Err(e)) => Err(SourceProblem::Unreadable(e.to_string())), Err(_) => Err(SourceProblem::Unreadable("the file could not be read".into())), } } fn source_document(bytes: &Arc>, password: Option<&str>) -> Result { match std::panic::catch_unwind(|| pdfcraft_cos::Document::open_with_password(bytes.clone(), password)) { Ok(Ok(d)) if d.permissions().is_some_and(|p| !p.assemble()) => Err(SourceProblem::NotPermitted), Ok(Ok(d)) => Ok(d), Ok(Err(pdfcraft_cos::CosError::NeedsPassword)) => Err(SourceProblem::Password), Ok(Err(pdfcraft_cos::CosError::WrongPassword)) => Err(SourceProblem::WrongPassword), Ok(Err(e)) => Err(SourceProblem::Unreadable(e.to_string())), Err(_) => Err(SourceProblem::Unreadable("the file could not be read".into())), } } /// Whether `bytes` can be combined, opened with `password` (user or owner) if given, and if not /// why: what Combine would refuse it for, so the Combine files list can say so before Combine /// is pressed. The owner (permissions) password lifts the restriction on copying pages. pub fn combine_source_check(bytes: &Arc>, password: Option<&str>) -> Result<(), SourceProblem> { source_document(bytes, password).map(|_| ()) } /// The number of pages of `bytes`, opened with `password` (user or owner) if given, whatever /// its permissions allow; `None` when it can't be read. pub fn source_page_count(bytes: &Arc>, password: Option<&str>) -> Option { let doc = std::panic::catch_unwind(|| pdfcraft_cos::Document::open_with_password(bytes.clone(), password)).ok()?.ok()?; pdfcraft_organize::page_count(&doc).ok() }