@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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@esslassi/electron-printer",
3
3
  "description": "Node.js and Electron bindings",
4
- "version": "0.0.6",
4
+ "version": "0.0.8",
5
5
  "main": "./lib/index.js",
6
6
  "private": false,
7
7
  "scripts": {
@@ -1,171 +1,371 @@
1
1
  #include "linux_printer.h"
2
+
2
3
  #include <cups/cups.h>
3
4
  #include <cups/ppd.h>
5
+ #include <cups/ipp.h>
6
+ #include <cups/http.h>
7
+
8
+ #include <vector>
9
+ #include <string>
10
+ #include <map>
11
+ #include <fstream>
12
+ #include <algorithm>
13
+ #include <unistd.h>
14
+ #include <cstring>
15
+
16
+ /* =========================================================
17
+ Helpers
18
+ ========================================================= */
4
19
 
5
- std::string LinuxPrinter::GetPrinterStatus(ipp_pstate_t state)
20
+ static std::string ToUpper(std::string s)
6
21
  {
7
- switch (state)
8
- {
9
- case IPP_PRINTER_IDLE:
10
- return "ready";
11
- case IPP_PRINTER_PROCESSING:
12
- return "printing";
13
- case IPP_PRINTER_STOPPED:
14
- return "stopped";
15
- default:
16
- return "unknown";
17
- }
22
+ std::transform(s.begin(), s.end(), s.begin(),
23
+ [](unsigned char c) { return (char)std::toupper(c); });
24
+ return s;
18
25
  }
19
26
 
20
- PrinterInfo LinuxPrinter::GetPrinterDetails(const std::string &printerName, bool isDefault)
27
+ static std::vector<uint8_t> ReadAllBytes(const std::string &path)
21
28
  {
22
- PrinterInfo info;
23
- info.name = printerName;
24
- info.isDefault = isDefault;
29
+ std::ifstream f(path, std::ios::binary);
30
+ if (!f) return {};
31
+
32
+ f.seekg(0, std::ios::end);
33
+ std::streamsize size = f.tellg();
34
+ if (size < 0) return {};
25
35
 
26
- cups_dest_t *dests;
27
- int num_dests = cupsGetDests(&dests);
28
- cups_dest_t *dest = cupsGetDest(printerName.c_str(), NULL, num_dests, dests);
36
+ f.seekg(0, std::ios::beg);
37
+
38
+ std::vector<uint8_t> buf((size_t)size);
39
+ if (size > 0)
40
+ f.read((char*)buf.data(), size);
41
+
42
+ return buf;
43
+ }
44
+
45
+ /* =========================================================
46
+ Printer Listing
47
+ ========================================================= */
48
+
49
+ std::vector<PrinterDetailsNative> LinuxPrinter::GetPrinters()
50
+ {
51
+ std::vector<PrinterDetailsNative> out;
29
52
 
30
- if (dest != NULL)
53
+ cups_dest_t *dests = nullptr;
54
+ int num = cupsGetDests(&dests);
55
+
56
+ for (int i = 0; i < num; i++)
31
57
  {
32
- for (int i = 0; i < dest->num_options; i++)
58
+ PrinterDetailsNative p;
59
+ p.name = dests[i].name ? dests[i].name : "";
60
+ p.isDefault = dests[i].is_default != 0;
61
+
62
+ for (int k = 0; k < dests[i].num_options; k++)
33
63
  {
34
- info.details[dest->options[i].name] = dest->options[i].value;
64
+ if (dests[i].options[k].name && dests[i].options[k].value)
65
+ p.options[dests[i].options[k].name] = dests[i].options[k].value;
35
66
  }
36
67
 
37
- http_t *http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
38
- HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
39
- if (http != NULL)
40
- {
41
- ipp_t *request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
68
+ out.push_back(std::move(p));
69
+ }
42
70
 
43
- char uri[HTTP_MAX_URI];
44
- httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
45
- "localhost", 0, "/printers/%s", printerName.c_str());
71
+ cupsFreeDests(num, dests);
72
+ return out;
73
+ }
46
74
 
47
- ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
48
- "printer-uri", NULL, uri);
75
+ PrinterDetailsNative LinuxPrinter::GetPrinter(const std::string &printerName)
76
+ {
77
+ auto list = GetPrinters();
78
+ for (auto &p : list)
79
+ if (p.name == printerName)
80
+ return p;
49
81
 
50
- ipp_t *response = cupsDoRequest(http, request, "/");
51
- if (response != NULL)
52
- {
53
- ipp_attribute_t *attr = ippFindAttribute(response,
54
- "printer-state", IPP_TAG_ENUM);
55
- if (attr != NULL)
56
- {
57
- info.status = GetPrinterStatus((ipp_pstate_t)ippGetInteger(attr, 0));
58
- }
59
-
60
- attr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT);
61
- if (attr != NULL)
62
- info.details["location"] = ippGetString(attr, 0, NULL);
63
-
64
- attr = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT);
65
- if (attr != NULL)
66
- info.details["comment"] = ippGetString(attr, 0, NULL);
67
-
68
- attr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT);
69
- if (attr != NULL)
70
- info.details["driver"] = ippGetString(attr, 0, NULL);
71
-
72
- attr = ippFindAttribute(response, "port", IPP_TAG_TEXT);
73
- if (attr != NULL)
74
- info.details["port"] = ippGetString(attr, 0, NULL);
75
-
76
- ippDelete(response);
77
- }
78
- httpClose(http);
79
- }
80
- }
81
- cupsFreeDests(num_dests, dests);
82
- return info;
82
+ PrinterDetailsNative p;
83
+ p.name = printerName;
84
+ p.isDefault = false;
85
+ return p;
83
86
  }
