mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 19:48:14 -06:00
While a handle of a Fill & Sign line, check, cross, dot, typed signature or comment stamp was dragged, only the selection frame followed the pointer; the stamp kept its old size (or place) until release. Image signatures already had a live layer. Every stamp now gets one: the page without the stamp, and the stamp drawn alone on a transparent page, whose pixels are stretched with the frame. Resizing a stamp only changes its /Rect, which its appearance is stretched to fit, so the preview is what the release produces. - render: RenderConfig.transparent clears the page to transparent. - annot: isolate_annotation leaves a page showing one annotation alone. - engine: Document::stamp_preview builds the two read-only layers; the page beneath gets the appearances drawn for comments without one. - ui-egui: the signature drag layer covers every stamp, rendering the stamp's own tile sharp at the current zoom.
84 lines
3.8 KiB
Rust
84 lines
3.8 KiB
Rust
//! 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<Vec<u8>>,
|
|
stamp: Arc<Vec<u8>>,
|
|
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<RenderedPage, String> {
|
|
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<RenderedPage, String> {
|
|
self.render(&self.stamp, self.stamp_config(), scale)
|
|
}
|
|
|
|
fn stamp_config(&self) -> RenderConfig {
|
|
RenderConfig { transparent: true, ..self.config.clone() }
|
|
}
|
|
|
|
fn render(&self, bytes: &Arc<Vec<u8>>, config: RenderConfig, scale: f32) -> Result<RenderedPage, String> {
|
|
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<Option<StampPreview>, 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() }))
|
|
})?
|
|
}
|
|
}
|