Land #616 (fix #597: a browser-copied image pastes as the picture)
This commit is contained in:
commit
e037064b2d
File diff suppressed because one or more lines are too long
|
|
@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use vectorcraft_engine::Session;
|
||||
use vectorcraft_engine::cmd::clipboard::{FILE_HEAD, Flavour, PASTE_ORDER, file_flavour};
|
||||
use vectorcraft_engine::cmd::clipboard::{FILE_HEAD, Flavour, PASTE_ORDER, file_flavour, is_address};
|
||||
use vectorcraft_engine::cmd::fileio;
|
||||
use vectorcraft_engine::cmd::recovery::{self, Hold, RecoveryStore};
|
||||
use vectorcraft_ui_egui::graphics::GraphicsLoss;
|
||||
|
|
@ -200,7 +200,8 @@ fn track_paste(pasted: &Rc<RefCell<Pasted>>) {
|
|||
let on_paste = wasm_bindgen::closure::Closure::<dyn FnMut(web_sys::ClipboardEvent)>::new(move |e: web_sys::ClipboardEvent| {
|
||||
let Some(data) = e.clipboard_data() else { return };
|
||||
let Some(files) = data.files().filter(|f| f.length() > 0) else { return };
|
||||
if data.get_data("text").is_ok_and(|t| !t.is_empty()) {
|
||||
// Text is egui's, but for the address a browser's Copy Image puts next to the picture.
|
||||
if data.get_data("text").is_ok_and(|t| !t.is_empty() && !is_address(&t)) {
|
||||
return;
|
||||
}
|
||||
e.prevent_default();
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ use vectorcraft_color::Swatch;
|
|||
use vectorcraft_doc::{Document, GraphicStyle, ImageBlob, Node, NodeId, PatternDef, Symbol, TextStyleDef};
|
||||
use vectorcraft_geom::Rect;
|
||||
|
||||
pub use flavours::{BITMAP, EMF, FILE_HEAD, Flavour, PASTE_ORDER, PDF, PNG, SVG, TEXT, file_flavour};
|
||||
pub use flavours::{BITMAP, EMF, FILE_HEAD, Flavour, PASTE_ORDER, PDF, PNG, SVG, TEXT, file_flavour, is_address};
|
||||
pub(crate) use resources::SwatchChoices;
|
||||
|
||||
use super::*;
|
||||
|
|
@ -220,6 +220,25 @@ mod tests {
|
|||
s
|
||||
}
|
||||
|
||||
/// The address a browser's Copy Image puts next to the picture (#597), and text that isn't one.
|
||||
#[test]
|
||||
fn an_address_alone_is_told_from_other_text() {
|
||||
for a in [
|
||||
"https://example.com/cat.png",
|
||||
" http://x.org/a?b=1
|
||||
",
|
||||
"HTTPS://EXAMPLE.COM",
|
||||
"file:///C:/art/cat.png",
|
||||
"data:image/png;base64,iVBOR",
|
||||
"blob:https://x.org/1",
|
||||
] {
|
||||
assert!(is_address(a), "{a}");
|
||||
}
|
||||
for t in ["Pasted words", "see https://example.com", "https://a.org https://b.org", "https://", "example.com/cat.png", ""] {
|
||||
assert!(!is_address(t), "{t}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_exports_svg_that_round_trips_through_paste() {
|
||||
let mut s = session();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,15 @@ pub const EMF: &str = "image/emf";
|
|||
/// What Paste reads from other apps, best first: vector art before text, text before bitmaps
|
||||
/// (word processors offer a picture of copied text too).
|
||||
pub const PASTE_ORDER: [&str; 5] = [SVG, PDF, EMF, TEXT, BITMAP];
|
||||
/// Is `text` only an address (a web page's, an image's, a file's)? A browser's Copy Image puts the
|
||||
/// image's address as text next to the picture: then the picture is what Paste takes (#597). An
|
||||
/// address alone pastes as text, and nothing is fetched.
|
||||
pub fn is_address(text: &str) -> bool {
|
||||
const SCHEMES: [&str; 6] = ["http://", "https://", "file://", "ftp://", "data:", "blob:"];
|
||||
let t = text.trim();
|
||||
!t.chars().any(char::is_whitespace) && SCHEMES.iter().any(|s| t.get(..s.len()).is_some_and(|h| h.eq_ignore_ascii_case(s)) && t.len() > s.len())
|
||||
}
|
||||
|
||||
/// How much of a copied file [`file_flavour`] needs to tell what it is.
|
||||
pub const FILE_HEAD: usize = 4096;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
//! reads from a paste itself ([`VectorcraftApp::paste_from_host`]).
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_engine::cmd::clipboard::{EMF, Flavour, PASTE_ORDER, PDF, SVG, TEXT, looks_like_svg};
|
||||
use vectorcraft_engine::cmd::clipboard::{BITMAP, EMF, Flavour, PASTE_ORDER, PDF, SVG, TEXT, is_address, looks_like_svg};
|
||||
|
||||
use crate::VectorcraftApp;
|
||||
|
||||
|
|
@ -90,6 +90,8 @@ impl VectorcraftApp {
|
|||
return Ok(());
|
||||
}
|
||||
let Some(f) = cb.read(&PASTE_ORDER) else { return Ok(()) };
|
||||
// Text that is only an address goes with a browser's copied picture: the picture.
|
||||
let f = if f.mime == TEXT && is_address(&String::from_utf8_lossy(&f.data)) { cb.read(&[BITMAP]).unwrap_or(f) } else { f };
|
||||
import_command(&f, center)
|
||||
}
|
||||
// Text only: SVG markup is art, other text is left alone.
|
||||
|
|
|
|||
|
|
@ -167,6 +167,22 @@ fn text_from_another_app_pastes_as_point_text_and_svg_text_as_art() {
|
|||
assert_eq!(count(&app), before);
|
||||
}
|
||||
|
||||
/// A browser's Copy Image: the picture with its address as text (and HTML, which Paste doesn't
|
||||
/// read). The picture is pasted; an address alone pastes as text, nothing is fetched (#597).
|
||||
#[test]
|
||||
fn an_image_copied_in_a_browser_pastes_as_the_picture_not_its_address() {
|
||||
let (mut app, board) = app();
|
||||
let url = b"https://example.com/images/cat.png".to_vec();
|
||||
copy_elsewhere(&board, vec![(TEXT, url.clone()), ("text/html", b"<img src=\"https://example.com/images/cat.png\">".to_vec()), (PNG, blue_png())]);
|
||||
run(&mut app, "edit.paste", json!({}));
|
||||
let NodeKind::Image(im) = &pasted(&app)[0].kind else { panic!("not an image: {:?}", pasted(&app)[0].kind) };
|
||||
assert!(im.link.is_none() && (im.width, im.height) == (4, 2));
|
||||
copy_elsewhere(&board, vec![(TEXT, url)]);
|
||||
run(&mut app, "edit.paste", json!({}));
|
||||
let NodeKind::Text(t) = &pasted(&app)[0].kind else { panic!("not type") };
|
||||
assert_eq!(t.plain_text(), "https://example.com/images/cat.png");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_pdf_from_another_app_pastes_as_vectors_ahead_of_its_bitmap() {
|
||||
let (mut app, board) = app();
|
||||
|
|
|
|||
Loading…
Reference in New Issue