84
87
 
85
- std::vector<PrinterInfo> LinuxPrinter::GetPrinters()
88
+ std::string LinuxPrinter::GetDefaultPrinterName()
86
89
  {
87
- std::vector<PrinterInfo> printers;
88
- cups_dest_t *dests;
89
- int num_dests = cupsGetDests(&dests);
90
+ cups_dest_t *dests = nullptr;
91
+ int num = cupsGetDests(&dests);
92
+
93
+ cups_dest_t *def = cupsGetDest(NULL, NULL, num, dests);
90
94
 
91
- for (int i = 0; i < num_dests; i++)
95
+ std::string name;
96
+ if (def && def->name)
97
+ name = def->name;
98
+
99
+ cupsFreeDests(num, dests);
100
+ return name;
101
+ }
102
+
103
+ /* =========================================================
104
+ Driver Options / Paper
105
+ ========================================================= */
106
+
107
+ DriverOptions LinuxPrinter::GetPrinterDriverOptions(const std::string &printerName)
108
+ {
109
+ DriverOptions out;
110
+
111
+ const char *ppdPath = cupsGetPPD(printerName.c_str());
112
+ if (!ppdPath) return out;
113
+
114
+ ppd_file_t *ppd = ppdOpenFile(ppdPath);
115
+ if (!ppd)
116
+ {
117
+ unlink(ppdPath);
118
+ return out;
119
+ }
120
+
121
+ ppdMarkDefaults(ppd);
122
+
123
+ for (ppd_option_t *opt = ppd->options; opt; opt = opt->next)
92
124
  {
93
- printers.push_back(GetPrinterDetails(dests[i].name, dests[i].is_default));
125
+ std::map<std::string, bool> choices;
126
+
127
+ for (int i = 0; i < opt->num_choices; i++)
128
+ {
129
+ bool isDefault =
130
+ (opt->defchoice &&
131
+ strcmp(opt->defchoice, opt->choices[i].choice) == 0);
132
+
133
+ choices[opt->choices[i].choice] = isDefault;
134
+ }
135
+
136
+ out[opt->keyword ? opt->keyword : ""] = choices;
94
137
  }
95
138
 
96
- cupsFreeDests(num_dests, dests);
97
- return printers;
139
+ ppdClose(ppd);
140
+ unlink(ppdPath);
141
+
142
+ return out;
98
143
  }
99
144
 
