diff --git a/src/lib.rs b/src/lib.rs index 13eb5ff..9bc05c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,6 +115,34 @@ impl Clipboard { self.set().html(html, alt_text) } + /// Places RTF data onto the clipboard. + /// + /// This method writes raw Rich Text Format bytes to the clipboard, under the + /// `public.rtf` type on macOS and equivalently on other platforms. The input is + /// expected to be valid UTF-8 or ASCII-compatible RTF data. + /// + /// # Errors + /// + /// Returns an error if the data failed to be stored on the clipboard. + #[cfg(target_os = "macos")] + pub fn set_rtf<'a, T: Into>>(&mut self, data: T) -> Result<(), Error> { + self.set().rtf(data) + } + + /// Places RTFD data onto the clipboard. + /// + /// This method writes Rich Text Format Directory (RTFD) content to the clipboard. + /// RTFD is similar to RTF but can include embedded resources such as images or + /// attachments, and is only meaningfully supported on macOS. + /// + /// # Errors + /// + /// Returns an error if the data failed to be stored on the clipboard. + #[cfg(target_os = "macos")] + pub fn set_rtfd<'a, T: Into>>(&mut self, data: T) -> Result<(), Error> { + self.set().rtfd(data) + } + /// Fetches image data from the clipboard, and returns the decoded pixels. /// /// Any image data placed on the clipboard with `set_image` will be possible read back, using @@ -235,6 +263,35 @@ impl Set<'_> { self.platform.html(html, alt_text) } + /// Completes the "set" operation by placing RTF data onto the clipboard. + /// + /// The RTF data must be provided as raw bytes, encoded in a way that is valid + /// according to the Rich Text Format specification. + /// + /// # Errors + /// + /// Returns an error if the data failed to be stored on the clipboard. + #[cfg(target_os = "macos")] + pub fn rtf<'a, T: Into>>(self, data: T) -> Result<(), Error> { + let data = data.into(); + self.platform.rtf(data) + } + + /// Completes the "set" operation by placing RTFD data onto the clipboard. + /// + /// RTFD (Rich Text Format Directory) is similar to RTF, but can also contain + /// embedded attachments such as images. On macOS, this type is represented by + /// `com.apple.rtfd`. + /// + /// # Errors + /// + /// Returns an error if the data failed to be stored on the clipboard. + #[cfg(target_os = "macos")] + pub fn rtfd<'a, T: Into>>(self, data: T) -> Result<(), Error> { + let data = data.into(); + self.platform.rtfd(data) + } + /// Completes the "set" operation by placing an image onto the clipboard. /// /// The chosen output format, depending on the platform is the following: diff --git a/src/platform/osx.rs b/src/platform/osx.rs index 31952b6..a2d60f4 100644 --- a/src/platform/osx.rs +++ b/src/platform/osx.rs @@ -18,10 +18,10 @@ use objc2::{ ClassType, }; use objc2_app_kit::{ - NSPasteboard, NSPasteboardTypeHTML, NSPasteboardTypeString, - NSPasteboardURLReadingFileURLsOnlyKey, + NSPasteboard, NSPasteboardTypeHTML, NSPasteboardTypeRTF, NSPasteboardTypeRTFD, + NSPasteboardTypeString, NSPasteboardURLReadingFileURLsOnlyKey, }; -use objc2_foundation::{ns_string, NSArray, NSDictionary, NSNumber, NSString, NSURL}; +use objc2_foundation::{ns_string, NSArray, NSData, NSDictionary, NSNumber, NSString, NSURL}; use std::{ borrow::Cow, panic::{RefUnwindSafe, UnwindSafe}, @@ -339,6 +339,40 @@ impl<'clipboard> Set<'clipboard> { } } + pub(crate) fn rtf(self, data: Cow<'_, [u8]>) -> Result<(), Error> { + self.clipboard.clear(); + + let rtf_data = NSData::with_bytes(data.as_ref()); + let success = unsafe { + self.clipboard.pasteboard.setData_forType(Some(&rtf_data), NSPasteboardTypeRTF) + }; + + add_clipboard_exclusions(self.clipboard, self.exclude_from_history); + + if success { + Ok(()) + } else { + Err(Error::unknown("NSPasteboard#setData_forType(RTF): returned false")) + } + } + + pub(crate) fn rtfd(self, data: Cow<'_, [u8]>) -> Result<(), Error> { + self.clipboard.clear(); + + let rtfd_data = NSData::with_bytes(data.as_ref()); + let success = unsafe { + self.clipboard.pasteboard.setData_forType(Some(&rtfd_data), NSPasteboardTypeRTFD) + }; + + add_clipboard_exclusions(self.clipboard, self.exclude_from_history); + + if success { + Ok(()) + } else { + Err(Error::unknown("NSPasteboard#setData_forType(RTFD): returned false")) + } + } + #[cfg(feature = "image-data")] pub(crate) fn image(self, data: ImageData) -> Result<(), Error> { let pixels = data.bytes.into();