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