100
- PrinterInfo LinuxPrinter::GetSystemDefaultPrinter()
145
+ std::string LinuxPrinter::GetSelectedPaperSize(const std::string &printerName)
101
146
  {
102
- cups_dest_t *dests;
103
- int num_dests = cupsGetDests(&dests);
104
- cups_dest_t *dest = cupsGetDest(NULL, NULL, num_dests, dests);
147
+ std::string paper;
105
148
 
106
- PrinterInfo printer;
107
- if (dest != NULL)
149
+ const char *ppdPath = cupsGetPPD(printerName.c_str());
150
+ if (!ppdPath) return paper;
151
+
152
+ ppd_file_t *ppd = ppdOpenFile(ppdPath);
153
+ if (!ppd)
108
154
  {
109
- printer = GetPrinterDetails(dest->name, true);
155
+ unlink(ppdPath);
156
+ return paper;
110
157
  }
111
158
 
112
- cupsFreeDests(num_dests, dests);
113
- return printer;
159
+ ppdMarkDefaults(ppd);
160
+
161
+ ppd_option_t *opt = ppdFindOption(ppd, "PageSize");
162
+ if (!opt)
163
+ opt = ppdFindOption(ppd, "PageRegion");
164
+
165
+ if (opt && opt->defchoice)
166
+ paper = opt->defchoice;
167
+
168
+ ppdClose(ppd);
169
+ unlink(ppdPath);
170
+
171
+ return paper;
114
172
  }
115
173
 
116
- bool LinuxPrinter::PrintDirect(const std::string &printerName,
117
- const std::vector<uint8_t> &data,
118
- const std::string &dataType)
174
+ /* =========================================================
175
+ Capabilities
176
+ ========================================================= */
177
+
178
+ std::vector<std::string> LinuxPrinter::GetSupportedPrintFormats()
179
+ {
180
+ return { "RAW", "TEXT", "PDF", "JPEG", "POSTSCRIPT", "COMMAND", "AUTO" };
181
+ }
182
+
183
+ /* =========================================================
184
+ Printing
185
+ ========================================================= */
186
+
187
+ int LinuxPrinter::PrintDirect(const std::string &printerName,
188
+ const std::vector<uint8_t> &data,
189
+ const std::string &type,
190
+ const StringMap &options)
119
191
  {
120
- int jobId = cupsCreateJob(CUPS_HTTP_DEFAULT, printerName.c_str(),
121
- "Node.js Print Job", 0, NULL);
192
+ std::string t = ToUpper(type);
193
+
194
+ // For PDF/JPEG/POSTSCRIPT -> use temp file + cupsPrintFile
195
+ if (t == "PDF" || t == "JPEG" || t == "POSTSCRIPT")
196
+ {
197
+ char tmpName[] = "/tmp/esslassi_print_XXXXXX";
198
+ int fd = mkstemp(tmpName);
199
+ if (fd < 0)
200
+ return 0;
201
+
202
+ FILE *fp = fdopen(fd, "wb");
203
+ if (!fp)
204
+ {
205
+ close(fd);
206
+ return 0;
207
+ }
208
+
209
+ fwrite(data.data(), 1, data.size(), fp);
210
+ fclose(fp);
211
+
212
+ cups_option_t *cupOpts = nullptr;
213
+ int num = 0;
214
+
215
+ for (auto &kv : options)
216
+ num = cupsAddOption(kv.first.c_str(),
217
+ kv.second.c_str(),
218
+ num,
219
+ &cupOpts);
220
+
221
+ int jobId = cupsPrintFile(
222
+ printerName.c_str(),
223
+ tmpName,
224
+ "Node Print Job",
225
+ num,
226
+ cupOpts);
227
+
228
+ if (cupOpts)
229
+ cupsFreeOptions(num, cupOpts);
230
+
231
+ unlink(tmpName);
232
+
233
+ return jobId > 0 ? jobId : 0;
234
+ }
235
+
236
+ // RAW / TEXT / COMMAND
237
+ int jobId = cupsCreateJob(
238
+ CUPS_HTTP_DEFAULT,
239
+ printerName.c_str(),
240
+ "Node Print Job",
241
+ 0,
242
+ NULL);
122
243
 
123
244
  if (jobId <= 0)
124
- return false;
245
+ return 0;
125
246
 
126
- http_status_t status = cupsStartDocument(CUPS_HTTP_DEFAULT, printerName.c_str(),
127
- jobId, "Node.js Print Job",
128
- dataType.c_str(), 1);
247
+ http_status_t st = cupsStartDocument(
248
+ CUPS_HTTP_DEFAULT,
249
+ printerName.c_str(),
250
+ jobId,
251
+ "Node Print Job",
252
+ CUPS_FORMAT_RAW,
253
+ 1);
129
254
 
130
- if (status != HTTP_STATUS_CONTINUE)
255
+ if (st != HTTP_STATUS_CONTINUE)
131
256
  {
132
257
  cupsCancelJob(printerName.c_str(), jobId);
133
- return false;
258
+ return 0;
134
259
  }
135
260
 
136
- if (cupsWriteRequestData(CUPS_HTTP_DEFAULT,
137
- reinterpret_cast<const char *>(data.data()),
138
- data.size()) != HTTP_STATUS_CONTINUE)
261
+ if (cupsWriteRequestData(
262
+ CUPS_HTTP_DEFAULT,
263
+ (const char*)data.data(),
264
+ data.size()) != HTTP_STATUS_CONTINUE)
139
265
  {
140
266
  cupsCancelJob(printerName.c_str(), jobId);
141
- return false;
267
+ return 0;
142
268
  }
143
269
 
144
- status = static_cast<http_status_t>(cupsFinishDocument(CUPS_HTTP_DEFAULT, printerName.c_str()));
145
- return status == HTTP_STATUS_OK;
270
+ ipp_status_t fin =
271
+ (ipp_status_t)cupsFinishDocument(
272
+ CUPS_HTTP_DEFAULT,
273
+ printerName.c_str());
274
+
275
+ if (fin > IPP_STATUS_OK_CONFLICT)
276
+ return 0;
277
+
278
+ return jobId;
279
+ }
280
+
281
+ int LinuxPrinter::PrintFile(const std::string &printerName,
282
+ const std::string &filename)
283
+ {
284
+ int jobId = cupsPrintFile(
285
+ printerName.c_str(),
286
+ filename.c_str(),
287
+ "Node Print Job",
288
+ 0,
289
+ NULL);
290
+
291
+ return jobId > 0 ? jobId : 0;
146
292
  }
