#1089: an image copied in a browser pastes as the picture, also from its markup
A browser's Copy Image puts the picture on the clipboard with text next to it. Paste already took the picture when that text was only the image's address (#597), but some browsers (seen on Fedora with KDE) put the image's HTML there instead, `<img src="…" class="…" style="…">`, which isn't an address, so the markup was pasted as type. names_a_picture also takes markup whose only content is <img> elements (no text outside its tags) as naming the picture, and Paste then reads the bitmap. Markup with words in it, and an address alone, still paste as text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c7d29e5b92
commit
8c69173bb7
File diff suppressed because one or more lines are too long
|
|
@ -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, is_address};
|
||||
pub use flavours::{BITMAP, EMF, FILE_HEAD, Flavour, PASTE_ORDER, PDF, PNG, SVG, TEXT, file_flavour, is_address, names_a_picture};
|
||||
pub(crate) use resources::SwatchChoices;
|
||||
|
||||
use super::*;
|
||||
|
|
@ -248,6 +248,24 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// The image markup some browsers put as text next to a copied picture names it too (#1089);
|
||||
/// markup with words in it is text.
|
||||
#[test]
|
||||
fn markup_of_only_images_names_a_picture() {
|
||||
for m in [
|
||||
r#"<img src="https://static.example.com/eagle.png" jsaction="" class="sFlh5c" style="max-width: 351px" alt="">"#,
|
||||
r#"<meta http-equiv="content-type" content="text/html; charset=utf-8"><img src="a.png">"#,
|
||||
"<html><body><!--StartFragment--><IMG SRC=\"a.png\"/><!--EndFragment--></body></html>",
|
||||
r#"<a href="https://x.org"><img src="a.png"></a>"#,
|
||||
"https://example.com/cat.png",
|
||||
] {
|
||||
assert!(names_a_picture(m), "{m}");
|
||||
}
|
||||
for t in ["<p>Hello</p>", r#"<img src="a.png"> and a caption"#, "<b>bold</b>", "<imgx>", "<img", "plain words", "<br>", ""] {
|
||||
assert!(!names_a_picture(t), "{t}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_exports_svg_that_round_trips_through_paste() {
|
||||
let mut s = session();
|
||||
|
|
|
|||
|
|
@ -39,6 +39,30 @@ pub fn is_address(text: &str) -> bool {
|
|||
!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())
|
||||
}
|
||||
|
||||
/// Is `text` only a reference to a picture: an address ([`is_address`]), or markup that shows
|
||||
/// nothing but images (`<img …>`)? A browser's Copy Image puts one of them next to the picture
|
||||
/// (some put the image's HTML there as text, #1089): then the picture is what Paste takes.
|
||||
pub fn names_a_picture(text: &str) -> bool {
|
||||
is_address(text) || only_images(text)
|
||||
}
|
||||
|
||||
/// Is `html` markup whose only content is `<img>` elements: no text outside its tags (whitespace
|
||||
/// between them aside), and at least one image?
|
||||
fn only_images(html: &str) -> bool {
|
||||
let mut images = 0;
|
||||
let mut rest = html.trim();
|
||||
while !rest.is_empty() {
|
||||
let Some(tag) = rest.strip_prefix('<') else { return false };
|
||||
let Some(end) = tag.find('>') else { return false };
|
||||
let name = tag.get(..end).unwrap_or_default().split(|c: char| c.is_whitespace() || c == '/').next().unwrap_or_default();
|
||||
if name.eq_ignore_ascii_case("img") {
|
||||
images += 1;
|
||||
}
|
||||
rest = tag.get(end + 1..).unwrap_or_default().trim_start();
|
||||
}
|
||||
images > 0
|
||||
}
|
||||
|
||||
/// 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::{BITMAP, EMF, Flavour, PASTE_ORDER, PDF, SVG, TEXT, is_address, looks_like_svg};
|
||||
use vectorcraft_engine::cmd::clipboard::{BITMAP, EMF, Flavour, PASTE_ORDER, PDF, SVG, TEXT, looks_like_svg, names_a_picture};
|
||||
|
||||
use crate::VectorcraftApp;
|
||||
|
||||
|
|
@ -90,8 +90,9 @@ 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 };
|
||||
// Text that only names a picture (its address, or its image markup) goes with a
|
||||
// browser's copied picture: the picture.
|
||||
let f = if f.mime == TEXT && names_a_picture(&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.
|
||||
|
|
|
|||
|
|
@ -168,7 +168,8 @@ fn text_from_another_app_pastes_as_point_text_and_svg_text_as_art() {
|
|||
}
|
||||
|
||||
/// 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).
|
||||
/// read), or its markup as text. The picture is pasted; an address alone pastes as text, nothing
|
||||
/// is fetched (#597, #1089).
|
||||
#[test]
|
||||
fn an_image_copied_in_a_browser_pastes_as_the_picture_not_its_address() {
|
||||
let (mut app, board) = app();
|
||||
|
|
@ -177,6 +178,11 @@ fn an_image_copied_in_a_browser_pastes_as_the_picture_not_its_address() {
|
|||
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));
|
||||
// Some browsers (on Linux) put the image's markup there as its text: the picture still (#1089).
|
||||
let markup = br#"<img src="https://example.com/images/cat.png" jsaction="" class="sFlh5c" style="max-width: 351px" alt="">"#.to_vec();
|
||||
copy_elsewhere(&board, vec![(TEXT, markup), (PNG, blue_png())]);
|
||||
run(&mut app, "edit.paste", json!({}));
|
||||
assert!(matches!(pasted(&app)[0].kind, NodeKind::Image(_)), "{:?}", pasted(&app)[0].kind);
|
||||
copy_elsewhere(&board, vec![(TEXT, url)]);
|
||||
run(&mut app, "edit.paste", json!({}));
|
||||
let NodeKind::Text(t) = &pasted(&app)[0].kind else { panic!("not type") };
|
||||
|
|
|
|||
Loading…
Reference in New Issue