@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.
@@ -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
+ }