147
293
 
148
- PrinterInfo LinuxPrinter::GetStatusPrinter(const std::string &printerName)
294
+ /* =========================================================
295
+ Job Management
296
+ ========================================================= */
297
+
298
+ JobDetailsNative LinuxPrinter::GetJob(const std::string &printerName, int jobId)
149
299
  {
150
- cups_dest_t *dests;
151
- int num_dests = cupsGetDests(&dests);
300
+ JobDetailsNative j;
301
+ j.id = jobId;
302
+ j.printerName = printerName;
303
+
304
+ cups_job_t *jobs = nullptr;
305
+ int num = cupsGetJobs(&jobs,
306
+ printerName.c_str(),
307
+ 0,
308
+ CUPS_WHICHJOBS_ALL);
309
+
310
+ for (int i = 0; i < num; i++)
311
+ {
312
+ if (jobs[i].id == jobId)
313
+ {
314
+ j.name = jobs[i].title ? jobs[i].title : "";
315
+ j.user = jobs[i].user ? jobs[i].user : "";
316
+ j.format = jobs[i].format ? jobs[i].format : "";
317
+ j.priority = jobs[i].priority;
318
+ j.size = jobs[i].size;
319
+
320
+ switch (jobs[i].state)
321
+ {
322
+ case IPP_JSTATE_PENDING: j.status = { "PENDING" }; break;
323
+ case IPP_JSTATE_HELD: j.status = { "PAUSED" }; break;
324
+ case IPP_JSTATE_PROCESSING: j.status = { "PRINTING" }; break;
325
+ case IPP_JSTATE_STOPPED: j.status = { "ABORTED" }; break;
326
+ case IPP_JSTATE_CANCELED: j.status = { "CANCELLED" }; break;
327
+ case IPP_JSTATE_ABORTED: j.status = { "ABORTED" }; break;
328
+ case IPP_JSTATE_COMPLETED: j.status = { "PRINTED" }; break;
329
+ default: j.status = { "PENDING" }; break;
330
+ }
152
331
 
153
- cups_dest_t *defaultDest = cupsGetDest(NULL, NULL, num_dests, dests);
154
- bool isDefault = false;
332
+ j.creationTime = jobs[i].creation_time;
333
+ j.processingTime = jobs[i].processing_time;
334
+ j.completedTime = jobs[i].completed_time;
335
+ break;
336
+ }
337
+ }
338
+
339
+ cupsFreeJobs(num, jobs);
340
+ return j;
341
+ }
342
+
343
+ void LinuxPrinter::SetJob(const std::string &printerName,
344
+ int jobId,
345
+ const std::string &command)
346
+ {
347
+ std::string cmd = ToUpper(command);
155
348
 
156
- if (defaultDest != NULL)
349
+ if (cmd == "CANCEL")
157
350
  {
158
- isDefault = (printerName == defaultDest->name);
351
+ cupsCancelJob(printerName.c_str(), jobId);
352
+ return;
159
353
  }
160
354
 
161
- cups_dest_t *dest = cupsGetDest(printerName.c_str(), NULL, num_dests, dests);
162
- PrinterInfo printer;
355
+ if (cmd == "PAUSE" || cmd == "HOLD")
356
+ {
357
+ cupsHoldJob(printerName.c_str(), jobId);
358
+ return;
359
+ }
163
360
 
164
- if (dest != NULL)
361
+ if (cmd == "RESUME" || cmd == "RELEASE")
165
362
  {
166
- printer = GetPrinterDetails(printerName, isDefault);
363
+ cupsReleaseJob(printerName.c_str(), jobId);
364
+ return;
167
365
  }
366
+ }
168
367
 
