@luckykiet/node-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/ChangeLog +36 -0
- package/Gruntfile.js +75 -0
- package/README.md +61 -0
- package/binding.gyp +77 -0
- package/examples/cancelJob.js +34 -0
- package/examples/example_zebra_printer.js +15 -0
- package/examples/getDefaultPrinterName.js +4 -0
- package/examples/getPrinterDriverOptions.js +9 -0
- package/examples/getPrinters.js +3 -0
- package/examples/getSupportedFormats.js +3 -0
- package/examples/getSupportedJobCommands.js +3 -0
- package/examples/printFile.js +30 -0
- package/examples/printPDFFileInBuffer.js +37 -0
- package/examples/printPDFInWindows.js +52 -0
- package/examples/print_raw.js +10 -0
- package/examples/test.pdf +0 -0
- package/lib/index.js +1 -0
- package/lib/node_printer.node +0 -0
- package/lib/printer.js +330 -0
- package/package.json +48 -0
- package/printer.js +1 -0
- package/src/macros.hh +159 -0
- package/src/node_printer.cc +41 -0
- package/src/node_printer.hpp +123 -0
- package/src/node_printer_posix.cc +530 -0
- package/src/node_printer_win.cc +750 -0
- package/test/getPrinters.js +14 -0
- package/tools/buildElectronLinux.sh +10 -0
- package/tools/buildElectronWindows.ps1 +20 -0
- package/tools/buildWindows.ps1 +23 -0
- package/tools/generateReleaseBuildsLinux.sh +59 -0
- package/tools/generateReleaseBuildsWindows.ps1 +63 -0
- package/tools/getSourceFiles.py +12 -0
- package/tsconfig.json +22 -0
- package/types/index.d.ts +57 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#ifndef NODE_PRINTER_HPP
|
|
2
|
+
#define NODE_PRINTER_HPP
|
|
3
|
+
|
|
4
|
+
#include "macros.hh"
|
|
5
|
+
|
|
6
|
+
#include <node.h>
|
|
7
|
+
#include <v8.h>
|
|
8
|
+
|
|
9
|
+
#include <string>
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Send data to printer
|
|
13
|
+
*
|
|
14
|
+
* @param data String/NativeBuffer, mandatory, raw data bytes
|
|
15
|
+
* @param printername String, mandatory, specifying printer name
|
|
16
|
+
* @param docname String, mandatory, specifying document name
|
|
17
|
+
* @param type String, mandatory, specifying data type. E.G.: RAW, TEXT, ...
|
|
18
|
+
*
|
|
19
|
+
* @returns true for success, false for failure.
|
|
20
|
+
*/
|
|
21
|
+
MY_NODE_MODULE_CALLBACK(PrintDirect);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Send file to printer
|
|
25
|
+
*
|
|
26
|
+
* @param filename String, mandatory, specifying filename to print
|
|
27
|
+
* @param docname String, mandatory, specifying document name
|
|
28
|
+
* @param printer String, mandatory, specifying printer name
|
|
29
|
+
*
|
|
30
|
+
* @returns jobId for success, or error message for failure.
|
|
31
|
+
*/
|
|
32
|
+
MY_NODE_MODULE_CALLBACK(PrintFile);
|
|
33
|
+
|
|
34
|
+
/** Retrieve all printers and jobs
|
|
35
|
+
* posix: minimum version: CUPS 1.1.21/OS X 10.4
|
|
36
|
+
*/
|
|
37
|
+
MY_NODE_MODULE_CALLBACK(getPrinters);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Return default printer name, if null then default printer is not set
|
|
41
|
+
*/
|
|
42
|
+
MY_NODE_MODULE_CALLBACK(getDefaultPrinterName);
|
|
43
|
+
|
|
44
|
+
/** Retrieve printer info and jobs
|
|
45
|
+
* @param printer name String
|
|
46
|
+
*/
|
|
47
|
+
MY_NODE_MODULE_CALLBACK(getPrinter);
|
|
48
|
+
|
|
49
|
+
/** Retrieve printer driver info
|
|
50
|
+
* @param printer name String
|
|
51
|
+
*/
|
|
52
|
+
MY_NODE_MODULE_CALLBACK(getPrinterDriverOptions);
|
|
53
|
+
|
|
54
|
+
/** Retrieve job info
|
|
55
|
+
* @param printer name String
|
|
56
|
+
* @param job id Number
|
|
57
|
+
*/
|
|
58
|
+
MY_NODE_MODULE_CALLBACK(getJob);
|
|
59
|
+
|
|
60
|
+
//TODO
|
|
61
|
+
/** Set job command.
|
|
62
|
+
* arguments:
|
|
63
|
+
* @param printer name String
|
|
64
|
+
* @param job id Number
|
|
65
|
+
* @param job command String
|
|
66
|
+
* Possible commands:
|
|
67
|
+
* "CANCEL"
|
|
68
|
+
* "PAUSE"
|
|
69
|
+
* "RESTART"
|
|
70
|
+
* "RESUME"
|
|
71
|
+
* "DELETE"
|
|
72
|
+
* "SENT-TO-PRINTER"
|
|
73
|
+
* "LAST-PAGE-EJECTED"
|
|
74
|
+
* "RETAIN"
|
|
75
|
+
* "RELEASE"
|
|
76
|
+
*/
|
|
77
|
+
MY_NODE_MODULE_CALLBACK(setJob);
|
|
78
|
+
|
|
79
|
+
/** Get supported print formats for printDirect. It depends on platform
|
|
80
|
+
*/
|
|
81
|
+
MY_NODE_MODULE_CALLBACK(getSupportedPrintFormats);
|
|
82
|
+
|
|
83
|
+
/** Get supported job commands for setJob method
|
|
84
|
+
*/
|
|
85
|
+
MY_NODE_MODULE_CALLBACK(getSupportedJobCommands);
|
|
86
|
+
|
|
87
|
+
//TODO:
|
|
88
|
+
// optional ability to get printer spool
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
// util class
|
|
92
|
+
|
|
93
|
+
/** Memory value class management to avoid memory leak
|
|
94
|
+
* TODO: move to std::unique_ptr on switching to C++11
|
|
95
|
+
*/
|
|
96
|
+
template<typename Type>
|
|
97
|
+
class MemValueBase
|
|
98
|
+
{
|
|
99
|
+
public:
|
|
100
|
+
MemValueBase(): _value(NULL) {}
|
|
101
|
+
|
|
102
|
+
/** Destructor. The allocated memory will be deallocated
|
|
103
|
+
*/
|
|
104
|
+
virtual ~MemValueBase() {}
|
|
105
|
+
|
|
106
|
+
Type * get() {return _value; }
|
|
107
|
+
Type * operator ->() { return &_value; }
|
|
108
|
+
operator bool() const { return (_value != NULL); }
|
|
109
|
+
protected:
|
|
110
|
+
Type *_value;
|
|
111
|
+
|
|
112
|
+
virtual void free() {};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* try to extract String or buffer from v8 value
|
|
117
|
+
* @param iV8Value - source v8 value
|
|
118
|
+
* @param oData - destination data
|
|
119
|
+
* @return TRUE if value is String or Buffer, FALSE otherwise
|
|
120
|
+
*/
|
|
121
|
+
bool getStringOrBufferFromV8Value(v8::Local<v8::Value> iV8Value, std::string &oData);
|
|
122
|
+
|
|
123
|
+
#endif
|
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
#include "node_printer.hpp"
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <map>
|
|
5
|
+
#include <utility>
|
|
6
|
+
#include <sstream>
|
|
7
|
+
#include <node_version.h>
|
|
8
|
+
|
|
9
|
+
#include <cups/cups.h>
|
|
10
|
+
#include <cups/ppd.h>
|
|
11
|
+
|
|
12
|
+
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
13
|
+
|
|
14
|
+
namespace
|
|
15
|
+
{
|
|
16
|
+
typedef std::map<std::string, int> StatusMapType;
|
|
17
|
+
typedef std::map<std::string, std::string> FormatMapType;
|
|
18
|
+
|
|
19
|
+
const StatusMapType& getJobStatusMap()
|
|
20
|
+
{
|
|
21
|
+
static StatusMapType result;
|
|
22
|
+
if(!result.empty())
|
|
23
|
+
{
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
// add only first time
|
|
27
|
+
#define STATUS_PRINTER_ADD(value, type) result.insert(std::make_pair(value, type))
|
|
28
|
+
// Common statuses
|
|
29
|
+
STATUS_PRINTER_ADD("PRINTING", IPP_JOB_PROCESSING);
|
|
30
|
+
STATUS_PRINTER_ADD("PRINTED", IPP_JOB_COMPLETED);
|
|
31
|
+
STATUS_PRINTER_ADD("PAUSED", IPP_JOB_HELD);
|
|
32
|
+
// Specific statuses
|
|
33
|
+
STATUS_PRINTER_ADD("PENDING", IPP_JOB_PENDING);
|
|
34
|
+
STATUS_PRINTER_ADD("PAUSED", IPP_JOB_STOPPED);
|
|
35
|
+
STATUS_PRINTER_ADD("CANCELLED", IPP_JOB_CANCELLED);
|
|
36
|
+
STATUS_PRINTER_ADD("ABORTED", IPP_JOB_ABORTED);
|
|
37
|
+
|
|
38
|
+
#undef STATUS_PRINTER_ADD
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const FormatMapType& getPrinterFormatMap()
|
|
43
|
+
{
|
|
44
|
+
static FormatMapType result;
|
|
45
|
+
if(!result.empty())
|
|
46
|
+
{
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
result.insert(std::make_pair("RAW", CUPS_FORMAT_RAW));
|
|
50
|
+
result.insert(std::make_pair("TEXT", CUPS_FORMAT_TEXT));
|
|
51
|
+
#ifdef CUPS_FORMAT_PDF
|
|
52
|
+
result.insert(std::make_pair("PDF", CUPS_FORMAT_PDF));
|
|
53
|
+
#endif
|
|
54
|
+
#ifdef CUPS_FORMAT_JPEG
|
|
55
|
+
result.insert(std::make_pair("JPEG", CUPS_FORMAT_JPEG));
|
|
56
|
+
#endif
|
|
57
|
+
#ifdef CUPS_FORMAT_POSTSCRIPT
|
|
58
|
+
result.insert(std::make_pair("POSTSCRIPT", CUPS_FORMAT_POSTSCRIPT));
|
|
59
|
+
#endif
|
|
60
|
+
#ifdef CUPS_FORMAT_COMMAND
|
|
61
|
+
result.insert(std::make_pair("COMMAND", CUPS_FORMAT_COMMAND));
|
|
62
|
+
#endif
|
|
63
|
+
#ifdef CUPS_FORMAT_AUTO
|
|
64
|
+
result.insert(std::make_pair("AUTO", CUPS_FORMAT_AUTO));
|
|
65
|
+
#endif
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Parse job info object.
|
|
70
|
+
* @return error string. if empty, then no error
|
|
71
|
+
*/
|
|
72
|
+
std::string parseJobObject(const cups_job_t *job, v8::Local<v8::Object> result_printer_job)
|
|
73
|
+
{
|
|
74
|
+
MY_NODE_MODULE_ISOLATE_DECL
|
|
75
|
+
//Common fields
|
|
76
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("id"), V8_VALUE_NEW(Number, job->id));
|
|
77
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("name"), V8_STRING_NEW_UTF8(job->title));
|
|
78
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("printerName"), V8_STRING_NEW_UTF8(job->dest));
|
|
79
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("user"), V8_STRING_NEW_UTF8(job->user));
|
|
80
|
+
std::string job_format(job->format);
|
|
81
|
+
|
|
82
|
+
// Try to parse the data format, otherwise will write the unformatted one
|
|
83
|
+
for(FormatMapType::const_iterator itFormat = getPrinterFormatMap().begin(); itFormat != getPrinterFormatMap().end(); ++itFormat)
|
|
84
|
+
{
|
|
85
|
+
if(itFormat->second == job_format)
|
|
86
|
+
{
|
|
87
|
+
job_format = itFormat->first;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("format"), V8_STRING_NEW_UTF8(job_format.c_str()));
|
|
93
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("priority"), V8_VALUE_NEW(Number, job->priority));
|
|
94
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("size"), V8_VALUE_NEW(Number, job->size));
|
|
95
|
+
v8::Local<v8::Array> result_printer_job_status = V8_VALUE_NEW_DEFAULT(Array);
|
|
96
|
+
int i_status = 0;
|
|
97
|
+
for(StatusMapType::const_iterator itStatus = getJobStatusMap().begin(); itStatus != getJobStatusMap().end(); ++itStatus)
|
|
98
|
+
{
|
|
99
|
+
if(job->state == itStatus->second)
|
|
100
|
+
{
|
|
101
|
+
Nan::Set(result_printer_job_status, i_status++, V8_STRING_NEW_UTF8(itStatus->first.c_str()));
|
|
102
|
+
// only one status could be on posix
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if(i_status == 0)
|
|
107
|
+
{
|
|
108
|
+
// A new status? report as unsupported
|
|
109
|
+
std::ostringstream s;
|
|
110
|
+
s << "unsupported job status: " << job->state;
|
|
111
|
+
Nan::Set(result_printer_job_status, i_status++, V8_STRING_NEW_UTF8(s.str().c_str()));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("status"), result_printer_job_status);
|
|
115
|
+
|
|
116
|
+
//Specific fields
|
|
117
|
+
// Ecmascript store time in milliseconds, but time_t in seconds
|
|
118
|
+
|
|
119
|
+
double creationTime = ((double)job->creation_time) * 1000;
|
|
120
|
+
double completedTime = ((double)job->completed_time) * 1000;
|
|
121
|
+
double processingTime = ((double)job->processing_time) * 1000;
|
|
122
|
+
|
|
123
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("completedTime"), Nan::New<v8::Date>(completedTime).ToLocalChecked());
|
|
124
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("creationTime"), Nan::New<v8::Date>(creationTime).ToLocalChecked());
|
|
125
|
+
Nan::Set(result_printer_job, V8_STRING_NEW_UTF8("processingTime"), Nan::New<v8::Date>(processingTime).ToLocalChecked());
|
|
126
|
+
|
|
127
|
+
// No error. return an empty string
|
|
128
|
+
return "";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Parses printer driver PPD options
|
|
132
|
+
*/
|
|
133
|
+
void populatePpdOptions(v8::Local<v8::Object> ppd_options, ppd_file_t *ppd, ppd_group_t *group)
|
|
134
|
+
{
|
|
135
|
+
int i, j;
|
|
136
|
+
ppd_option_t *option;
|
|
137
|
+
ppd_choice_t *choice;
|
|
138
|
+
ppd_group_t *subgroup;
|
|
139
|
+
|
|
140
|
+
for (i = group->num_options, option = group->options; i > 0; --i, ++option)
|
|
141
|
+
{
|
|
142
|
+
MY_NODE_MODULE_ISOLATE_DECL
|
|
143
|
+
v8::Local<v8::Object> ppd_suboptions = V8_VALUE_NEW_DEFAULT(Object);
|
|
144
|
+
for (j = option->num_choices, choice = option->choices;
|
|
145
|
+
j > 0;
|
|
146
|
+
--j, ++choice)
|
|
147
|
+
{
|
|
148
|
+
Nan::Set(ppd_suboptions, V8_STRING_NEW_UTF8(choice->choice), V8_VALUE_NEW(Boolean, static_cast<bool>(choice->marked)));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Nan::Set(ppd_options, V8_STRING_NEW_UTF8(option->keyword), ppd_suboptions);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
for (i = group->num_subgroups, subgroup = group->subgroups; i > 0; --i, ++subgroup) {
|
|
155
|
+
populatePpdOptions(ppd_options, ppd, subgroup);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Parse printer driver options
|
|
160
|
+
* @return error string.
|
|
161
|
+
*/
|
|
162
|
+
std::string parseDriverOptions(const cups_dest_t * printer, v8::Local<v8::Object> ppd_options)
|
|
163
|
+
{
|
|
164
|
+
const char *filename;
|
|
165
|
+
ppd_file_t *ppd;
|
|
166
|
+
ppd_group_t *group;
|
|
167
|
+
int i;
|
|
168
|
+
|
|
169
|
+
std::ostringstream error_str; // error string
|
|
170
|
+
|
|
171
|
+
if ((filename = cupsGetPPD(printer->name)) != NULL)
|
|
172
|
+
{
|
|
173
|
+
if ((ppd = ppdOpenFile(filename)) != NULL)
|
|
174
|
+
{
|
|
175
|
+
ppdMarkDefaults(ppd);
|
|
176
|
+
cupsMarkOptions(ppd, printer->num_options, printer->options);
|
|
177
|
+
|
|
178
|
+
for (i = ppd->num_groups, group = ppd->groups; i > 0; --i, ++group)
|
|
179
|
+
{
|
|
180
|
+
populatePpdOptions(ppd_options, ppd, group);
|
|
181
|
+
}
|
|
182
|
+
ppdClose(ppd);
|
|
183
|
+
}
|
|
184
|
+
else
|
|
185
|
+
{
|
|
186
|
+
error_str << "Unable to open PPD filename " << filename << " ";
|
|
187
|
+
}
|
|
188
|
+
unlink(filename);
|
|
189
|
+
}
|
|
190
|
+
else
|
|
191
|
+
{
|
|
192
|
+
error_str << "Unable to get CUPS PPD driver file. ";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return error_str.str();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
/** Parse printer info object
|
|
200
|
+
* @return error string.
|
|
201
|
+
*/
|
|
202
|
+
std::string parsePrinterInfo(const cups_dest_t * printer, v8::Local<v8::Object> result_printer)
|
|
203
|
+
{
|
|
204
|
+
MY_NODE_MODULE_ISOLATE_DECL
|
|
205
|
+
Nan::Set(result_printer, V8_STRING_NEW_UTF8("name"), V8_STRING_NEW_UTF8(printer->name));
|
|
206
|
+
Nan::Set(result_printer, V8_STRING_NEW_UTF8("isDefault"), V8_VALUE_NEW(Boolean, static_cast<bool>(printer->is_default)));
|
|
207
|
+
|
|
208
|
+
if(printer->instance)
|
|
209
|
+
{
|
|
210
|
+
Nan::Set(result_printer, V8_STRING_NEW_UTF8("instance"), V8_STRING_NEW_UTF8(printer->instance));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
v8::Local<v8::Object> result_printer_options = V8_VALUE_NEW_DEFAULT(Object);
|
|
214
|
+
cups_option_t *dest_option = printer->options;
|
|
215
|
+
for(int j = 0; j < printer->num_options; ++j, ++dest_option)
|
|
216
|
+
{
|
|
217
|
+
Nan::Set(result_printer_options, V8_STRING_NEW_UTF8(dest_option->name), V8_STRING_NEW_UTF8(dest_option->value));
|
|
218
|
+
}
|
|
219
|
+
Nan::Set(result_printer, V8_STRING_NEW_UTF8("options"), result_printer_options);
|
|
220
|
+
// Get printer jobs
|
|
221
|
+
cups_job_t * jobs;
|
|
222
|
+
int totalJobs = cupsGetJobs(&jobs, printer->name, 0 /*0 means all users*/, CUPS_WHICHJOBS_ACTIVE);
|
|
223
|
+
std::string error_str;
|
|
224
|
+
if(totalJobs > 0)
|
|
225
|
+
{
|
|
226
|
+
v8::Local<v8::Array> result_priner_jobs = V8_VALUE_NEW(Array, totalJobs);
|
|
227
|
+
int jobi =0;
|
|
228
|
+
cups_job_t * job = jobs;
|
|
229
|
+
for(; jobi < totalJobs; ++jobi, ++job)
|
|
230
|
+
{
|
|
231
|
+
v8::Local<v8::Object> result_printer_job = V8_VALUE_NEW_DEFAULT(Object);
|
|
232
|
+
error_str = parseJobObject(job, result_printer_job);
|
|
233
|
+
if(!error_str.empty())
|
|
234
|
+
{
|
|
235
|
+
// got an error? break then.
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
Nan::Set(result_priner_jobs, jobi, result_printer_job);
|
|
239
|
+
}
|
|
240
|
+
Nan::Set(result_printer, V8_STRING_NEW_UTF8("jobs"), result_priner_jobs);
|
|
241
|
+
}
|
|
242
|
+
cupsFreeJobs(totalJobs, jobs);
|
|
243
|
+
return error_str;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/// cups option class to automatically free memory.
|
|
247
|
+
class CupsOptions: public MemValueBase<cups_option_t> {
|
|
248
|
+
protected:
|
|
249
|
+
int num_options;
|
|
250
|
+
virtual void free() {
|
|
251
|
+
if(_value != NULL)
|
|
252
|
+
{
|
|
253
|
+
cupsFreeOptions(num_options, get());
|
|
254
|
+
_value = NULL;
|
|
255
|
+
num_options = 0;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
public:
|
|
259
|
+
CupsOptions(): num_options(0) {}
|
|
260
|
+
~CupsOptions () { free(); }
|
|
261
|
+
|
|
262
|
+
/// Add options from v8 object
|
|
263
|
+
CupsOptions(v8::Local<v8::Object> iV8Options): num_options(0) {
|
|
264
|
+
v8::Local<v8::Array> props = Nan::GetPropertyNames(iV8Options).ToLocalChecked();
|
|
265
|
+
|
|
266
|
+
for(unsigned int i = 0; i < props->Length(); ++i) {
|
|
267
|
+
v8::Local<v8::Value> key(Nan::Get(props, i).ToLocalChecked());
|
|
268
|
+
Nan::Utf8String keyStr(V8_LOCAL_STRING_FROM_VALUE(key));
|
|
269
|
+
Nan::Utf8String valStr(V8_LOCAL_STRING_FROM_VALUE(Nan::Get(iV8Options, key).ToLocalChecked()));
|
|
270
|
+
|
|
271
|
+
num_options = cupsAddOption(*keyStr, *valStr, num_options, &_value);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const int& getNumOptions() { return num_options; }
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
MY_NODE_MODULE_CALLBACK(getPrinters)
|
|
280
|
+
{
|
|
281
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
282
|
+
|
|
283
|
+
cups_dest_t *printers = NULL;
|
|
284
|
+
int printers_size = cupsGetDests(&printers);
|
|
285
|
+
v8::Local<v8::Array> result = V8_VALUE_NEW(Array, printers_size);
|
|
286
|
+
cups_dest_t *printer = printers;
|
|
287
|
+
std::string error_str;
|
|
288
|
+
for(int i = 0; i < printers_size; ++i, ++printer)
|
|
289
|
+
{
|
|
290
|
+
v8::Local<v8::Object> result_printer = V8_VALUE_NEW_DEFAULT(Object);
|
|
291
|
+
error_str = parsePrinterInfo(printer, result_printer);
|
|
292
|
+
if(!error_str.empty())
|
|
293
|
+
{
|
|
294
|
+
// got an error? break then
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
Nan::Set(result, i, result_printer);
|
|
298
|
+
}
|
|
299
|
+
cupsFreeDests(printers_size, printers);
|
|
300
|
+
if(!error_str.empty())
|
|
301
|
+
{
|
|
302
|
+
// got an error? return the error then
|
|
303
|
+
RETURN_EXCEPTION_STR(error_str.c_str());
|
|
304
|
+
}
|
|
305
|
+
MY_NODE_MODULE_RETURN_VALUE(result);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
MY_NODE_MODULE_CALLBACK(getDefaultPrinterName)
|
|
309
|
+
{
|
|
310
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
311
|
+
//This does not return default user printer name according to https://www.cups.org/documentation.php/doc-2.0/api-cups.html#cupsGetDefault2
|
|
312
|
+
//so leave as undefined and JS implementation will loop in all printers
|
|
313
|
+
/*
|
|
314
|
+
const char * printerName = cupsGetDefault();
|
|
315
|
+
|
|
316
|
+
// return default printer name only if defined
|
|
317
|
+
if(printerName != NULL) {
|
|
318
|
+
MY_NODE_MODULE_RETURN_VALUE(V8_STRING_NEW_UTF8(printerName));
|
|
319
|
+
}
|
|
320
|
+
*/
|
|
321
|
+
MY_NODE_MODULE_RETURN_UNDEFINED();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
MY_NODE_MODULE_CALLBACK(getPrinter)
|
|
325
|
+
{
|
|
326
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
327
|
+
REQUIRE_ARGUMENTS(iArgs, 1);
|
|
328
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 0, printername);
|
|
329
|
+
|
|
330
|
+
cups_dest_t *printers = NULL, *printer = NULL;
|
|
331
|
+
int printers_size = cupsGetDests(&printers);
|
|
332
|
+
printer = cupsGetDest(*printername, NULL, printers_size, printers);
|
|
333
|
+
v8::Local<v8::Object> result_printer = V8_VALUE_NEW_DEFAULT(Object);
|
|
334
|
+
if(printer != NULL)
|
|
335
|
+
{
|
|
336
|
+
parsePrinterInfo(printer, result_printer);
|
|
337
|
+
}
|
|
338
|
+
cupsFreeDests(printers_size, printers);
|
|
339
|
+
if(printer == NULL)
|
|
340
|
+
{
|
|
341
|
+
// printer not found
|
|
342
|
+
RETURN_EXCEPTION_STR("Printer not found");
|
|
343
|
+
}
|
|
344
|
+
MY_NODE_MODULE_RETURN_VALUE(result_printer);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
MY_NODE_MODULE_CALLBACK(getPrinterDriverOptions)
|
|
348
|
+
{
|
|
349
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
350
|
+
REQUIRE_ARGUMENTS(iArgs, 1);
|
|
351
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 0, printername);
|
|
352
|
+
|
|
353
|
+
cups_dest_t *printers = NULL, *printer = NULL;
|
|
354
|
+
int printers_size = cupsGetDests(&printers);
|
|
355
|
+
printer = cupsGetDest(*printername, NULL, printers_size, printers);
|
|
356
|
+
v8::Local<v8::Object> driver_options = V8_VALUE_NEW_DEFAULT(Object);
|
|
357
|
+
if(printer != NULL)
|
|
358
|
+
{
|
|
359
|
+
parseDriverOptions(printer, driver_options);
|
|
360
|
+
}
|
|
361
|
+
cupsFreeDests(printers_size, printers);
|
|
362
|
+
if(printer == NULL)
|
|
363
|
+
{
|
|
364
|
+
// printer not found
|
|
365
|
+
RETURN_EXCEPTION_STR("Printer not found");
|
|
366
|
+
}
|
|
367
|
+
MY_NODE_MODULE_RETURN_VALUE(driver_options);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
MY_NODE_MODULE_CALLBACK(getJob)
|
|
371
|
+
{
|
|
372
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
373
|
+
REQUIRE_ARGUMENTS(iArgs, 2);
|
|
374
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 0, printername);
|
|
375
|
+
REQUIRE_ARGUMENT_INTEGER(iArgs, 1, jobId);
|
|
376
|
+
|
|
377
|
+
v8::Local<v8::Object> result_printer_job = V8_VALUE_NEW_DEFAULT(Object);
|
|
378
|
+
// Get printer jobs
|
|
379
|
+
cups_job_t *jobs = NULL, *jobFound = NULL;
|
|
380
|
+
int totalJobs = cupsGetJobs(&jobs, *printername, 0 /*0 means all users*/, CUPS_WHICHJOBS_ALL);
|
|
381
|
+
if(totalJobs > 0)
|
|
382
|
+
{
|
|
383
|
+
int jobi =0;
|
|
384
|
+
cups_job_t * job = jobs;
|
|
385
|
+
for(; jobi < totalJobs; ++jobi, ++job)
|
|
386
|
+
{
|
|
387
|
+
if(job->id != jobId)
|
|
388
|
+
{
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
// Job Found
|
|
392
|
+
jobFound = job;
|
|
393
|
+
parseJobObject(job, result_printer_job);
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
cupsFreeJobs(totalJobs, jobs);
|
|
398
|
+
if(jobFound == NULL)
|
|
399
|
+
{
|
|
400
|
+
// printer not found
|
|
401
|
+
RETURN_EXCEPTION_STR("Printer job not found");
|
|
402
|
+
}
|
|
403
|
+
MY_NODE_MODULE_RETURN_VALUE(result_printer_job);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
MY_NODE_MODULE_CALLBACK(setJob)
|
|
407
|
+
{
|
|
408
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
409
|
+
REQUIRE_ARGUMENTS(iArgs, 3);
|
|
410
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 0, printername);
|
|
411
|
+
REQUIRE_ARGUMENT_INTEGER(iArgs, 1, jobId);
|
|
412
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 2, jobCommandV8);
|
|
413
|
+
if(jobId < 0)
|
|
414
|
+
{
|
|
415
|
+
RETURN_EXCEPTION_STR("Wrong job number");
|
|
416
|
+
}
|
|
417
|
+
std::string jobCommandStr(*jobCommandV8);
|
|
418
|
+
bool result_ok = false;
|
|
419
|
+
if(jobCommandStr == "CANCEL")
|
|
420
|
+
{
|
|
421
|
+
result_ok = (cupsCancelJob(*printername, jobId) == 1);
|
|
422
|
+
}
|
|
423
|
+
else
|
|
424
|
+
{
|
|
425
|
+
RETURN_EXCEPTION_STR("wrong job command. use getSupportedJobCommands to see the possible commands");
|
|
426
|
+
}
|
|
427
|
+
MY_NODE_MODULE_RETURN_VALUE(V8_VALUE_NEW(Boolean, result_ok));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
MY_NODE_MODULE_CALLBACK(getSupportedJobCommands)
|
|
431
|
+
{
|
|
432
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
433
|
+
v8::Local<v8::Array> result = V8_VALUE_NEW_DEFAULT(Array);
|
|
434
|
+
int i = 0;
|
|
435
|
+
Nan::Set(result, i++, V8_STRING_NEW_UTF8("CANCEL"));
|
|
436
|
+
MY_NODE_MODULE_RETURN_VALUE(result);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
MY_NODE_MODULE_CALLBACK(getSupportedPrintFormats)
|
|
440
|
+
{
|
|
441
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
442
|
+
v8::Local<v8::Array> result = V8_VALUE_NEW_DEFAULT(Array);
|
|
443
|
+
int i = 0;
|
|
444
|
+
for(FormatMapType::const_iterator itFormat = getPrinterFormatMap().begin(); itFormat != getPrinterFormatMap().end(); ++itFormat)
|
|
445
|
+
{
|
|
446
|
+
Nan::Set(result, i++, V8_STRING_NEW_UTF8(itFormat->first.c_str()));
|
|
447
|
+
}
|
|
448
|
+
MY_NODE_MODULE_RETURN_VALUE(result);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
MY_NODE_MODULE_CALLBACK(PrintDirect)
|
|
452
|
+
{
|
|
453
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
454
|
+
REQUIRE_ARGUMENTS(iArgs, 5);
|
|
455
|
+
|
|
456
|
+
// can be string or buffer
|
|
457
|
+
if(iArgs.Length() <= 0)
|
|
458
|
+
{
|
|
459
|
+
RETURN_EXCEPTION_STR("Argument 0 missing");
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
std::string data;
|
|
463
|
+
v8::Local<v8::Value> arg0(iArgs[0]);
|
|
464
|
+
if (!getStringOrBufferFromV8Value(arg0, data))
|
|
465
|
+
{
|
|
466
|
+
RETURN_EXCEPTION_STR("Argument 0 must be a string or Buffer");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 1, printername);
|
|
470
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 2, docname);
|
|
471
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 3, type);
|
|
472
|
+
REQUIRE_ARGUMENT_OBJECT(iArgs, 4, print_options);
|
|
473
|
+
|
|
474
|
+
std::string type_str(*type);
|
|
475
|
+
FormatMapType::const_iterator itFormat = getPrinterFormatMap().find(type_str);
|
|
476
|
+
if(itFormat == getPrinterFormatMap().end())
|
|
477
|
+
{
|
|
478
|
+
RETURN_EXCEPTION_STR("unsupported format type");
|
|
479
|
+
}
|
|
480
|
+
type_str = itFormat->second;
|
|
481
|
+
|
|
482
|
+
CupsOptions options(print_options);
|
|
483
|
+
|
|
484
|
+
int job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, *printername, *docname, options.getNumOptions(), options.get());
|
|
485
|
+
if(job_id == 0) {
|
|
486
|
+
RETURN_EXCEPTION_STR(cupsLastErrorString());
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if(HTTP_CONTINUE != cupsStartDocument(CUPS_HTTP_DEFAULT, *printername, job_id, *docname, type_str.c_str(), 1 /*last document*/)) {
|
|
490
|
+
RETURN_EXCEPTION_STR(cupsLastErrorString());
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/* cupsWriteRequestData can be called as many times as needed */
|
|
494
|
+
//TODO: to split big buffer
|
|
495
|
+
if (HTTP_CONTINUE != cupsWriteRequestData(CUPS_HTTP_DEFAULT, data.c_str(), data.size())) {
|
|
496
|
+
cupsFinishDocument(CUPS_HTTP_DEFAULT, *printername);
|
|
497
|
+
RETURN_EXCEPTION_STR(cupsLastErrorString());
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
cupsFinishDocument(CUPS_HTTP_DEFAULT, *printername);
|
|
501
|
+
|
|
502
|
+
MY_NODE_MODULE_RETURN_VALUE(V8_VALUE_NEW(Number, job_id));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
MY_NODE_MODULE_CALLBACK(PrintFile)
|
|
506
|
+
{
|
|
507
|
+
MY_NODE_MODULE_HANDLESCOPE;
|
|
508
|
+
REQUIRE_ARGUMENTS(iArgs, 3);
|
|
509
|
+
|
|
510
|
+
// can be string or buffer
|
|
511
|
+
if(iArgs.Length() <= 0)
|
|
512
|
+
{
|
|
513
|
+
RETURN_EXCEPTION_STR("Argument 0 missing");
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 0, filename);
|
|
517
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 1, docname);
|
|
518
|
+
REQUIRE_ARGUMENT_STRING(iArgs, 2, printer);
|
|
519
|
+
REQUIRE_ARGUMENT_OBJECT(iArgs, 3, print_options);
|
|
520
|
+
|
|
521
|
+
CupsOptions options(print_options);
|
|
522
|
+
|
|
523
|
+
int job_id = cupsPrintFile(*printer, *filename, *docname, options.getNumOptions(), options.get());
|
|
524
|
+
|
|
525
|
+
if(job_id == 0){
|
|
526
|
+
MY_NODE_MODULE_RETURN_VALUE(V8_STRING_NEW_UTF8(cupsLastErrorString()));
|
|
527
|
+
} else {
|
|
528
|
+
MY_NODE_MODULE_RETURN_VALUE(V8_VALUE_NEW(Number, job_id));
|
|
529
|
+
}
|
|
530
|
+
}
|