1#[cfg(target_os = "windows")]
2use windows::Win32::Foundation::HANDLE;
3
4#[cfg(test)]
5mod tests {
6 use super::*;
7
8 #[test]
9 #[cfg(target_os = "linux")]
10 fn test_write_to_device_linux() {
11 let payload = b"^FDhello world";
12 let result = write_to_device("/dev/usb/lp0", payload, Some("Test Document"));
13 assert!(result.is_ok());
14 }
15
16 #[test]
17 #[cfg(target_os = "windows")]
18 fn test_write_to_device_windows() {
19 let payload = b"^FDhello world";
20 let result = write_to_device("ZDesigner ZD220-203dpi ZPL",
21 payload,
22 Some("Test Document"),
23 );
24 assert!(result.is_ok());
25 }
26}
27
28#[cfg(target_os = "linux")]
46pub fn write_to_device(
47 printer: &str,
48 payload: &[u8],
49 _document_name: Option<&str>,
50) -> Result<usize, std::io::Error> {
51 use std::fs::OpenOptions;
52 use std::io::Write;
53
54 let mut device = OpenOptions::new().write(true).open(printer)?;
55 let bytes_written = device.write(payload)?;
56 device.flush()?; Ok(bytes_written)
58}
59
60#[cfg(target_os = "windows")]
61pub fn write_to_device(
62 printer: &str,
63 payload: &[u8],
64 document_name: Option<&str>,
65) -> Result<usize, std::io::Error> {
66 use std::ffi::CString;
67 use std::ptr;
68 use windows::core::PCSTR;
69 use windows::Win32::Graphics::Printing::{
70 EndDocPrinter, EndPagePrinter, OpenPrinterA, StartDocPrinterA, StartPagePrinter,
71 WritePrinter, DOC_INFO_1A, PRINTER_ACCESS_USE, PRINTER_DEFAULTSA,
72 };
73
74 let printer_name = CString::new(printer)
75 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
76
77 let mut printer_handle: HANDLE = HANDLE(std::ptr::null_mut());
78
79 unsafe {
81 let pd = PRINTER_DEFAULTSA {
82 pDatatype: windows::core::PSTR(ptr::null_mut()),
83 pDevMode: ptr::null_mut(),
84 DesiredAccess: PRINTER_ACCESS_USE,
85 };
86
87 if OpenPrinterA(
88 PCSTR(printer_name.as_ptr() as *const u8),
89 &mut printer_handle,
90 Some(&pd),
91 )
92 .is_err()
93 {
94 return Err(std::io::Error::new(
95 std::io::ErrorKind::NotFound,
96 format!("Failed to open printer: {}", printer),
97 ));
98 }
99
100 let _cleanup = PrinterGuard::new(printer_handle);
102
103 let doc_name = document_name.unwrap_or("RAW_Print");
104 let doc_name_cstring = CString::new(doc_name)
105 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
106
107 let datatype_cstring = CString::new("RAW").unwrap();
108
109 let doc_info = DOC_INFO_1A {
110 pDocName: windows::core::PSTR(doc_name_cstring.as_ptr() as *mut u8),
111 pOutputFile: windows::core::PSTR::null(),
112 pDatatype: windows::core::PSTR(datatype_cstring.as_ptr() as *mut u8),
113 };
114
115 let job_id = StartDocPrinterA(printer_handle, 1, &doc_info as *const _ as _);
117 if job_id == 0 {
118 return Err(std::io::Error::new(
119 std::io::ErrorKind::Other,
120 "Failed to start document",
121 ));
122 }
123
124 if !StartPagePrinter(printer_handle).as_bool() {
126 let _ = EndDocPrinter(printer_handle);
127 return Err(std::io::Error::new(
128 std::io::ErrorKind::Other,
129 "Failed to start page",
130 ));
131 }
132
133 let buffer = payload;
134 let mut bytes_written: u32 = 0;
135
136 let write_result = WritePrinter(
137 printer_handle,
138 buffer.as_ptr() as _,
139 buffer.len() as u32,
140 &mut bytes_written,
141 );
142
143 let _ = EndPagePrinter(printer_handle);
145 let _ = EndDocPrinter(printer_handle);
146
147 if !write_result.as_bool() {
148 return Err(std::io::Error::new(
149 std::io::ErrorKind::Other,
150 "Failed to write to printer",
151 ));
152 }
153
154 Ok(bytes_written as usize)
155 }
156}
157
158#[cfg(target_os = "windows")]
159struct PrinterGuard {
160 handle: HANDLE,
161}
162
163#[cfg(target_os = "windows")]
164impl PrinterGuard {
165 fn new(handle: HANDLE) -> Self {
166 Self { handle }
167 }
168}
169
170#[cfg(target_os = "windows")]
171impl Drop for PrinterGuard {
172 fn drop(&mut self) {
173 unsafe {
174 use windows::Win32::Graphics::Printing::ClosePrinter;
175 let _ = ClosePrinter(self.handle);
176 }
177 }
178}