169
- cupsFreeDests(num_dests, dests);
170
- return printer;
368
+ std::vector<std::string> LinuxPrinter::GetSupportedJobCommands()
369
+ {
370
+ return { "CANCEL", "PAUSE", "RESUME" };
171
371
  }
@@ -1,21 +1,31 @@
1
1
  #ifndef LINUX_PRINTER_H
2
2
  #define LINUX_PRINTER_H
3
- #include <cups/cups.h>
4
- #include <cups/ppd.h>
5
- #include <cstdint>
3
+
6
4
  #include "printer_interface.h"
7
5
 
8
6
  class LinuxPrinter : public PrinterInterface
9
7
  {
10
- private:
11
- std::string GetPrinterStatus(ipp_pstate_t state);
12
-
13
8
  public:
14
- virtual PrinterInfo GetPrinterDetails(const std::string &printerName, bool isDefault = false) override;
15
- virtual std::vector<PrinterInfo> GetPrinters() override;
16
- virtual PrinterInfo GetSystemDefaultPrinter() override;
17
- virtual bool PrintDirect(const std::string &printerName, const std::vector<uint8_t> &data, const std::string &dataType) override;
18
- virtual PrinterInfo GetStatusPrinter(const std::string &printerName) override;
9
+ std::vector<PrinterDetailsNative> GetPrinters() override;
10
+ PrinterDetailsNative GetPrinter(const std::string &printerName) override;
11
+ std::string GetDefaultPrinterName() override;
12
+
13
+ DriverOptions GetPrinterDriverOptions(const std::string &printerName) override;
14
+ std::string GetSelectedPaperSize(const std::string &printerName) override;
15
+
16
+ int PrintDirect(const std::string &printerName,
17
+ const std::vector<uint8_t> &data,
18
+ const std::string &type,
19
+ const StringMap &options) override;
20
+
21
+ int PrintFile(const std::string &printerName,
22
+ const std::string &filename) override;
23
+
24
+ std::vector<std::string> GetSupportedPrintFormats() override;
25
+
26
+ JobDetailsNative GetJob(const std::string &printerName, int jobId) override;
27
+ void SetJob(const std::string &printerName, int jobId, const std::string &command) override;
28
+ std::vector<std::string> GetSupportedJobCommands() override;
19
29
  };
20
30
 
21
31
  #endif