clipboard_win_html/
lib.rs1use std::fmt;
27use std::ffi::CString;
28use windows::Win32::{
29 Foundation::{HANDLE, HGLOBAL},
30 System::Memory::{GlobalAlloc, GlobalLock, GlobalUnlock, GMEM_MOVEABLE},
31};
32
33#[derive(Debug)]
35pub enum Error {
36 HtmlTemplateCreationError,
37 OpenClipboardError,
38 EmptyClipboardError,
39 SetClipboardError,
40 CloseClipboardError,
41 MemoryAllocationError,
42}
43
44impl fmt::Display for Error {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 match self {
47 Error::HtmlTemplateCreationError => write!(f, "Failed to create HTML template."),
48 Error::OpenClipboardError => write!(f, "Failed to open clipboard."),
49 Error::EmptyClipboardError => write!(f, "Failed to empty clipboard."),
50 Error::SetClipboardError => write!(f, "Failed to set clipboard."),
51 Error::CloseClipboardError => write!(f, "Failed to close clipboard."),
52 Error::MemoryAllocationError => write!(f, "Failed to allocate memory."),
53 }
54 }
55}
56
57pub fn set_clipboard_html(html: String) -> Result<(), Error> {
63 let fragment = html;
65
66 let start_html = 391;
67 let start_fragment = 454;
68 let start_selection = Some(start_fragment);
69
70 let end_fragment = start_fragment + fragment.len() - 1;
71 let end_selection = Some(end_fragment);
72 let end_html = end_fragment + 37;
74
75 let mut document = String::new();
76
77 document.push_str(format!("Version:{}\n", "0.9").as_str());
81
82 document.push_str(format!("StartHTML:{:0>50}\n", start_html).as_str());
84 document.push_str(format!("EndHTML:{:0>50}\n", end_html).as_str());
86
87 document.push_str(format!("StartFragment:{:0>50}\n", start_fragment).as_str());
89 document.push_str(format!("EndFragment:{:0>50}\n", end_fragment).as_str());
91
92 if let (Some(start_selection), Some(end_selection)) = (start_selection, end_selection) {
93 document.push_str(format!("StartSelection:{:0>50}\n", start_selection).as_str());
95 document.push_str(format!("EndSelection:{:0>50}\n", end_selection).as_str());
97 }
98
99 document.push_str(
102 r#"<!DOCTYPE>
103<HTML>
104<HEAD>
105</HEAD>
106<BODY>
107<!-- StartFragment -->
108"#,
109 );
110 document.push_str(&fragment);
111 document.push_str(
112 r#"
113<!-- EndFragment -->
114</BODY>
115</HTML>"#,
116 );
117
118 let cstring = CString::new(document)
119 .map_err(|_| Error::HtmlTemplateCreationError)?
120 .into_bytes_with_nul();
121
122 #[allow(non_snake_case)]
124 let CF_HTML;
125 unsafe {
126 let format_name: Vec<u16> = "HTML Format\0".encode_utf16().collect();
130 let pcwstr = windows::core::PCWSTR(format_name.as_ptr());
131 let uint = windows::Win32::System::DataExchange::RegisterClipboardFormatW(pcwstr);
133 CF_HTML = uint;
134 }
135
136
137 unsafe {
144 windows::Win32::System::DataExchange::OpenClipboard(None)
145 .map_err(|_| Error::OpenClipboardError)?;
146 }
147
148 unsafe {
150 windows::Win32::System::DataExchange::EmptyClipboard()
151 .map_err(|_| Error::EmptyClipboardError)?;
152 }
153
154 unsafe {
156 let mem_alloc: HGLOBAL =
157 GlobalAlloc(GMEM_MOVEABLE, cstring.len() * std::mem::size_of::<u16>())
158 .map_err(|_| Error::MemoryAllocationError)?;
159 let mem_lock = GlobalLock(mem_alloc);
160 std::ptr::copy_nonoverlapping(cstring.as_ptr(), mem_lock as *mut u8, cstring.len());
161 let _ = GlobalUnlock(mem_alloc);
162 let handle = HANDLE(mem_alloc.0 as isize);
163
164 if windows::Win32::System::DataExchange::SetClipboardData(CF_HTML, handle).is_err() {
165 panic!("Failed to set clipboard.");
166 }
167 }
168
169 unsafe {
171 windows::Win32::System::DataExchange::CloseClipboard()
172 .map_err(|_| Error::CloseClipboardError)?;
173 }
174
175 Ok(())
176}