mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-12 05:22:58 +00:00
Update Background, Update Watermark and Update Header and Footer previewed the new marks on the page's thumbnail, which still has the marks Update replaces. A pale yellow background updated to light blue showed a muddy teal (the blue multiplied onto the yellow), and a new watermark or header was drawn over the old one. The applied result was right; only the preview was wrong. In Update, the preview now draws the page without its marks of that kind. The engine removes them on a copy of the document (Document::page_without_marks, read-only like the other preview layers), and the dialog renders that page off the UI thread at the preview's size, showing a blank sheet until it arrives. Add, and pages without marks of that kind, keep the thumbnail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
6.1 KiB
Rust
125 lines
6.1 KiB
Rust
//! The page beneath an inline text editor, with the source glyphs removed on a COS clone; and,
|
|
//! the same way, the page beneath a dragged added item or the marks an Update replaces.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use pdfcraft_render::{PageRenderer, RenderConfig, RenderPool, RenderRequest, RenderedPage};
|
|
|
|
use crate::{Document, guard};
|
|
|
|
pub struct TextEditPreview {
|
|
page: usize,
|
|
bytes: Arc<Vec<u8>>,
|
|
config: RenderConfig,
|
|
}
|
|
|
|
impl TextEditPreview {
|
|
pub fn renderer(&self) -> RenderPool {
|
|
RenderPool::new(self.bytes.clone(), 1, self.config.clone())
|
|
}
|
|
|
|
pub fn render_background(&self, scale: f32) -> Result<RenderedPage, String> {
|
|
if !scale.is_finite() || scale <= 0.0 {
|
|
return Err("preview scale must be finite and positive".into());
|
|
}
|
|
guard(|| {
|
|
let mut renderer = PageRenderer::new(self.bytes.clone(), self.config.clone());
|
|
let out = renderer.render(RenderRequest { page: self.page, scale, ..Default::default() });
|
|
if let Some(e) = &out.error {
|
|
return Err(e.clone());
|
|
}
|
|
Ok(out)
|
|
})?
|
|
}
|
|
}
|
|
|
|
impl Document {
|
|
/// Background for retyping an added text item, without changing its saved content.
|
|
pub fn added_text_preview(&self, page: usize, index: usize) -> Result<TextEditPreview, String> {
|
|
guard(|| {
|
|
if !self.added().iter().filter(|a| a.page == page).nth(index).is_some_and(|a| matches!(a.content, crate::AddedContent::Text(_))) {
|
|
return Err("the selected added item is not text".into());
|
|
}
|
|
let editor = self.editor.as_ref().ok_or("the document can't be read")?;
|
|
let mut cos = editor.cos.clone();
|
|
pdfcraft_edit::delete_content(&mut cos, page, index).map_err(|e| e.to_string())?;
|
|
let bytes = Arc::new(pdfcraft_cos::write_incremental(&cos, &pdfcraft_cos::SaveOptions::default()).map_err(|e| e.to_string())?);
|
|
Ok(TextEditPreview { page, bytes, config: self.config.clone() })
|
|
})?
|
|
}
|
|
|
|
/// Measure a draft without changing bytes, dirty state, or undo history.
|
|
pub fn text_edit_layout(
|
|
&self,
|
|
page: usize,
|
|
block: usize,
|
|
text: &str,
|
|
style: &pdfcraft_edit::BlockStyle,
|
|
) -> Result<pdfcraft_edit::text::TextEditLayout, String> {
|
|
guard(|| {
|
|
let editor = self.editor.as_ref().ok_or("the document can't be read")?;
|
|
pdfcraft_edit::text::text_edit_layout(&editor.cos, page, block, text, style).map_err(|e| e.to_string())
|
|
})?
|
|
}
|
|
|
|
/// Embedded source faces for a text editor, bounded and validated as sfnt programs.
|
|
/// This is read-only; no source font is installed or exported.
|
|
pub fn text_editor_fonts(&self, page: usize) -> Vec<pdfcraft_edit::text::TextEditorFont> {
|
|
self.editor.as_ref().and_then(|e| pdfcraft_edit::text::text_editor_fonts(&e.cos, page).ok()).unwrap_or_default()
|
|
}
|
|
|
|
/// Preparing the background never changes working bytes, dirty state or undo history.
|
|
pub fn text_edit_preview(&self, page: usize, block: usize) -> Result<TextEditPreview, String> {
|
|
guard(|| {
|
|
let editor = self.editor.as_ref().ok_or("the document can't be read")?;
|
|
let mut cos = editor.cos.clone();
|
|
pdfcraft_edit::clear_block(&mut cos, page, block).map_err(|e| e.to_string())?;
|
|
let bytes = Arc::new(pdfcraft_cos::write_incremental(&cos, &pdfcraft_cos::SaveOptions::default()).map_err(|e| e.to_string())?);
|
|
Ok(TextEditPreview { page, bytes, config: self.config.clone() })
|
|
})?
|
|
}
|
|
|
|
/// The page beneath an added item while it is dragged: `index` counts the page's added
|
|
/// items, as `Edit::DeleteContent` does. Read-only, like [`Self::text_edit_preview`].
|
|
pub fn added_content_preview(&self, page: usize, index: usize) -> Result<TextEditPreview, String> {
|
|
guard(|| {
|
|
let editor = self.editor.as_ref().ok_or("the document can't be read")?;
|
|
let mut cos = editor.cos.clone();
|
|
pdfcraft_edit::delete_content(&mut cos, page, index).map_err(|e| e.to_string())?;
|
|
let bytes = Arc::new(pdfcraft_cos::write_incremental(&cos, &pdfcraft_cos::SaveOptions::default()).map_err(|e| e.to_string())?);
|
|
Ok(TextEditPreview { page, bytes, config: self.config.clone() })
|
|
})?
|
|
}
|
|
|
|
/// The page without its marks of `kind`: what Update replaces them on, so its preview doesn't
|
|
/// draw the new marks over the old ones (or tint the old background). `None` when the page has
|
|
/// none, and its thumbnail already is that page. Read-only, like [`Self::text_edit_preview`].
|
|
pub fn page_without_marks(&self, page: usize, kind: crate::MarkKind) -> Result<Option<TextEditPreview>, String> {
|
|
if !self.marks().contains(&kind) {
|
|
return Ok(None);
|
|
}
|
|
guard(|| {
|
|
let editor = self.editor.as_ref().ok_or("the document can't be read")?;
|
|
let mut cos = editor.cos.clone();
|
|
if pdfcraft_edit::remove_marks(&mut cos, &[page], kind).map_err(|e| e.to_string())? == 0 {
|
|
return Ok(None);
|
|
}
|
|
let bytes = Arc::new(pdfcraft_cos::write_incremental(&cos, &pdfcraft_cos::SaveOptions::default()).map_err(|e| e.to_string())?);
|
|
Ok(Some(TextEditPreview { page, bytes, config: self.config.clone() }))
|
|
})?
|
|
}
|
|
|
|
/// An added item alone, on a transparent page: what follows the pointer while it is dragged,
|
|
/// so the page under its first place doesn't come along. Read-only, like
|
|
/// [`Self::added_content_preview`].
|
|
pub fn added_content_alone(&self, page: usize, index: usize) -> Result<TextEditPreview, String> {
|
|
guard(|| {
|
|
let editor = self.editor.as_ref().ok_or("the document can't be read")?;
|
|
let mut cos = editor.cos.clone();
|
|
pdfcraft_edit::isolate_content(&mut cos, page, index).map_err(|e| e.to_string())?;
|
|
let bytes = Arc::new(pdfcraft_cos::write_incremental(&cos, &pdfcraft_cos::SaveOptions::default()).map_err(|e| e.to_string())?);
|
|
Ok(TextEditPreview { page, bytes, config: RenderConfig { transparent: true, ..self.config.clone() } })
|
|
})?
|
|
}
|
|
}
|