1+ #[ cfg( target_os = "windows" ) ]
2+ use windows:: Win32 :: Foundation :: HANDLE ;
3+
14#[ cfg( test) ]
25mod tests {
36 use super :: * ;
@@ -12,7 +15,11 @@ mod tests {
1215 #[ test]
1316 #[ cfg( target_os = "windows" ) ]
1417 fn test_write_to_device_windows ( ) {
15- let result = write_to_device ( "ZDesigner ZD220-203dpi ZPL" , "^FDhello world" , Some ( "Test Document" ) ) ;
18+ let result = write_to_device (
19+ "ZDesigner ZD220-203dpi ZPL" ,
20+ "^FDhello world" ,
21+ Some ( "Test Document" ) ,
22+ ) ;
1623 assert ! ( result. is_ok( ) ) ;
1724 }
1825}
@@ -30,37 +37,42 @@ mod tests {
3037/// let zpl = "^FDhello world";
3138/// let printer = "/dev/usb/lp0";
3239/// let result = raw_printer::write_to_device(printer, zpl, Some("My Custom Document"));
33- ///
40+ ///
3441/// assert!(result.is_ok());
35- ///
42+ ///
3643/// ```
3744#[ cfg( target_os = "linux" ) ]
38- pub fn write_to_device ( printer : & str , payload : & str , _document_name : Option < & str > ) -> Result < usize , std:: io:: Error > {
45+ pub fn write_to_device (
46+ printer : & str ,
47+ payload : & str ,
48+ _document_name : Option < & str > ,
49+ ) -> Result < usize , std:: io:: Error > {
3950 use std:: fs:: OpenOptions ;
4051 use std:: io:: Write ;
4152
42- let device_path = OpenOptions :: new ( ) . write ( true ) . open ( printer) ;
43-
44- match device_path {
45- Ok ( mut device) => {
46- let bytes_written = device. write ( payload. as_bytes ( ) ) ?;
47- Ok ( bytes_written)
48- }
49- Err ( e) => Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ,
50- }
53+ let mut device = OpenOptions :: new ( ) . write ( true ) . open ( printer) ?;
54+ let bytes_written = device. write ( payload. as_bytes ( ) ) ?;
55+ device. flush ( ) ?; // Ensure data is written
56+ Ok ( bytes_written)
5157}
5258
5359#[ cfg( target_os = "windows" ) ]
54- pub fn write_to_device ( printer : & str , payload : & str , document_name : Option < & str > ) -> Result < usize , std:: io:: Error > {
60+ pub fn write_to_device (
61+ printer : & str ,
62+ payload : & str ,
63+ document_name : Option < & str > ,
64+ ) -> Result < usize , std:: io:: Error > {
5565 use std:: ffi:: CString ;
5666 use std:: ptr;
57- use windows:: Win32 :: Foundation :: HANDLE ;
67+ use windows:: core :: PCSTR ;
5868 use windows:: Win32 :: Graphics :: Printing :: {
59- ClosePrinter , EndDocPrinter , EndPagePrinter , OpenPrinterA , StartDocPrinterA ,
60- StartPagePrinter , WritePrinter , DOC_INFO_1A , PRINTER_ACCESS_USE , PRINTER_DEFAULTSA ,
69+ EndDocPrinter , EndPagePrinter , OpenPrinterA , StartDocPrinterA , StartPagePrinter ,
70+ WritePrinter , DOC_INFO_1A , PRINTER_ACCESS_USE , PRINTER_DEFAULTSA ,
6171 } ;
6272
63- let printer_name = CString :: new ( printer) . unwrap_or_default ( ) ; // null-terminated string
73+ let printer_name = CString :: new ( printer)
74+ . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidInput , e) ) ?;
75+
6476 let mut printer_handle: HANDLE = HANDLE ( std:: ptr:: null_mut ( ) ) ;
6577
6678 // Open the printer
@@ -72,53 +84,94 @@ pub fn write_to_device(printer: &str, payload: &str, document_name: Option<&str>
7284 } ;
7385
7486 if OpenPrinterA (
75- windows :: core :: PCSTR ( printer_name. as_bytes ( ) . as_ptr ( ) ) ,
87+ PCSTR ( printer_name. as_ptr ( ) as * const u8 ) ,
7688 & mut printer_handle,
7789 Some ( & pd) ,
7890 )
79- . is_ok ( )
91+ . is_err ( )
8092 {
81- let doc_name = document_name. unwrap_or ( "Print Job" ) ;
82- let doc_name_cstring = CString :: new ( doc_name) . unwrap_or_default ( ) ;
83-
84- let doc_info = DOC_INFO_1A {
85- pDocName : windows:: core:: PSTR ( doc_name_cstring. as_ptr ( ) as * mut u8 ) ,
86- pOutputFile : windows:: core:: PSTR :: null ( ) ,
87- pDatatype : windows:: core:: PSTR ( "RAW\0 " . as_ptr ( ) as * mut u8 ) ,
88- } ;
89-
90- // Start the document
91- let job = StartDocPrinterA ( printer_handle, 1 , & doc_info as * const _ as _ ) ;
92- if job == 0 {
93- return Err ( std:: io:: Error :: from ( windows:: core:: Error :: from_win32 ( ) ) ) ;
94- }
95-
96- // Start the page
97- if !StartPagePrinter ( printer_handle) . as_bool ( ) {
98- return Err ( std:: io:: Error :: from ( windows:: core:: Error :: from_win32 ( ) ) ) ;
99- }
100-
101- let buffer = payload. as_bytes ( ) ;
102-
103- let mut bytes_written: u32 = 0 ;
104- if !WritePrinter (
105- printer_handle,
106- buffer. as_ptr ( ) as _ ,
107- buffer. len ( ) as u32 ,
108- & mut bytes_written,
109- )
110- . as_bool ( )
111- {
112- return Err ( std:: io:: Error :: from ( windows:: core:: Error :: from_win32 ( ) ) ) ;
113- }
114-
115- // End the page and document
116- let _ = EndPagePrinter ( printer_handle) ;
93+ return Err ( std:: io:: Error :: new (
94+ std:: io:: ErrorKind :: NotFound ,
95+ format ! ( "Failed to open printer: {}" , printer) ,
96+ ) ) ;
97+ }
98+
99+ // Ensure proper cleanup with RAII-style wrapper
100+ 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+ // Start the document
115+ 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+ // Start the page
124+ if !StartPagePrinter ( printer_handle) . as_bool ( ) {
117125 let _ = EndDocPrinter ( printer_handle) ;
118- let _ = ClosePrinter ( printer_handle ) ;
119- return Ok ( bytes_written as usize ) ;
120- } else {
121- return Err ( std :: io :: Error :: from ( windows :: core :: Error :: from_win32 ( ) ) ) ;
126+ return Err ( std :: io :: Error :: new (
127+ std :: io :: ErrorKind :: Other ,
128+ "Failed to start page" ,
129+ ) ) ;
122130 }
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+ // Always end the page and document, regardless of write success
143+ 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 )
123154 }
124155}
156+
157+ #[ cfg( target_os = "windows" ) ]
158+ struct PrinterGuard {
159+ handle : HANDLE ,
160+ }
161+
162+ #[ cfg( target_os = "windows" ) ]
163+ impl PrinterGuard {
164+ fn new ( handle : HANDLE ) -> Self {
165+ Self { handle }
166+ }
167+ }
168+
169+ #[ cfg( target_os = "windows" ) ]
170+ impl 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+ }
0 commit comments