1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[cfg(target_os = "linux")]
    fn test_write_to_device_linux() {
        let result = write_to_device("/dev/usb/lp0", "^FDhello world");
        assert!(result.is_ok());
    }

    #[test]
    #[cfg(target_os = "windows")]
    fn test_write_to_device_windows() {
        let result = write_to_device("ZDesigner ZD220-203dpi ZPL", "^FDhello world");
        assert!(result.is_ok());
    }
}

#[cfg(target_os = "linux")]
pub fn write_to_device(printer: &str, payload: &str) -> Result<(), std::io::Error> {
    use std::fs::OpenOptions;
    use std::io::Write;

    let device_path = OpenOptions::new().write(true).open(printer);

    match device_path {
        Ok(mut device) => {
            let _ = device.write(payload.as_bytes())?;
            Ok(())
        }
        Err(_) => Err(std::io::Error::new(
            std::io::ErrorKind::Other,
            "Failed to open device",
        )),
    }
}

#[cfg(target_os = "windows")]
pub fn write_to_device(printer: &str, payload: &str) -> Result<(), std::io::Error> {
    use std::ffi::CString;
    use std::ptr;
    use windows::Win32::Foundation::HANDLE;
    use windows::Win32::Graphics::Printing::{
        ClosePrinter, EndDocPrinter, EndPagePrinter, OpenPrinterA, StartDocPrinterA,
        StartPagePrinter, WritePrinter, DOC_INFO_1A, PRINTER_ACCESS_USE, PRINTER_DEFAULTSA,
    };

    let printer_name = CString::new(printer).unwrap_or_default(); // null-terminated string
    let mut printer_handle: HANDLE = HANDLE(0);

    // Open the printer
    unsafe {
        let pd = PRINTER_DEFAULTSA {
            pDatatype: windows::core::PSTR(ptr::null_mut()),
            pDevMode: ptr::null_mut(),
            DesiredAccess: PRINTER_ACCESS_USE,
        };

        if OpenPrinterA(
            windows::core::PCSTR(printer_name.as_bytes().as_ptr()),
            &mut printer_handle,
            Some(&pd),
        )
        .is_ok()
        {
            let doc_info = DOC_INFO_1A {
                pDocName: windows::core::PSTR("My Document\0".as_ptr() as *mut u8),
                pOutputFile: windows::core::PSTR::null(),
                pDatatype: windows::core::PSTR("RAW\0".as_ptr() as *mut u8),
            };

            // Start the document
            let job = StartDocPrinterA(printer_handle, 1, &doc_info as *const _ as _);
            if job == 0 {
                return Err(std::io::Error::from(windows::core::Error::from_win32()));
            }

            // Start the page
            if !StartPagePrinter(printer_handle).as_bool() {
                return Err(std::io::Error::from(windows::core::Error::from_win32()));
            }

            let buffer = payload.as_bytes();

            let mut bytes_written: u32 = 0;
            if !WritePrinter(
                printer_handle,
                buffer.as_ptr() as _,
                buffer.len() as u32,
                &mut bytes_written,
            )
            .as_bool()
            {
                return Err(std::io::Error::from(windows::core::Error::from_win32()));
            }

            // End the page and document
            EndPagePrinter(printer_handle);
            EndDocPrinter(printer_handle);
            let _ = ClosePrinter(printer_handle);
        } else {
            return Err(std::io::Error::from(windows::core::Error::from_win32()));
        }
    }

    Ok(())
}