@esslassi/electron-printer 0.0.7 → 0.0.8

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.
@@ -1,347 +1,347 @@
1
- #include "windows_printer.h"
2
-
3
- #include <vector>
4
- #include <string>
5
- #include <map>
6
- #include <fstream>
7
- #include <sstream>
8
- #include <algorithm>
9
-
10
- static std::string ToUpper(std::string s) {
11
- std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return (char)std::toupper(c); });
12
- return s;
13
- }
14
-
15
- std::wstring WindowsPrinter::Utf8ToWide(const std::string &str)
16
- {
17
- std::wstring wstr;
18
- int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
19
- if (len > 0)
20
- {
21
- wstr.resize(len - 1);
22
- MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], len);
23
- }
24
- return wstr;
25
- }
26
-
27
- std::string WindowsPrinter::WideToUtf8(LPWSTR wstr)
28
- {
29
- if (!wstr)
30
- return "";
31
-
32
- int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
33
- if (len <= 0)
34
- return "";
35
-
36
- std::vector<char> buffer(len);
37
- WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer.data(), len, NULL, NULL);
38
- return std::string(buffer.data());
39
- }
40
-
41
- static std::time_t SystemTimeToTimeT(const SYSTEMTIME &st)
42
- {
43
- FILETIME ft;
44
- if (!SystemTimeToFileTime(&st, &ft)) return 0;
45
-
46
- ULARGE_INTEGER ull;
47
- ull.LowPart = ft.dwLowDateTime;
48
- ull.HighPart = ft.dwHighDateTime;
49
-
50
- // Windows FILETIME is 100-ns since Jan 1, 1601 (UTC)
51
- // Convert to Unix epoch (seconds since Jan 1, 1970)
52
- const unsigned long long EPOCH_DIFFERENCE = 116444736000000000ULL; // 100ns
53
- if (ull.QuadPart < EPOCH_DIFFERENCE) return 0;
54
-
55
- unsigned long long unix100ns = ull.QuadPart - EPOCH_DIFFERENCE;
56
- return (std::time_t)(unix100ns / 10000000ULL);
57
- }
58
-
59
- std::vector<std::string> WindowsPrinter::MapJobStatus(DWORD status)
60
- {
61
- std::vector<std::string> out;
62
-
63
- if (status & JOB_STATUS_PAUSED) out.push_back("PAUSED");
64
- if (status & JOB_STATUS_PRINTING) out.push_back("PRINTING");
65
- if (status & JOB_STATUS_SPOOLING) out.push_back("PENDING");
66
- if (status & JOB_STATUS_DELETING) out.push_back("CANCELLED");
67
- if (status & JOB_STATUS_DELETED) out.push_back("CANCELLED");
68
- if (status & JOB_STATUS_ERROR) out.push_back("ABORTED");
69
- if (status & JOB_STATUS_OFFLINE) out.push_back("PENDING");
70
- if (status & JOB_STATUS_PAPEROUT) out.push_back("PENDING");
71
- if (status & JOB_STATUS_PRINTED) out.push_back("PRINTED");
72
-
73
- if (out.empty()) out.push_back("PENDING");
74
- return out;
75
- }
76
-
77
- static bool ReadAllBytes(const std::string &path, std::vector<uint8_t> &out)
78
- {
79
- std::ifstream f(path, std::ios::binary);
80
- if (!f) return false;
81
- f.seekg(0, std::ios::end);
82
- std::streamsize size = f.tellg();
83
- if (size < 0) return false;
84
- f.seekg(0, std::ios::beg);
85
-
86
- out.resize((size_t)size);
87
- if (size > 0)
88
- f.read((char*)out.data(), size);
89
-
90
- return true;
91
- }
92
-
93
- std::vector<PrinterDetailsNative> WindowsPrinter::GetPrinters()
94
- {
95
- std::vector<PrinterDetailsNative> printers;
96
-
97
- DWORD needed = 0, returned = 0;
98
- EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, NULL, 0, &needed, &returned);
99
- if (needed == 0) return printers;
100
-
101
- std::vector<BYTE> buffer(needed);
102
- if (!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, buffer.data(), needed, &needed, &returned))
103
- return printers;
104
-
105
- wchar_t defaultPrinter[512];
106
- DWORD defaultSize = (DWORD)(sizeof(defaultPrinter) / sizeof(defaultPrinter[0]));
107
- std::string defaultName;
108
- if (GetDefaultPrinterW(defaultPrinter, &defaultSize))
109
- defaultName = WideToUtf8(defaultPrinter);
110
-
111
- PRINTER_INFO_2W *pInfo = (PRINTER_INFO_2W *)buffer.data();
112
-
113
- for (DWORD i = 0; i < returned; i++)
114
- {
115
- PrinterDetailsNative p;
116
- p.name = WideToUtf8(pInfo[i].pPrinterName);
117
- p.isDefault = (!defaultName.empty() && p.name == defaultName);
118
-
119
- // options: keep it clean and stable
120
- if (pInfo[i].pLocation) p.options["location"] = WideToUtf8(pInfo[i].pLocation);
121
- if (pInfo[i].pComment) p.options["comment"] = WideToUtf8(pInfo[i].pComment);
122
- if (pInfo[i].pDriverName) p.options["driver"] = WideToUtf8(pInfo[i].pDriverName);
123
- if (pInfo[i].pPortName) p.options["port"] = WideToUtf8(pInfo[i].pPortName);
124
-
125
- printers.push_back(std::move(p));
126
- }
127
-
128
- return printers;
129
- }
130
-
131
- PrinterDetailsNative WindowsPrinter::GetPrinter(const std::string &printerName)
132
- {
133
- // Build from GetPrinters (stable + consistent)
134
- auto list = GetPrinters();
135
- for (auto &p : list)
136
- {
137
- if (p.name == printerName) return p;
138
- }
139
-
140
- PrinterDetailsNative p;
141
- p.name = printerName;
142
- p.isDefault = false;
143
- return p;
144
- }
145
-
146
- std::string WindowsPrinter::GetDefaultPrinterName()
147
- {
148
- wchar_t printerName[512];
149
- DWORD size = (DWORD)(sizeof(printerName) / sizeof(printerName[0]));
150
- if (GetDefaultPrinterW(printerName, &size))
151
- return WideToUtf8(printerName);
152
- return "";
153
- }
154
-
155
- DriverOptions WindowsPrinter::GetPrinterDriverOptions(const std::string &printerName)
156
- {
157
- // Windows driver option enumeration is complex (DEVMODE / DocumentProperties).
158
- // For now: return empty, still satisfies your TS typing.
159
- // You can enhance later with DEVMODE-driven options.
160
- (void)printerName;
161
- return DriverOptions{};
162
- }
163
-
164
- std::string WindowsPrinter::GetSelectedPaperSize(const std::string &printerName)
165
- {
166
- HANDLE hPrinter = NULL;
167
- std::wstring wName = Utf8ToWide(printerName);
168
-
169
- if (!OpenPrinterW((LPWSTR)wName.c_str(), &hPrinter, NULL))
170
- return "";
171
-
172
- // Query DEVMODE size
173
- LONG dmSize = DocumentPropertiesW(NULL, hPrinter, (LPWSTR)wName.c_str(), NULL, NULL, 0);
174
- if (dmSize <= 0)
175
- {
176
- ClosePrinter(hPrinter);
177
- return "";
178
- }
179
-
180
- std::vector<BYTE> dmBuffer((size_t)dmSize);
181
- DEVMODEW *dm = (DEVMODEW *)dmBuffer.data();
182
-
183
- LONG res = DocumentPropertiesW(NULL, hPrinter, (LPWSTR)wName.c_str(), dm, NULL, DM_OUT_BUFFER);
184
- if (res != IDOK)
185
- {
186
- ClosePrinter(hPrinter);
187
- return "";
188
- }
189
-
190
- std::string paper;
191
-
192
- if ((dm->dmFields & DM_FORMNAME) && dm->dmFormName[0] != L'\0')
193
- {
194
- paper = WideToUtf8(dm->dmFormName);
195
- }
196
- else if (dm->dmFields & DM_PAPERSIZE)
197
- {
198
- // Fallback: return numeric code as string (still useful)
199
- paper = std::to_string((int)dm->dmPaperSize);
200
- }
201
-
202
- ClosePrinter(hPrinter);
203
- return paper;
204
- }
205
-
206
- std::vector<std::string> WindowsPrinter::GetSupportedPrintFormats()
207
- {
208
- // On Windows spooler, “RAW” is always safe. PDF/JPEG/PS conversion is not guaranteed without extra pipeline.
209
- return { "RAW", "TEXT", "COMMAND" };
210
- }
211
-
212
- int WindowsPrinter::PrintDirect(const std::string &printerName,
213
- const std::vector<uint8_t> &data,
214
- const std::string &type,
215
- const StringMap &options)
216
- {
217
- (void)options;
218
-
219
- HANDLE hPrinter = NULL;
220
- std::wstring wPrinterName = Utf8ToWide(printerName);
221
-
222
- if (!OpenPrinterW((LPWSTR)wPrinterName.c_str(), &hPrinter, NULL))
223
- return 0;
224
-
225
- std::string t = ToUpper(type);
226
- // Windows expects a spool datatype string. For our types:
227
- // RAW/TEXT/COMMAND => use RAW to send bytes directly.
228
- std::wstring wDataType = Utf8ToWide("RAW");
229
-
230
- DOC_INFO_1W docInfo;
231
- wchar_t docName[] = L"Node Print Job";
232
- docInfo.pDocName = docName;
233
- docInfo.pOutputFile = NULL;
234
- docInfo.pDatatype = (LPWSTR)wDataType.c_str();
235
-
236
- DWORD jobId = StartDocPrinterW(hPrinter, 1, (LPBYTE)&docInfo);
237
- if (jobId == 0)
238
- {
239
- ClosePrinter(hPrinter);
240
- return 0;
241
- }
242
-
243
- if (!StartPagePrinter(hPrinter))
244
- {
245
- EndDocPrinter(hPrinter);
246
- ClosePrinter(hPrinter);
247
- return 0;
248
- }
249
-
250
- DWORD bytesWritten = 0;
251
- BOOL ok = WritePrinter(hPrinter, (LPVOID)data.data(), (DWORD)data.size(), &bytesWritten);
252
-
253
- EndPagePrinter(hPrinter);
254
- EndDocPrinter(hPrinter);
255
- ClosePrinter(hPrinter);
256
-
257
- if (!ok || bytesWritten != (DWORD)data.size())
258
- return 0;
259
-
260
- return (int)jobId;
261
- }
262
-
263
- int WindowsPrinter::PrintFile(const std::string &printerName,
264
- const std::string &filename)
265
- {
266
- std::vector<uint8_t> data;
267
- if (!ReadAllBytes(filename, data))
268
- return 0;
269
-
270
- // PrintFile typing does not include type, so we treat it as RAW bytes.
271
- StringMap emptyOpts;
272
- return PrintDirect(printerName, data, "RAW", emptyOpts);
273
- }
274
-
275
- JobDetailsNative WindowsPrinter::GetJob(const std::string &printerName, int jobId)
276
- {
277
- JobDetailsNative j;
278
- j.id = jobId;
279
- j.printerName = printerName;
280
-
281
- HANDLE hPrinter = NULL;
282
- std::wstring wPrinterName = Utf8ToWide(printerName);
283
- if (!OpenPrinterW((LPWSTR)wPrinterName.c_str(), &hPrinter, NULL))
284
- return j;
285
-
286
- DWORD needed = 0;
287
- GetJobW(hPrinter, (DWORD)jobId, 2, NULL, 0, &needed);
288
- if (needed == 0)
289
- {
290
- ClosePrinter(hPrinter);
291
- return j;
292
- }
293
-
294
- std::vector<BYTE> buffer(needed);
295
- if (!GetJobW(hPrinter, (DWORD)jobId, 2, buffer.data(), needed, &needed))
296
- {
297
- ClosePrinter(hPrinter);
298
- return j;
299
- }
300
-
301
- JOB_INFO_2W *ji = (JOB_INFO_2W *)buffer.data();
302
-
303
- j.name = ji->pDocument ? WideToUtf8(ji->pDocument) : "";
304
- j.user = ji->pUserName ? WideToUtf8(ji->pUserName) : "";
305
- j.format = "RAW";
306
- j.priority = (int)ji->Priority;
307
- j.size = (int)ji->Size;
308
- j.status = MapJobStatus(ji->Status);
309
-
310
- // Times
311
- j.creationTime = SystemTimeToTimeT(ji->Submitted);
312
- j.processingTime = j.creationTime;
313
- j.completedTime = 0;
314
-
315
- ClosePrinter(hPrinter);
316
- return j;
317
- }
318
-
319
- void WindowsPrinter::SetJob(const std::string &printerName, int jobId, const std::string &command)
320
- {
321
- HANDLE hPrinter = NULL;
322
- std::wstring wPrinterName = Utf8ToWide(printerName);
323
- if (!OpenPrinterW((LPWSTR)wPrinterName.c_str(), &hPrinter, NULL))
324
- return;
325
-
326
- std::string cmd = ToUpper(command);
327
-
328
- if (cmd == "CANCEL")
329
- {
330
- SetJobW(hPrinter, (DWORD)jobId, 0, NULL, JOB_CONTROL_CANCEL);
331
- }
332
- else if (cmd == "PAUSE")
333
- {
334
- SetJobW(hPrinter, (DWORD)jobId, 0, NULL, JOB_CONTROL_PAUSE);
335
- }
336
- else if (cmd == "RESUME")
337
- {
338
- SetJobW(hPrinter, (DWORD)jobId, 0, NULL, JOB_CONTROL_RESUME);
339
- }
340
-
341
- ClosePrinter(hPrinter);
342
- }
343
-
344
- std::vector<std::string> WindowsPrinter::GetSupportedJobCommands()
345
- {
346
- return { "CANCEL", "PAUSE", "RESUME" };
1
+ #include "windows_printer.h"
2
+
3
+ #include <vector>
4
+ #include <string>
5
+ #include <map>
6
+ #include <fstream>
7
+ #include <sstream>
8
+ #include <algorithm>
9
+
10
+ static std::string ToUpper(std::string s) {
11
+ std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return (char)std::toupper(c); });
12
+ return s;
13
+ }
14
+
15
+ std::wstring WindowsPrinter::Utf8ToWide(const std::string &str)
16
+ {
17
+ std::wstring wstr;
18
+ int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
19
+ if (len > 0)
20
+ {
21
+ wstr.resize(len - 1);
22
+ MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], len);
23
+ }
24
+ return wstr;
25
+ }
26
+
27
+ std::string WindowsPrinter::WideToUtf8(LPWSTR wstr)
28
+ {
29
+ if (!wstr)
30
+ return "";
31
+
32
+ int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
33
+ if (len <= 0)
34
+ return "";
35
+
36
+ std::vector<char> buffer(len);
37
+ WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer.data(), len, NULL, NULL);
38
+ return std::string(buffer.data());
39
+ }
40
+
41
+ static std::time_t SystemTimeToTimeT(const SYSTEMTIME &st)
42
+ {
43
+ FILETIME ft;
44
+ if (!SystemTimeToFileTime(&st, &ft)) return 0;
45
+
46
+ ULARGE_INTEGER ull;
47
+ ull.LowPart = ft.dwLowDateTime;
48
+ ull.HighPart = ft.dwHighDateTime;
49
+
50
+ // Windows FILETIME is 100-ns since Jan 1, 1601 (UTC)
51
+ // Convert to Unix epoch (seconds since Jan 1, 1970)
52
+ const unsigned long long EPOCH_DIFFERENCE = 116444736000000000ULL; // 100ns
53
+ if (ull.QuadPart < EPOCH_DIFFERENCE) return 0;
54
+
55
+ unsigned long long unix100ns = ull.QuadPart - EPOCH_DIFFERENCE;
56
+ return (std::time_t)(unix100ns / 10000000ULL);
57
+ }
58
+
59
+ std::vector<std::string> WindowsPrinter::MapJobStatus(DWORD status)
60
+ {
61
+ std::vector<std::string> out;
62
+
63
+ if (status & JOB_STATUS_PAUSED) out.push_back("PAUSED");
64
+ if (status & JOB_STATUS_PRINTING) out.push_back("PRINTING");
65
+ if (status & JOB_STATUS_SPOOLING) out.push_back("PENDING");
66
+ if (status & JOB_STATUS_DELETING) out.push_back("CANCELLED");
67
+ if (status & JOB_STATUS_DELETED) out.push_back("CANCELLED");
68
+ if (status & JOB_STATUS_ERROR) out.push_back("ABORTED");
69
+ if (status & JOB_STATUS_OFFLINE) out.push_back("PENDING");
70
+ if (status & JOB_STATUS_PAPEROUT) out.push_back("PENDING");
71
+ if (status & JOB_STATUS_PRINTED) out.push_back("PRINTED");
72
+
73
+ if (out.empty()) out.push_back("PENDING");
74
+ return out;
75
+ }
76
+
77
+ static bool ReadAllBytes(const std::string &path, std::vector<uint8_t> &out)
78
+ {
79
+ std::ifstream f(path, std::ios::binary);
80
+ if (!f) return false;
81
+ f.seekg(0, std::ios::end);
82
+ std::streamsize size = f.tellg();
83
+ if (size < 0) return false;
84
+ f.seekg(0, std::ios::beg);
85
+
86
+ out.resize((size_t)size);
87
+ if (size > 0)
88
+ f.read((char*)out.data(), size);
89
+
90
+ return true;
91
+ }
92
+
93
+ std::vector<PrinterDetailsNative> WindowsPrinter::GetPrinters()
94
+ {
95
+ std::vector<PrinterDetailsNative> printers;
96
+
97
+ DWORD needed = 0, returned = 0;
98
+ EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, NULL, 0, &needed, &returned);
99
+ if (needed == 0) return printers;
100
+
101
+ std::vector<BYTE> buffer(needed);
102
+ if (!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, buffer.data(), needed, &needed, &returned))
103
+ return printers;
104
+
105
+ wchar_t defaultPrinter[512];
106
+ DWORD defaultSize = (DWORD)(sizeof(defaultPrinter) / sizeof(defaultPrinter[0]));
107
+ std::string defaultName;
108
+ if (GetDefaultPrinterW(defaultPrinter, &defaultSize))
109
+ defaultName = WideToUtf8(defaultPrinter);
110
+
111
+ PRINTER_INFO_2W *pInfo = (PRINTER_INFO_2W *)buffer.data();
112
+
113
+ for (DWORD i = 0; i < returned; i++)
114
+ {
115
+ PrinterDetailsNative p;
116
+ p.name = WideToUtf8(pInfo[i].pPrinterName);
117
+ p.isDefault = (!defaultName.empty() && p.name == defaultName);
118
+
119
+ // options: keep it clean and stable
120
+ if (pInfo[i].pLocation) p.options["location"] = WideToUtf8(pInfo[i].pLocation);
121
+ if (pInfo[i].pComment) p.options["comment"] = WideToUtf8(pInfo[i].pComment);
122
+ if (pInfo[i].pDriverName) p.options["driver"] = WideToUtf8(pInfo[i].pDriverName);
123
+ if (pInfo[i].pPortName) p.options["port"] = WideToUtf8(pInfo[i].pPortName);
124
+
125
+ printers.push_back(std::move(p));
126
+ }
127
+
128
+ return printers;
129
+ }
130
+
131
+ PrinterDetailsNative WindowsPrinter::GetPrinter(const std::string &printerName)
132
+ {
133
+ // Build from GetPrinters (stable + consistent)
134
+ auto list = GetPrinters();
135
+ for (auto &p : list)
136
+ {
137
+ if (p.name == printerName) return p;
138
+ }
139
+
140
+ PrinterDetailsNative p;
141
+ p.name = printerName;
142
+ p.isDefault = false;
143
+ return p;
144
+ }
145
+
146
+ std::string WindowsPrinter::GetDefaultPrinterName()
147
+ {
148
+ wchar_t printerName[512];
149
+ DWORD size = (DWORD)(sizeof(printerName) / sizeof(printerName[0]));
150
+ if (GetDefaultPrinterW(printerName, &size))
151
+ return WideToUtf8(printerName);
152
+ return "";
153
+ }
154
+
155
+ DriverOptions WindowsPrinter::GetPrinterDriverOptions(const std::string &printerName)
156
+ {
157
+ // Windows driver option enumeration is complex (DEVMODE / DocumentProperties).
158
+ // For now: return empty, still satisfies your TS typing.
159
+ // You can enhance later with DEVMODE-driven options.
160
+ (void)printerName;
161
+ return DriverOptions{};
162
+ }
163
+
164
+ std::string WindowsPrinter::GetSelectedPaperSize(const std::string &printerName)
165
+ {
166
+ HANDLE hPrinter = NULL;
167
+ std::wstring wName = Utf8ToWide(printerName);
168
+
169
+ if (!OpenPrinterW((LPWSTR)wName.c_str(), &hPrinter, NULL))
170
+ return "";
171
+
172
+ // Query DEVMODE size
173
+ LONG dmSize = DocumentPropertiesW(NULL, hPrinter, (LPWSTR)wName.c_str(), NULL, NULL, 0);
174
+ if (dmSize <= 0)
175
+ {
176
+ ClosePrinter(hPrinter);
177
+ return "";
178
+ }
179
+
180
+ std::vector<BYTE> dmBuffer((size_t)dmSize);
181
+ DEVMODEW *dm = (DEVMODEW *)dmBuffer.data();
182
+
183
+ LONG res = DocumentPropertiesW(NULL, hPrinter, (LPWSTR)wName.c_str(), dm, NULL, DM_OUT_BUFFER);
184
+ if (res != IDOK)
185
+ {
186
+ ClosePrinter(hPrinter);
187
+ return "";
188
+ }
189
+
190
+ std::string paper;
191
+
192
+ if ((dm->dmFields & DM_FORMNAME) && dm->dmFormName[0] != L'\0')
193
+ {
194
+ paper = WideToUtf8(dm->dmFormName);
195
+ }
196
+ else if (dm->dmFields & DM_PAPERSIZE)
197
+ {
198
+ // Fallback: return numeric code as string (still useful)
199
+ paper = std::to_string((int)dm->dmPaperSize);
200
+ }
201
+
202
+ ClosePrinter(hPrinter);
203
+ return paper;
204
+ }
205
+
206
+ std::vector<std::string> WindowsPrinter::GetSupportedPrintFormats()
207
+ {
208
+ // On Windows spooler, “RAW” is always safe. PDF/JPEG/PS conversion is not guaranteed without extra pipeline.
209
+ return { "RAW", "TEXT", "COMMAND" };
210
+ }
211
+
212
+ int WindowsPrinter::PrintDirect(const std::string &printerName,
213
+ const std::vector<uint8_t> &data,
214
+ const std::string &type,
215
+ const StringMap &options)
216
+ {
217
+ (void)options;
218
+
219
+ HANDLE hPrinter = NULL;
220
+ std::wstring wPrinterName = Utf8ToWide(printerName);
221
+
222
+ if (!OpenPrinterW((LPWSTR)wPrinterName.c_str(), &hPrinter, NULL))
223
+ return 0;
224
+
225
+ std::string t = ToUpper(type);
226
+ // Windows expects a spool datatype string. For our types:
227
+ // RAW/TEXT/COMMAND => use RAW to send bytes directly.
228
+ std::wstring wDataType = Utf8ToWide("RAW");
229
+
230
+ DOC_INFO_1W docInfo;
231
+ wchar_t docName[] = L"Node Print Job";
232
+ docInfo.pDocName = docName;
233
+ docInfo.pOutputFile = NULL;
234
+ docInfo.pDatatype = (LPWSTR)wDataType.c_str();
235
+
236
+ DWORD jobId = StartDocPrinterW(hPrinter, 1, (LPBYTE)&docInfo);
237
+ if (jobId == 0)
238
+ {
239
+ ClosePrinter(hPrinter);
240
+ return 0;
241
+ }
242
+
243
+ if (!StartPagePrinter(hPrinter))
244
+ {
245
+ EndDocPrinter(hPrinter);
246
+ ClosePrinter(hPrinter);
247
+ return 0;
248
+ }
249
+
250
+ DWORD bytesWritten = 0;
251
+ BOOL ok = WritePrinter(hPrinter, (LPVOID)data.data(), (DWORD)data.size(), &bytesWritten);
252
+
253
+ EndPagePrinter(hPrinter);
254
+ EndDocPrinter(hPrinter);
255
+ ClosePrinter(hPrinter);
256
+
257
+ if (!ok || bytesWritten != (DWORD)data.size())
258
+ return 0;
259
+
260
+ return (int)jobId;
261
+ }
262
+
263
+ int WindowsPrinter::PrintFile(const std::string &printerName,
264
+ const std::string &filename)
265
+ {
266
+ std::vector<uint8_t> data;
267
+ if (!ReadAllBytes(filename, data))
268
+ return 0;
269
+
270
+ // PrintFile typing does not include type, so we treat it as RAW bytes.
271
+ StringMap emptyOpts;
272
+ return PrintDirect(printerName, data, "RAW", emptyOpts);
273
+ }
274
+
275
+ JobDetailsNative WindowsPrinter::GetJob(const std::string &printerName, int jobId)
276
+ {
277
+ JobDetailsNative j;
278
+ j.id = jobId;
279
+ j.printerName = printerName;
280
+
281
+ HANDLE hPrinter = NULL;
282
+ std::wstring wPrinterName = Utf8ToWide(printerName);
283
+ if (!OpenPrinterW((LPWSTR)wPrinterName.c_str(), &hPrinter, NULL))
284
+ return j;
285
+
286
+ DWORD needed = 0;
287
+ GetJobW(hPrinter, (DWORD)jobId, 2, NULL, 0, &needed);
288
+ if (needed == 0)
289
+ {
290
+ ClosePrinter(hPrinter);
291
+ return j;
292
+ }
293
+
294
+ std::vector<BYTE> buffer(needed);
295
+ if (!GetJobW(hPrinter, (DWORD)jobId, 2, buffer.data(), needed, &needed))
296
+ {
297
+ ClosePrinter(hPrinter);
298
+ return j;
299
+ }
300
+
301
+ JOB_INFO_2W *ji = (JOB_INFO_2W *)buffer.data();
302
+
303
+ j.name = ji->pDocument ? WideToUtf8(ji->pDocument) : "";
304
+ j.user = ji->pUserName ? WideToUtf8(ji->pUserName) : "";
305
+ j.format = "RAW";
306
+ j.priority = (int)ji->Priority;
307
+ j.size = (int)ji->Size;
308
+ j.status = MapJobStatus(ji->Status);
309
+
310
+ // Times
311
+ j.creationTime = SystemTimeToTimeT(ji->Submitted);
312
+ j.processingTime = j.creationTime;
313
+ j.completedTime = 0;
314
+
315
+ ClosePrinter(hPrinter);
316
+ return j;
317
+ }
318
+
319
+ void WindowsPrinter::SetJob(const std::string &printerName, int jobId, const std::string &command)
320
+ {
321
+ HANDLE hPrinter = NULL;
322
+ std::wstring wPrinterName = Utf8ToWide(printerName);
323
+ if (!OpenPrinterW((LPWSTR)wPrinterName.c_str(), &hPrinter, NULL))
324
+ return;
325
+
326
+ std::string cmd = ToUpper(command);
327
+
328
+ if (cmd == "CANCEL")
329
+ {
330
+ SetJobW(hPrinter, (DWORD)jobId, 0, NULL, JOB_CONTROL_CANCEL);
331
+ }
332
+ else if (cmd == "PAUSE")
333
+ {
334
+ SetJobW(hPrinter, (DWORD)jobId, 0, NULL, JOB_CONTROL_PAUSE);
335
+ }
336
+ else if (cmd == "RESUME")
337
+ {
338
+ SetJobW(hPrinter, (DWORD)jobId, 0, NULL, JOB_CONTROL_RESUME);
339
+ }
340
+
341
+ ClosePrinter(hPrinter);
342
+ }
343
+
344
+ std::vector<std::string> WindowsPrinter::GetSupportedJobCommands()
345
+ {
346
+ return { "CANCEL", "PAUSE", "RESUME" };
347
347
  }