mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-28 03:58:45 -06:00
* M7: a moved text box's drag preview shows the box alone (#4464) While an added text box or picture was dragged, the pixels that followed the pointer were cut from the page raster inside the item's box, so whatever the box covered at its first place (a filled shape, a picture, other text) was carried along with it. The drag now renders the item alone too: a copy of the document whose page keeps only the item's own content stream and no annotations, drawn on a transparent page (`pdfcraft_edit::isolate_content`, `Document::added_content_alone`). Those pixels follow the pointer over the page rendered without the item, so what it covered stays put. The preview shows once both rasters are in; until then the item stays where it is, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC * M7: drop the move preview's earlier rasters when a re-render fails (#4464) When the scale changed after both the page-without-item and item-alone rasters were in, and a render at the new scale failed, only the renderers were cleared. The old textures stayed, so the item kept following the pointer from them although the preview had given up. A failure now clears both texture slots too, so the item stays where it is while dragged. The receive step moves into its own method so a test can drive it with a pool whose renders always fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
106 lines
5.0 KiB
Rust
106 lines
5.0 KiB
Rust
//! The page beneath an inline text editor, with the source glyphs removed on a COS clone.
|
|
|
|
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() })
|
|
})?
|
|
}
|
|
|
|
/// 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() } })
|
|
})?
|
|
}
|
|
}
|