//! A stamp while it is moved or resized: the page without it, and the stamp alone. use std::sync::Arc; use pdfcraft_cos::{SaveOptions, write_incremental}; use pdfcraft_render::{PageRenderer, RenderConfig, RenderPool, RenderRequest, RenderedPage}; use crate::{Document, guard}; /// Read-only layers for interactive movement and resizing: the page without the stamp, and the /// stamp alone on a transparent page. Moving or resizing a stamp only changes its `/Rect`, which /// its appearance is stretched to fit, so these two show the result before it is applied. /// Preparing them never changes the working bytes, dirty flag or undo history. pub struct StampPreview { page: usize, background: Arc>, stamp: Arc>, config: RenderConfig, } impl StampPreview { /// One worker for the page beneath the stamp. pub fn background_renderer(&self) -> RenderPool { RenderPool::new(self.background.clone(), 1, self.config.clone()) } /// One worker for the stamp alone: its page has no content, no other annotations and no /// background, so only the stamp's own pixels are drawn. pub fn stamp_renderer(&self) -> RenderPool { RenderPool::new(self.stamp.clone(), 1, self.stamp_config()) } /// The page beneath the stamp, rendered synchronously with the renderer's normal pixel limits. pub fn render_background(&self, scale: f32) -> Result { self.render(&self.background, self.config.clone(), scale) } /// The stamp alone on a transparent page, rendered like [`Self::render_background`]. pub fn render_stamp(&self, scale: f32) -> Result { self.render(&self.stamp, self.stamp_config(), scale) } fn stamp_config(&self) -> RenderConfig { RenderConfig { transparent: true, ..self.config.clone() } } fn render(&self, bytes: &Arc>, config: RenderConfig, scale: f32) -> Result { if !scale.is_finite() || scale <= 0.0 { return Err("preview scale must be finite and positive".into()); } guard(|| { let out = PageRenderer::new(bytes.clone(), config).render(RenderRequest { page: self.page, scale, ..Default::default() }); if let Some(e) = &out.error { return Err(e.clone()); } Ok(out) })? } } impl Document { /// The layers that show the stamp at `(page, index)` moved or resized as it is dragged. /// `None` for an annotation that isn't a stamp, or one without an appearance to draw. pub fn stamp_preview(&self, page: usize, index: usize) -> Result, String> { guard(|| { let editor = self.editor.as_ref().ok_or("the document can't be read")?; let is_stamp = self.info.annotations.iter().any(|a| a.page == page && a.index == index && a.subtype == "Stamp"); if !is_stamp { return Ok(None); } let mut stamp = editor.cos.clone(); if !pdfcraft_annot::isolate_annotation(&mut stamp, page, index).map_err(|e| e.to_string())? { return Ok(None); } let mut background = editor.cos.clone(); pdfcraft_annot::delete_annotation(&mut background, page, index).map_err(|e| e.to_string())?; // The page beneath as it is displayed: comments without an appearance get theirs. let background = pdfcraft_annot::with_missing_appearances(&background).unwrap_or(background); let write = |cos: &pdfcraft_cos::Document| write_incremental(cos, &SaveOptions::default()).map(Arc::new).map_err(|e| e.to_string()); Ok(Some(StampPreview { page, background: write(&background)?, stamp: write(&stamp)?, config: self.config.clone() })) })? } }