@skalfa/skalfa-printer 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +56 -0
- package/dist/PrinterSelect.component.d.ts +7 -0
- package/dist/PrinterSelect.component.js +51 -0
- package/dist/commands.d.ts +11 -0
- package/dist/commands.js +17 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +23 -0
- package/dist/usePrinter.hook.d.ts +13 -0
- package/dist/usePrinter.hook.js +114 -0
- package/dist/utils.d.ts +33 -0
- package/dist/utils.js +64 -0
- package/package.json +29 -0
- package/src-rust/Cargo.lock +4473 -0
- package/src-rust/Cargo.toml +30 -0
- package/src-rust/build.rs +5 -0
- package/src-rust/permissions/autogenerated/commands/get_printer_status.toml +13 -0
- package/src-rust/permissions/autogenerated/commands/list_printers.toml +13 -0
- package/src-rust/permissions/autogenerated/commands/print_raw.toml +13 -0
- package/src-rust/permissions/autogenerated/reference.md +97 -0
- package/src-rust/permissions/default.toml +3 -0
- package/src-rust/permissions/schemas/schema.json +342 -0
- package/src-rust/src/commands.rs +62 -0
- package/src-rust/src/error.rs +34 -0
- package/src-rust/src/lib.rs +20 -0
- package/src-rust/src/printer_windows.rs +291 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
use crate::error::Error;
|
|
2
|
+
use serde::Serialize;
|
|
3
|
+
use windows::core::PCWSTR;
|
|
4
|
+
use windows::Win32::Foundation::{BOOL, HANDLE};
|
|
5
|
+
use windows::Win32::Graphics::Printing::{
|
|
6
|
+
ClosePrinter, EndDocPrinter, EndPagePrinter, EnumPrintersW, GetDefaultPrinterW,
|
|
7
|
+
OpenPrinterW, StartDocPrinterW, StartPagePrinter, WritePrinter, DOC_INFO_1W,
|
|
8
|
+
PRINTER_DEFAULTSW, PRINTER_ENUM_LOCAL, PRINTER_ENUM_CONNECTIONS,
|
|
9
|
+
PRINTER_INFO_2W,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
#[derive(Debug, Clone, Serialize)]
|
|
13
|
+
pub struct PrinterInfo {
|
|
14
|
+
pub name: String,
|
|
15
|
+
pub port: String,
|
|
16
|
+
pub driver: String,
|
|
17
|
+
pub is_default: bool,
|
|
18
|
+
pub status: u32,
|
|
19
|
+
pub status_text: String,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fn to_wide(s: &str) -> Vec<u16> {
|
|
23
|
+
s.encode_utf16().chain(std::iter::once(0)).collect()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
unsafe fn pwstr_to_string(ptr: *const u16) -> String {
|
|
27
|
+
if ptr.is_null() {
|
|
28
|
+
return String::new();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let mut len = 0;
|
|
32
|
+
while *ptr.add(len) != 0 {
|
|
33
|
+
len += 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let slice = std::slice::from_raw_parts(ptr, len);
|
|
37
|
+
String::from_utf16_lossy(slice)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub fn enum_printers() -> Result<Vec<PrinterInfo>, Error> {
|
|
41
|
+
let default_printer = get_default_printer_name().unwrap_or_default();
|
|
42
|
+
|
|
43
|
+
let flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
|
|
44
|
+
let mut bytes_needed: u32 = 0;
|
|
45
|
+
let mut count: u32 = 0;
|
|
46
|
+
|
|
47
|
+
unsafe {
|
|
48
|
+
let _ = EnumPrintersW(
|
|
49
|
+
flags,
|
|
50
|
+
PCWSTR::null(),
|
|
51
|
+
2,
|
|
52
|
+
None,
|
|
53
|
+
&mut bytes_needed,
|
|
54
|
+
&mut count,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if bytes_needed == 0 {
|
|
59
|
+
return Ok(Vec::new());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let mut buffer: Vec<u8> = vec![0u8; bytes_needed as usize];
|
|
63
|
+
|
|
64
|
+
unsafe {
|
|
65
|
+
EnumPrintersW(
|
|
66
|
+
flags,
|
|
67
|
+
PCWSTR::null(),
|
|
68
|
+
2,
|
|
69
|
+
Some(&mut buffer),
|
|
70
|
+
&mut bytes_needed,
|
|
71
|
+
&mut count,
|
|
72
|
+
)
|
|
73
|
+
.map_err(|e| Error::EnumPrinters(e.to_string()))?;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let printers_ptr = buffer.as_ptr() as *const PRINTER_INFO_2W;
|
|
77
|
+
let mut result = Vec::with_capacity(count as usize);
|
|
78
|
+
|
|
79
|
+
for i in 0..count as isize {
|
|
80
|
+
unsafe {
|
|
81
|
+
let info = &*printers_ptr.offset(i);
|
|
82
|
+
let name = pwstr_to_string(info.pPrinterName.0);
|
|
83
|
+
let port = pwstr_to_string(info.pPortName.0);
|
|
84
|
+
let driver = pwstr_to_string(info.pDriverName.0);
|
|
85
|
+
let status = info.Status;
|
|
86
|
+
|
|
87
|
+
result.push(PrinterInfo {
|
|
88
|
+
is_default: name == default_printer,
|
|
89
|
+
name,
|
|
90
|
+
port,
|
|
91
|
+
driver,
|
|
92
|
+
status,
|
|
93
|
+
status_text: status_to_string(status),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
Ok(result)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
pub fn print_raw(printer_name: &str, content: &str) -> Result<(), Error> {
|
|
102
|
+
let wide_name = to_wide(printer_name);
|
|
103
|
+
|
|
104
|
+
let mut handle = HANDLE::default();
|
|
105
|
+
|
|
106
|
+
let defaults = PRINTER_DEFAULTSW::default();
|
|
107
|
+
|
|
108
|
+
unsafe {
|
|
109
|
+
OpenPrinterW(
|
|
110
|
+
PCWSTR(wide_name.as_ptr()),
|
|
111
|
+
&mut handle,
|
|
112
|
+
Some(&defaults),
|
|
113
|
+
)
|
|
114
|
+
.map_err(|e| Error::OpenPrinter(printer_name.to_string(), e.to_string()))?;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let result = send_raw_document(handle, printer_name, content);
|
|
118
|
+
|
|
119
|
+
unsafe {
|
|
120
|
+
let _ = ClosePrinter(handle);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
result
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
pub fn get_printer_status(printer_name: &str) -> Result<PrinterInfo, Error> {
|
|
127
|
+
let printers = enum_printers()?;
|
|
128
|
+
printers
|
|
129
|
+
.into_iter()
|
|
130
|
+
.find(|p| p.name == printer_name)
|
|
131
|
+
.ok_or_else(|| Error::PrinterNotFound(printer_name.to_string()))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fn send_raw_document(handle: HANDLE, printer_name: &str, content: &str) -> Result<(), Error> {
|
|
135
|
+
let doc_name = to_wide("Skalfa Print Job");
|
|
136
|
+
let datatype = to_wide("RAW");
|
|
137
|
+
|
|
138
|
+
let doc_info = DOC_INFO_1W {
|
|
139
|
+
pDocName: windows::core::PWSTR(doc_name.as_ptr() as *mut _),
|
|
140
|
+
pOutputFile: windows::core::PWSTR::null(),
|
|
141
|
+
pDatatype: windows::core::PWSTR(datatype.as_ptr() as *mut _),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
unsafe {
|
|
145
|
+
let job_id = StartDocPrinterW(handle, 1, &doc_info as *const _ as *const _);
|
|
146
|
+
if job_id == 0 {
|
|
147
|
+
return Err(Error::StartDoc(format!(
|
|
148
|
+
"StartDocPrinterW returned 0 for printer '{}'",
|
|
149
|
+
printer_name
|
|
150
|
+
)));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let start_page_result: BOOL = StartPagePrinter(handle);
|
|
154
|
+
if !start_page_result.as_bool() {
|
|
155
|
+
let _ = EndDocPrinter(handle);
|
|
156
|
+
return Err(Error::WritePrinter("StartPagePrinter failed".to_string()));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let data = content.as_bytes();
|
|
160
|
+
let mut bytes_written: u32 = 0;
|
|
161
|
+
|
|
162
|
+
let write_result: BOOL = WritePrinter(
|
|
163
|
+
handle,
|
|
164
|
+
data.as_ptr() as *const _,
|
|
165
|
+
data.len() as u32,
|
|
166
|
+
&mut bytes_written,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if !write_result.as_bool() {
|
|
170
|
+
let _ = EndPagePrinter(handle);
|
|
171
|
+
let _ = EndDocPrinter(handle);
|
|
172
|
+
return Err(Error::WritePrinter("WritePrinter failed".to_string()));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let _ = EndPagePrinter(handle);
|
|
176
|
+
let _ = EndDocPrinter(handle);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Ok(())
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
fn get_default_printer_name() -> Option<String> {
|
|
183
|
+
let mut size: u32 = 0;
|
|
184
|
+
|
|
185
|
+
unsafe {
|
|
186
|
+
let _ = GetDefaultPrinterW(windows::core::PWSTR::null(), &mut size);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if size == 0 {
|
|
190
|
+
return None;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let mut buffer: Vec<u16> = vec![0u16; size as usize];
|
|
194
|
+
|
|
195
|
+
unsafe {
|
|
196
|
+
let result: BOOL = GetDefaultPrinterW(
|
|
197
|
+
windows::core::PWSTR(buffer.as_mut_ptr()),
|
|
198
|
+
&mut size,
|
|
199
|
+
);
|
|
200
|
+
if !result.as_bool() {
|
|
201
|
+
return None;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if let Some(pos) = buffer.iter().position(|&c| c == 0) {
|
|
206
|
+
buffer.truncate(pos);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
Some(String::from_utf16_lossy(&buffer))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
fn status_to_string(status: u32) -> String {
|
|
213
|
+
if status == 0 {
|
|
214
|
+
return "Ready".to_string();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let mut parts = Vec::new();
|
|
218
|
+
|
|
219
|
+
if status & 0x00000001 != 0 {
|
|
220
|
+
parts.push("Paused");
|
|
221
|
+
}
|
|
222
|
+
if status & 0x00000002 != 0 {
|
|
223
|
+
parts.push("Error");
|
|
224
|
+
}
|
|
225
|
+
if status & 0x00000004 != 0 {
|
|
226
|
+
parts.push("Pending Deletion");
|
|
227
|
+
}
|
|
228
|
+
if status & 0x00000008 != 0 {
|
|
229
|
+
parts.push("Paper Jam");
|
|
230
|
+
}
|
|
231
|
+
if status & 0x00000010 != 0 {
|
|
232
|
+
parts.push("Paper Out");
|
|
233
|
+
}
|
|
234
|
+
if status & 0x00000020 != 0 {
|
|
235
|
+
parts.push("Manual Feed");
|
|
236
|
+
}
|
|
237
|
+
if status & 0x00000040 != 0 {
|
|
238
|
+
parts.push("Paper Problem");
|
|
239
|
+
}
|
|
240
|
+
if status & 0x00000080 != 0 {
|
|
241
|
+
parts.push("Offline");
|
|
242
|
+
}
|
|
243
|
+
if status & 0x00000100 != 0 {
|
|
244
|
+
parts.push("IO Active");
|
|
245
|
+
}
|
|
246
|
+
if status & 0x00000200 != 0 {
|
|
247
|
+
parts.push("Busy");
|
|
248
|
+
}
|
|
249
|
+
if status & 0x00000400 != 0 {
|
|
250
|
+
parts.push("Printing");
|
|
251
|
+
}
|
|
252
|
+
if status & 0x00000800 != 0 {
|
|
253
|
+
parts.push("Output Bin Full");
|
|
254
|
+
}
|
|
255
|
+
if status & 0x00001000 != 0 {
|
|
256
|
+
parts.push("Not Available");
|
|
257
|
+
}
|
|
258
|
+
if status & 0x00002000 != 0 {
|
|
259
|
+
parts.push("Waiting");
|
|
260
|
+
}
|
|
261
|
+
if status & 0x00004000 != 0 {
|
|
262
|
+
parts.push("Processing");
|
|
263
|
+
}
|
|
264
|
+
if status & 0x00008000 != 0 {
|
|
265
|
+
parts.push("Initializing");
|
|
266
|
+
}
|
|
267
|
+
if status & 0x00010000 != 0 {
|
|
268
|
+
parts.push("Warming Up");
|
|
269
|
+
}
|
|
270
|
+
if status & 0x00020000 != 0 {
|
|
271
|
+
parts.push("Toner Low");
|
|
272
|
+
}
|
|
273
|
+
if status & 0x00040000 != 0 {
|
|
274
|
+
parts.push("No Toner");
|
|
275
|
+
}
|
|
276
|
+
if status & 0x00080000 != 0 {
|
|
277
|
+
parts.push("Page Punt");
|
|
278
|
+
}
|
|
279
|
+
if status & 0x00400000 != 0 {
|
|
280
|
+
parts.push("Door Open");
|
|
281
|
+
}
|
|
282
|
+
if status & 0x01000000 != 0 {
|
|
283
|
+
parts.push("Power Save");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if parts.is_empty() {
|
|
287
|
+
format!("Unknown (0x{:08X})", status)
|
|
288
|
+
} else {
|
|
289
|
+
parts.join(", ")
|
|
290
|
+
}
|
|
291
|
+
}
|