@esslassi/electron-printer 0.0.6 → 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.
- package/README.md +235 -167
- package/lib/electronPrinter.d.ts +52 -24
- package/lib/electronPrinter.js +82 -31
- package/package.json +1 -1
- package/src/linux_printer.cpp +312 -112
- package/src/linux_printer.h +21 -11
- package/src/mac_printer.cpp +317 -110
- package/src/mac_printer.h +21 -12
- package/src/main.cpp +42 -13
- package/src/print.cpp +288 -207
- package/src/printer_interface.h +48 -10
- package/src/windows_printer.cpp +271 -134
- package/src/windows_printer.h +40 -11
- package/test.js +120 -0
package/src/windows_printer.cpp
CHANGED
|
@@ -1,49 +1,15 @@
|
|
|
1
1
|
#include "windows_printer.h"
|
|
2
|
+
|
|
2
3
|
#include <vector>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <map>
|
|
6
|
+
#include <fstream>
|
|
7
|
+
#include <sstream>
|
|
8
|
+
#include <algorithm>
|
|
3
9
|
|
|
4
|
-
std::string
|
|
5
|
-
{
|
|
6
|
-
|
|
7
|
-
return "offline";
|
|
8
|
-
if (status & PRINTER_STATUS_ERROR)
|
|
9
|
-
return "error";
|
|
10
|
-
if (status & PRINTER_STATUS_PAPER_JAM)
|
|
11
|
-
return "paper-jam";
|
|
12
|
-
if (status & PRINTER_STATUS_PAPER_OUT)
|
|
13
|
-
return "paper-out";
|
|
14
|
-
if (status & PRINTER_STATUS_MANUAL_FEED)
|
|
15
|
-
return "manual-feed";
|
|
16
|
-
if (status & PRINTER_STATUS_PAPER_PROBLEM)
|
|
17
|
-
return "paper-problem";
|
|
18
|
-
if (status & PRINTER_STATUS_BUSY)
|
|
19
|
-
return "busy";
|
|
20
|
-
if (status & PRINTER_STATUS_PRINTING)
|
|
21
|
-
return "printing";
|
|
22
|
-
if (status & PRINTER_STATUS_OUTPUT_BIN_FULL)
|
|
23
|
-
return "output-bin-full";
|
|
24
|
-
if (status & PRINTER_STATUS_NOT_AVAILABLE)
|
|
25
|
-
return "not-available";
|
|
26
|
-
if (status & PRINTER_STATUS_WAITING)
|
|
27
|
-
return "waiting";
|
|
28
|
-
if (status & PRINTER_STATUS_PROCESSING)
|
|
29
|
-
return "processing";
|
|
30
|
-
if (status & PRINTER_STATUS_INITIALIZING)
|
|
31
|
-
return "initializing";
|
|
32
|
-
if (status & PRINTER_STATUS_WARMING_UP)
|
|
33
|
-
return "warming-up";
|
|
34
|
-
if (status & PRINTER_STATUS_TONER_LOW)
|
|
35
|
-
return "toner-low";
|
|
36
|
-
if (status & PRINTER_STATUS_NO_TONER)
|
|
37
|
-
return "no-toner";
|
|
38
|
-
if (status & PRINTER_STATUS_PAGE_PUNT)
|
|
39
|
-
return "page-punt";
|
|
40
|
-
if (status & PRINTER_STATUS_USER_INTERVENTION)
|
|
41
|
-
return "user-intervention";
|
|
42
|
-
if (status & PRINTER_STATUS_OUT_OF_MEMORY)
|
|
43
|
-
return "out-of-memory";
|
|
44
|
-
if (status & PRINTER_STATUS_DOOR_OPEN)
|
|
45
|
-
return "door-open";
|
|
46
|
-
return "ready";
|
|
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;
|
|
47
13
|
}
|
|
48
14
|
|
|
49
15
|
std::wstring WindowsPrinter::Utf8ToWide(const std::string &str)
|
|
@@ -72,139 +38,310 @@ std::string WindowsPrinter::WideToUtf8(LPWSTR wstr)
|
|
|
72
38
|
return std::string(buffer.data());
|
|
73
39
|
}
|
|
74
40
|
|
|
75
|
-
|
|
41
|
+
static std::time_t SystemTimeToTimeT(const SYSTEMTIME &st)
|
|
76
42
|
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
info.isDefault = isDefault;
|
|
43
|
+
FILETIME ft;
|
|
44
|
+
if (!SystemTimeToFileTime(&st, &ft)) return 0;
|
|
80
45
|
|
|
81
|
-
|
|
82
|
-
|
|
46
|
+
ULARGE_INTEGER ull;
|
|
47
|
+
ull.LowPart = ft.dwLowDateTime;
|
|
48
|
+
ull.HighPart = ft.dwHighDateTime;
|
|
83
49
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (needed > 0)
|
|
89
|
-
{
|
|
90
|
-
std::vector<BYTE> buffer(needed);
|
|
91
|
-
if (GetPrinterW(hPrinter, 2, buffer.data(), needed, &needed))
|
|
92
|
-
{
|
|
93
|
-
PRINTER_INFO_2W *pInfo = (PRINTER_INFO_2W *)buffer.data();
|
|
94
|
-
info.status = GetPrinterStatus(pInfo->Status);
|
|
95
|
-
|
|
96
|
-
if (pInfo->pLocation)
|
|
97
|
-
info.details["location"] = WideToUtf8(pInfo->pLocation);
|
|
98
|
-
if (pInfo->pComment)
|
|
99
|
-
info.details["comment"] = WideToUtf8(pInfo->pComment);
|
|
100
|
-
if (pInfo->pDriverName)
|
|
101
|
-
info.details["driver"] = WideToUtf8(pInfo->pDriverName);
|
|
102
|
-
if (pInfo->pPortName)
|
|
103
|
-
info.details["port"] = WideToUtf8(pInfo->pPortName);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
ClosePrinter(hPrinter);
|
|
107
|
-
}
|
|
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;
|
|
108
54
|
|
|
109
|
-
|
|
55
|
+
unsigned long long unix100ns = ull.QuadPart - EPOCH_DIFFERENCE;
|
|
56
|
+
return (std::time_t)(unix100ns / 10000000ULL);
|
|
110
57
|
}
|
|
111
58
|
|
|
112
|
-
std::vector<
|
|
59
|
+
std::vector<std::string> WindowsPrinter::MapJobStatus(DWORD status)
|
|
113
60
|
{
|
|
114
|
-
std::vector<
|
|
115
|
-
|
|
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");
|
|
116
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;
|
|
117
98
|
EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, NULL, 0, &needed, &returned);
|
|
118
|
-
if (needed
|
|
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++)
|
|
119
114
|
{
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (GetDefaultPrinterW(defaultPrinter, &size))
|
|
132
|
-
{
|
|
133
|
-
isDefault = (wcscmp(pInfo[i].pPrinterName, defaultPrinter) == 0);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
printers.push_back(GetPrinterDetails(name, isDefault));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
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));
|
|
139
126
|
}
|
|
140
127
|
|
|
141
128
|
return printers;
|
|
142
129
|
}
|
|
143
130
|
|
|
144
|
-
|
|
131
|
+
PrinterDetailsNative WindowsPrinter::GetPrinter(const std::string &printerName)
|
|
145
132
|
{
|
|
146
|
-
|
|
147
|
-
|
|
133
|
+
// Build from GetPrinters (stable + consistent)
|
|
134
|
+
auto list = GetPrinters();
|
|
135
|
+
for (auto &p : list)
|
|
136
|
+
{
|
|
137
|
+
if (p.name == printerName) return p;
|
|
138
|
+
}
|
|
148
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]));
|
|
149
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)
|
|
150
197
|
{
|
|
151
|
-
|
|
152
|
-
|
|
198
|
+
// Fallback: return numeric code as string (still useful)
|
|
199
|
+
paper = std::to_string((int)dm->dmPaperSize);
|
|
153
200
|
}
|
|
154
201
|
|
|
155
|
-
|
|
202
|
+
ClosePrinter(hPrinter);
|
|
203
|
+
return paper;
|
|
156
204
|
}
|
|
157
205
|
|
|
158
|
-
|
|
206
|
+
std::vector<std::string> WindowsPrinter::GetSupportedPrintFormats()
|
|
159
207
|
{
|
|
160
|
-
|
|
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;
|
|
161
220
|
std::wstring wPrinterName = Utf8ToWide(printerName);
|
|
162
|
-
std::wstring wDataType = Utf8ToWide(dataType);
|
|
163
221
|
|
|
164
222
|
if (!OpenPrinterW((LPWSTR)wPrinterName.c_str(), &hPrinter, NULL))
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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");
|
|
168
229
|
|
|
169
230
|
DOC_INFO_1W docInfo;
|
|
170
|
-
wchar_t docName[] = L"Node
|
|
231
|
+
wchar_t docName[] = L"Node Print Job";
|
|
171
232
|
docInfo.pDocName = docName;
|
|
172
233
|
docInfo.pOutputFile = NULL;
|
|
173
|
-
docInfo.pDatatype =
|
|
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
|
+
}
|
|
174
242
|
|
|
175
|
-
if (
|
|
243
|
+
if (!StartPagePrinter(hPrinter))
|
|
176
244
|
{
|
|
177
|
-
if (StartPagePrinter(hPrinter))
|
|
178
|
-
{
|
|
179
|
-
DWORD bytesWritten;
|
|
180
|
-
void *buffer = const_cast<void *>(static_cast<const void *>(data.data()));
|
|
181
|
-
if (WritePrinter(hPrinter, buffer, static_cast<DWORD>(data.size()), &bytesWritten))
|
|
182
|
-
{
|
|
183
|
-
EndPagePrinter(hPrinter);
|
|
184
|
-
EndDocPrinter(hPrinter);
|
|
185
|
-
ClosePrinter(hPrinter);
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
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;
|
|
190
299
|
}
|
|
191
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
|
+
|
|
192
315
|
ClosePrinter(hPrinter);
|
|
193
|
-
return
|
|
316
|
+
return j;
|
|
194
317
|
}
|
|
195
318
|
|
|
196
|
-
|
|
319
|
+
void WindowsPrinter::SetJob(const std::string &printerName, int jobId, const std::string &command)
|
|
197
320
|
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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);
|
|
201
327
|
|
|
202
|
-
if (
|
|
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")
|
|
203
337
|
{
|
|
204
|
-
|
|
205
|
-
isDefault = (printerName == defaultPrinterName);
|
|
338
|
+
SetJobW(hPrinter, (DWORD)jobId, 0, NULL, JOB_CONTROL_RESUME);
|
|
206
339
|
}
|
|
207
340
|
|
|
208
|
-
|
|
209
|
-
return printer;
|
|
341
|
+
ClosePrinter(hPrinter);
|
|
210
342
|
}
|
|
343
|
+
|
|
344
|
+
std::vector<std::string> WindowsPrinter::GetSupportedJobCommands()
|
|
345
|
+
{
|
|
346
|
+
return { "CANCEL", "PAUSE", "RESUME" };
|
|
347
|
+
}
|
package/src/windows_printer.h
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
#ifndef WINDOWS_PRINTER_H
|
|
2
2
|
#define WINDOWS_PRINTER_H
|
|
3
3
|
|
|
4
|
-
#
|
|
4
|
+
#include "printer_interface.h"
|
|
5
|
+
|
|
5
6
|
#include <windows.h>
|
|
6
7
|
#include <winspool.h>
|
|
7
|
-
#include <cstdint>
|
|
8
|
-
#include "printer_interface.h"
|
|
9
|
-
#include <vector>
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
/* Now remove macro pollution */
|
|
10
|
+
#ifdef GetDefaultPrinter
|
|
11
|
+
#undef GetDefaultPrinter
|
|
12
|
+
#endif
|
|
13
|
+
|
|
12
14
|
#ifdef GetDefaultPrinter
|
|
13
15
|
#undef GetDefaultPrinter
|
|
14
16
|
#endif
|
|
@@ -17,19 +19,46 @@
|
|
|
17
19
|
#undef GetPrinter
|
|
18
20
|
#endif
|
|
19
21
|
|
|
22
|
+
#ifdef GetJob
|
|
23
|
+
#undef GetJob
|
|
24
|
+
#endif
|
|
25
|
+
|
|
26
|
+
#ifdef SetJob
|
|
27
|
+
#undef SetJob
|
|
28
|
+
#endif
|
|
29
|
+
|
|
30
|
+
#include <vector>
|
|
31
|
+
#include <string>
|
|
32
|
+
#include <cstdint>
|
|
33
|
+
|
|
20
34
|
class WindowsPrinter : public PrinterInterface
|
|
21
35
|
{
|
|
22
36
|
private:
|
|
23
|
-
std::string GetPrinterStatus(DWORD status);
|
|
24
37
|
std::wstring Utf8ToWide(const std::string &str);
|
|
25
38
|
std::string WideToUtf8(LPWSTR wstr);
|
|
39
|
+
std::vector<std::string> MapJobStatus(DWORD status);
|
|
26
40
|
|
|
27
41
|
public:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
std::vector<PrinterDetailsNative> GetPrinters() override;
|
|
43
|
+
PrinterDetailsNative GetPrinter(const std::string &printerName) override;
|
|
44
|
+
std::string GetDefaultPrinterName() override;
|
|
45
|
+
|
|
46
|
+
DriverOptions GetPrinterDriverOptions(const std::string &printerName) override;
|
|
47
|
+
std::string GetSelectedPaperSize(const std::string &printerName) override;
|
|
48
|
+
|
|
49
|
+
int PrintDirect(const std::string &printerName,
|
|
50
|
+
const std::vector<uint8_t> &data,
|
|
51
|
+
const std::string &type,
|
|
52
|
+
const StringMap &options) override;
|
|
53
|
+
|
|
54
|
+
int PrintFile(const std::string &printerName,
|
|
55
|
+
const std::string &filename) override;
|
|
56
|
+
|
|
57
|
+
std::vector<std::string> GetSupportedPrintFormats() override;
|
|
58
|
+
|
|
59
|
+
JobDetailsNative GetJob(const std::string &printerName, int jobId) override;
|
|
60
|
+
void SetJob(const std::string &printerName, int jobId, const std::string &command) override;
|
|
61
|
+
std::vector<std::string> GetSupportedJobCommands() override;
|
|
33
62
|
};
|
|
34
63
|
|
|
35
64
|
#endif
|
package/test.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const printer = require('./lib');
|
|
2
|
+
|
|
3
|
+
async function run() {
|
|
4
|
+
console.log("===== TEST START =====");
|
|
5
|
+
|
|
6
|
+
// -------------------------------------------------
|
|
7
|
+
// 1️⃣ Get printers (sync)
|
|
8
|
+
// -------------------------------------------------
|
|
9
|
+
const printers = printer.getPrinters();
|
|
10
|
+
console.log("Printers:", printers);
|
|
11
|
+
|
|
12
|
+
if (!printers.length) {
|
|
13
|
+
console.log("No printers found. Exiting.");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const defaultPrinter = printer.getDefaultPrinterName();
|
|
18
|
+
console.log("Default printer:", defaultPrinter);
|
|
19
|
+
|
|
20
|
+
const selectedPrinter = defaultPrinter || printers[0].name;
|
|
21
|
+
console.log("Using printer:", selectedPrinter);
|
|
22
|
+
|
|
23
|
+
// -------------------------------------------------
|
|
24
|
+
// 2️⃣ Get single printer
|
|
25
|
+
// -------------------------------------------------
|
|
26
|
+
const printerDetails = printer.getPrinter(selectedPrinter);
|
|
27
|
+
console.log("Printer details:", printerDetails);
|
|
28
|
+
|
|
29
|
+
// -------------------------------------------------
|
|
30
|
+
// 3️⃣ Driver options
|
|
31
|
+
// -------------------------------------------------
|
|
32
|
+
const driverOptions = printer.getPrinterDriverOptions(selectedPrinter);
|
|
33
|
+
console.log("Driver options:", driverOptions);
|
|
34
|
+
|
|
35
|
+
// -------------------------------------------------
|
|
36
|
+
// 4️⃣ Paper size
|
|
37
|
+
// -------------------------------------------------
|
|
38
|
+
const paper = printer.getSelectedPaperSize(selectedPrinter);
|
|
39
|
+
console.log("Selected paper size:", paper);
|
|
40
|
+
|
|
41
|
+
// -------------------------------------------------
|
|
42
|
+
// 5️⃣ Supported formats
|
|
43
|
+
// -------------------------------------------------
|
|
44
|
+
console.log("Supported formats:", printer.getSupportedPrintFormats());
|
|
45
|
+
|
|
46
|
+
// -------------------------------------------------
|
|
47
|
+
// 6️⃣ Supported job commands
|
|
48
|
+
// -------------------------------------------------
|
|
49
|
+
console.log("Supported job commands:", printer.getSupportedJobCommands());
|
|
50
|
+
|
|
51
|
+
// -------------------------------------------------
|
|
52
|
+
// 7️⃣ printDirect (callback version)
|
|
53
|
+
// -------------------------------------------------
|
|
54
|
+
printer.printDirect({
|
|
55
|
+
data: "TEST PRINT FROM CALLBACK\n\n",
|
|
56
|
+
printer: selectedPrinter,
|
|
57
|
+
type: "RAW",
|
|
58
|
+
success: (jobId) => {
|
|
59
|
+
console.log("printDirect success jobId:", jobId);
|
|
60
|
+
|
|
61
|
+
const job = printer.getJob(selectedPrinter, parseInt(jobId));
|
|
62
|
+
console.log("Job details (sync):", job);
|
|
63
|
+
|
|
64
|
+
// Try cancel test (optional)
|
|
65
|
+
// printer.setJob(selectedPrinter, parseInt(jobId), "CANCEL");
|
|
66
|
+
},
|
|
67
|
+
error: (err) => {
|
|
68
|
+
console.error("printDirect error:", err);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// -------------------------------------------------
|
|
73
|
+
// 8️⃣ printDirectAsync
|
|
74
|
+
// -------------------------------------------------
|
|
75
|
+
try {
|
|
76
|
+
const jobId = await printer.printDirectAsync({
|
|
77
|
+
data: Buffer.from("TEST PRINT FROM ASYNC\n\n"),
|
|
78
|
+
printer: selectedPrinter,
|
|
79
|
+
type: "RAW"
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
console.log("printDirectAsync jobId:", jobId);
|
|
83
|
+
|
|
84
|
+
const jobAsync = await printer.getJobAsync(selectedPrinter, parseInt(jobId));
|
|
85
|
+
console.log("Job details (async):", jobAsync);
|
|
86
|
+
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error("printDirectAsync error:", err);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// -------------------------------------------------
|
|
92
|
+
// 9️⃣ printFileAsync
|
|
93
|
+
// -------------------------------------------------
|
|
94
|
+
try {
|
|
95
|
+
const jobId = await printer.printFileAsync({
|
|
96
|
+
filename: "./sample.txt", // make sure this exists
|
|
97
|
+
printer: selectedPrinter
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
console.log("printFileAsync jobId:", jobId);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.error("printFileAsync error:", err);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// -------------------------------------------------
|
|
106
|
+
// 🔟 getPrintersAsync
|
|
107
|
+
// -------------------------------------------------
|
|
108
|
+
const printersAsync = await printer.getPrintersAsync();
|
|
109
|
+
console.log("Printers (async):", printersAsync);
|
|
110
|
+
|
|
111
|
+
// -------------------------------------------------
|
|
112
|
+
// 11️⃣ getPrinterAsync
|
|
113
|
+
// -------------------------------------------------
|
|
114
|
+
const singlePrinterAsync = await printer.getPrinterAsync(selectedPrinter);
|
|
115
|
+
console.log("Single printer (async):", singlePrinterAsync);
|
|
116
|
+
|
|
117
|
+
console.log("===== TEST END =====");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
run().catch(console.error);
|