From 74921cc610e4a147247db8738906d057e941df96 Mon Sep 17 00:00:00 2001 From: lucieleblanc Date: Thu, 18 Jan 2024 15:00:56 -0500 Subject: [PATCH 1/4] start adding get_html to X11 --- examples/get_html.rs | 9 +++++++++ examples/hello_html.rs | 15 +++++++++++++++ src/common.rs | 16 ++++++++++++++++ src/lib.rs | 11 +++++++++++ src/platform/linux/mod.rs | 10 ++++++++++ src/platform/linux/x11.rs | 30 +++++++++++++++++++++++++++++- 6 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 examples/get_html.rs create mode 100644 examples/hello_html.rs diff --git a/examples/get_html.rs b/examples/get_html.rs new file mode 100644 index 0000000..a4b3424 --- /dev/null +++ b/examples/get_html.rs @@ -0,0 +1,9 @@ +use arboard::Clipboard; + +fn main() { + let mut ctx = Clipboard::new().unwrap(); + + let html_data = ctx.get_html().unwrap(); + + println!("HTML data is:\n{:?}\nAlt text is:\n{:?}", html_data.html, html_data.alt_text); +} \ No newline at end of file diff --git a/examples/hello_html.rs b/examples/hello_html.rs new file mode 100644 index 0000000..71a52bc --- /dev/null +++ b/examples/hello_html.rs @@ -0,0 +1,15 @@ +use arboard::Clipboard; + +fn main() { + env_logger::init(); + let mut clipboard = Clipboard::new().unwrap(); + println!("Clipboard text was: {:?}", clipboard.get_html()); + + let html = "

Hello, world!

This is HTML.

"; + let alt_text = "Hello, world!\nThis is HTML."; + clipboard.set_html(html, Some(alt_text)).unwrap(); + println!( + "But now the clipboard text should be this HTML: \"{}\" with this alternate text: \"{}\"", + html, alt_text + ); +} \ No newline at end of file diff --git a/src/common.rs b/src/common.rs index f115c16..e61b636 100644 --- a/src/common.rs +++ b/src/common.rs @@ -146,6 +146,22 @@ impl<'a> ImageData<'a> { } } +#[derive(Debug, Default)] +pub struct HTMLData { + pub html: String, + pub alt_text: String, +} + +impl HTMLData { + pub fn from_html(html: String) -> Self { + Self { html, ..Default::default() } + } + + pub fn from_alt_text(alt_text: String) -> Self { + Self { alt_text, ..Default::default() } + } +} + #[cfg(any(windows, all(unix, not(target_os = "macos"))))] pub(crate) struct ScopeGuard { callback: Option, diff --git a/src/lib.rs b/src/lib.rs index 48c5750..26e5d6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ mod common; use std::borrow::Cow; pub use common::Error; +use common::HTMLData; #[cfg(feature = "image-data")] pub use common::ImageData; @@ -78,6 +79,11 @@ impl Clipboard { self.set().text(text) } + /// Fetches HTML from the clipboard and returns it. + pub fn get_html(&mut self) -> Result { + self.get().html() + } + /// Places the HTML as well as a plain-text alternative onto the clipboard. /// /// Any valid UTF-8 string is accepted. @@ -145,6 +151,11 @@ impl Get<'_> { self.platform.text() } + /// Completes the "get" operation by fetching HTML data from the clipboard. + pub fn html(self) -> Result { + self.platform.html() + } + /// Completes the "get" operation by fetching image data from the clipboard and returning the /// decoded pixels. /// diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 5bb28f5..7a6d1bf 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,5 +1,7 @@ use std::borrow::Cow; +use crate::HTMLData; + #[cfg(feature = "wayland-data-control")] use log::{trace, warn}; @@ -114,6 +116,14 @@ impl<'clipboard> Get<'clipboard> { } } + pub(crate) fn html(self) -> Result { + match self.clipboard { + Clipboard::X11(clipboard) => clipboard.get_html(self.selection), + #[cfg(feature = "wayland-data-control")] + Clipboard::WlDataControl(clipboard) => Err("not implemented"), + } + } + #[cfg(feature = "image-data")] pub(crate) fn image(self) -> Result, Error> { match self.clipboard { diff --git a/src/platform/linux/x11.rs b/src/platform/linux/x11.rs index 8f3750c..e118594 100644 --- a/src/platform/linux/x11.rs +++ b/src/platform/linux/x11.rs @@ -48,7 +48,10 @@ use super::encode_as_png; use super::{into_unknown, LinuxClipboardKind}; #[cfg(feature = "image-data")] use crate::ImageData; -use crate::{common::ScopeGuard, Error}; +use crate::{ + common::{HTMLData, ScopeGuard}, + Error, +}; type Result = std::result::Result; @@ -884,6 +887,31 @@ impl Clipboard { self.inner.write(data, selection, wait) } + pub(crate) fn get_html(&self, selection: LinuxClipboardKind) -> Result { + let html_formats = [ + self.inner.atoms.HTML, + self.inner.atoms.UTF8_STRING, + self.inner.atoms.UTF8_MIME_0, + self.inner.atoms.UTF8_MIME_1, + self.inner.atoms.STRING, + self.inner.atoms.TEXT, + ]; + let result = self.inner.read(&html_formats, selection)?; + + if result.format == self.inner.atoms.HTML { + log::info!("result format and bytes: {:?}", result); + let html: String = result.bytes.into_iter().map(|c| c as char).collect(); + let mut data = HTMLData::from_html(html.clone()); + // TODO: remove HTML tags when getting + data.alt_text = String::from("Raw HTML: ") + html.as_str(); + Ok(data) + } else { + log::info!("didn't find HTML mime type, falling back to alt text"); + let alt_text = String::from_utf8(result.bytes).map_err(|_| Error::ConversionFailure)?; + Ok(HTMLData::from_alt_text(alt_text)) + } + } + pub(crate) fn set_html( &self, html: Cow<'_, str>, From 2200efd9dd5da102f71c83416458b35565bc4123 Mon Sep 17 00:00:00 2001 From: lucieleblanc Date: Thu, 18 Jan 2024 17:13:20 -0500 Subject: [PATCH 2/4] add logging for data blob --- src/platform/linux/x11.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/linux/x11.rs b/src/platform/linux/x11.rs index e118594..a57fa6f 100644 --- a/src/platform/linux/x11.rs +++ b/src/platform/linux/x11.rs @@ -902,7 +902,7 @@ impl Clipboard { log::info!("result format and bytes: {:?}", result); let html: String = result.bytes.into_iter().map(|c| c as char).collect(); let mut data = HTMLData::from_html(html.clone()); - // TODO: remove HTML tags when getting + // TODO: remove HTML tags when getting data.alt_text = String::from("Raw HTML: ") + html.as_str(); Ok(data) } else { @@ -920,16 +920,19 @@ impl Clipboard { wait: bool, ) -> Result<()> { let mut data = vec![]; + log::info!("started data blob"); if let Some(alt_text) = alt { data.push(ClipboardData { bytes: alt_text.into_owned().into_bytes(), format: self.inner.atoms.UTF8_STRING, }); + log::info!("pushed alt text to data blob, result: {:?}", data); } data.push(ClipboardData { bytes: html.into_owned().into_bytes(), format: self.inner.atoms.HTML, }); + log::info!("pushed HTML to data blob, result: {:?}", data); self.inner.write(data, selection, wait) } From 3792fec9799f2aff4e458678c1b2d997fa816a5e Mon Sep 17 00:00:00 2001 From: lucieleblanc Date: Fri, 19 Jan 2024 16:08:42 -0500 Subject: [PATCH 3/4] separate searching for alt text vs. HTML --- src/platform/linux/x11.rs | 44 ++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/platform/linux/x11.rs b/src/platform/linux/x11.rs index a57fa6f..4cde471 100644 --- a/src/platform/linux/x11.rs +++ b/src/platform/linux/x11.rs @@ -888,28 +888,42 @@ impl Clipboard { } pub(crate) fn get_html(&self, selection: LinuxClipboardKind) -> Result { - let html_formats = [ - self.inner.atoms.HTML, + let html_format = [self.inner.atoms.HTML]; + let alt_text_formats = [ self.inner.atoms.UTF8_STRING, self.inner.atoms.UTF8_MIME_0, self.inner.atoms.UTF8_MIME_1, self.inner.atoms.STRING, self.inner.atoms.TEXT, + self.inner.atoms.TEXT_MIME_UNKNOWN, ]; - let result = self.inner.read(&html_formats, selection)?; - - if result.format == self.inner.atoms.HTML { - log::info!("result format and bytes: {:?}", result); - let html: String = result.bytes.into_iter().map(|c| c as char).collect(); - let mut data = HTMLData::from_html(html.clone()); - // TODO: remove HTML tags when getting - data.alt_text = String::from("Raw HTML: ") + html.as_str(); - Ok(data) - } else { - log::info!("didn't find HTML mime type, falling back to alt text"); - let alt_text = String::from_utf8(result.bytes).map_err(|_| Error::ConversionFailure)?; - Ok(HTMLData::from_alt_text(alt_text)) + + let mut data = HTMLData::default(); + + log::info!("attempting to read HTML"); + let html_result = self.inner.read(&html_format, selection); + if let Ok(html_data) = html_result { + log::info!("HTML result format and bytes: {:?}", html_data); + let html: String = html_data.bytes.into_iter().map(|c| c as char).collect(); + data.html = html; } + + log::info!("attempting to read alt text"); + let alt_text_result = self.inner.read(&alt_text_formats, selection); + if let Ok(alt_text_data) = alt_text_result { + log::info!("alt text format and bytes: {:?}", alt_text_data); + data.alt_text = if alt_text_data.format == self.inner.atoms.STRING { + log::info!("reading alt text as string"); + // ISO Latin-1 + // See: https://stackoverflow.com/questions/28169745/what-are-the-options-to-convert-iso-8859-1-latin-1-to-a-string-utf-8 + alt_text_data.bytes.into_iter().map(|c| c as char).collect::() + } else { + log::info!("converting alt text from bytes"); + String::from_utf8(alt_text_data.bytes).map_err(|_| Error::ConversionFailure)? + }; + } + + Ok(data) } pub(crate) fn set_html( From f9e0a08f8b2b7dc955623a8eb0d0e8a475a1b391 Mon Sep 17 00:00:00 2001 From: lucieleblanc Date: Fri, 19 Jan 2024 18:18:48 -0500 Subject: [PATCH 4/4] remove log lines --- src/platform/linux/x11.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/platform/linux/x11.rs b/src/platform/linux/x11.rs index 4cde471..df39306 100644 --- a/src/platform/linux/x11.rs +++ b/src/platform/linux/x11.rs @@ -900,25 +900,19 @@ impl Clipboard { let mut data = HTMLData::default(); - log::info!("attempting to read HTML"); let html_result = self.inner.read(&html_format, selection); if let Ok(html_data) = html_result { - log::info!("HTML result format and bytes: {:?}", html_data); let html: String = html_data.bytes.into_iter().map(|c| c as char).collect(); data.html = html; } - log::info!("attempting to read alt text"); let alt_text_result = self.inner.read(&alt_text_formats, selection); if let Ok(alt_text_data) = alt_text_result { - log::info!("alt text format and bytes: {:?}", alt_text_data); data.alt_text = if alt_text_data.format == self.inner.atoms.STRING { - log::info!("reading alt text as string"); // ISO Latin-1 // See: https://stackoverflow.com/questions/28169745/what-are-the-options-to-convert-iso-8859-1-latin-1-to-a-string-utf-8 alt_text_data.bytes.into_iter().map(|c| c as char).collect::() } else { - log::info!("converting alt text from bytes"); String::from_utf8(alt_text_data.bytes).map_err(|_| Error::ConversionFailure)? }; } @@ -934,19 +928,16 @@ impl Clipboard { wait: bool, ) -> Result<()> { let mut data = vec![]; - log::info!("started data blob"); if let Some(alt_text) = alt { data.push(ClipboardData { bytes: alt_text.into_owned().into_bytes(), format: self.inner.atoms.UTF8_STRING, }); - log::info!("pushed alt text to data blob, result: {:?}", data); } data.push(ClipboardData { bytes: html.into_owned().into_bytes(), format: self.inner.atoms.HTML, }); - log::info!("pushed HTML to data blob, result: {:?}", data); self.inner.write(data, selection, wait) }