Skip to content

Commit d98274f

Browse files
committed
custom document name and refactor
1 parent eb5441b commit d98274f

File tree

2 files changed

+114
-61
lines changed

2 files changed

+114
-61
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "raw-printer"
33
description = "Direct RAW printing on windows or linux"
44
repository = "https://github.com/mushonnip/raw-printer"
55
keywords = ["printer"]
6-
version = "0.1.4"
6+
version = "0.1.5"
77
edition = "2021"
88
license = "MIT"
99

src/lib.rs

Lines changed: 113 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#[cfg(target_os = "windows")]
2+
use windows::Win32::Foundation::HANDLE;
3+
14
#[cfg(test)]
25
mod 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

Comments
 (0)