cupsffi 0.0.1
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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/cupsffi.gemspec +23 -0
- data/lib/cupsffi/job.rb +60 -0
- data/lib/cupsffi/lib.rb +390 -0
- data/lib/cupsffi/ppd.rb +66 -0
- data/lib/cupsffi/printer.rb +159 -0
- data/lib/cupsffi/version.rb +3 -0
- data/lib/cupsffi.rb +6 -0
- metadata +86 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/cupsffi.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "cupsffi/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "cupsffi"
|
7
|
+
s.version = Cupsffi::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Nathan Ehresman"]
|
10
|
+
s.email = ["nehresma@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{FFI wrapper around libcups}
|
13
|
+
s.description = %q{Simple wrapper around libcups to give CUPS printing capabilities to Ruby apps.}
|
14
|
+
|
15
|
+
s.add_development_dependency "ffi"
|
16
|
+
|
17
|
+
s.rubyforge_project = "cupsffi"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
data/lib/cupsffi/job.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# The MIT License
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011 Nathan Ehresman
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
class CupsJob
|
24
|
+
attr_reader :id, :printer
|
25
|
+
|
26
|
+
def initialize(id, printer = nil)
|
27
|
+
@id = Integer(id)
|
28
|
+
@printer = printer unless printer.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def cancel(printer = nil)
|
32
|
+
raise "cancel parameter must be a CupsPrinter or String" unless printer.nil? || [CupsPrinter, String].include?(printer.class)
|
33
|
+
p = printer || @printer
|
34
|
+
r = CupsFFI::cupsCancelJob(p.kind_of?(String) ? p : p.name, @id)
|
35
|
+
raise CupsFFI::cupsLastErrorString() if r == 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def status(printer = nil)
|
39
|
+
raise "status parameter must be a CupsPrinter or String" unless printer.nil? || [CupsPrinter, String].include?(printer.class)
|
40
|
+
pointer = FFI::MemoryPointer.new :pointer
|
41
|
+
p = printer || @printer
|
42
|
+
job_count = CupsFFI::cupsGetJobs(pointer, p.kind_of?(String) ? p : p.name, 0, CupsFFI::CUPS_WHICHJOBS_ALL)
|
43
|
+
|
44
|
+
free_jobs = lambda do
|
45
|
+
CupsFFI::cupsFreeJobs(job_count, pointer.get_pointer(0))
|
46
|
+
end
|
47
|
+
|
48
|
+
job_count.times do |i|
|
49
|
+
job = CupsFFI::CupsJobS.new(pointer.get_pointer(0) + (CupsFFI::CupsJobS.size * i))
|
50
|
+
if job[:id] == @id then
|
51
|
+
state = job[:state]
|
52
|
+
free_jobs.call
|
53
|
+
return state
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
free_jobs.call
|
58
|
+
raise "Job not found on printer"
|
59
|
+
end
|
60
|
+
end
|
data/lib/cupsffi/lib.rb
ADDED
@@ -0,0 +1,390 @@
|
|
1
|
+
# The MIT License
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011 Nathan Ehresman
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'ffi'
|
24
|
+
|
25
|
+
module CupsFFI
|
26
|
+
extend FFI::Library
|
27
|
+
ffi_lib 'cups'
|
28
|
+
|
29
|
+
### cups.h API
|
30
|
+
|
31
|
+
CUPS_JOBID_ALL = -1
|
32
|
+
CUPS_WHICHJOBS_ALL = -1
|
33
|
+
CUPS_WHICHJOBS_ACTIVE = 0
|
34
|
+
CUPS_WHICHJOBS_COMPLETED = 1
|
35
|
+
CUPS_HTTP_DEFAULT = nil
|
36
|
+
|
37
|
+
class CupsOptionS < FFI::Struct
|
38
|
+
layout :name, :string,
|
39
|
+
:value, :string
|
40
|
+
end
|
41
|
+
|
42
|
+
class CupsDestS < FFI::Struct
|
43
|
+
layout :name, :string,
|
44
|
+
:instance, :string,
|
45
|
+
:is_default, :int,
|
46
|
+
:num_options, :int,
|
47
|
+
:options, :pointer # pointer type is CupsOptionS
|
48
|
+
end
|
49
|
+
|
50
|
+
IppJState = enum [:pending, 3,
|
51
|
+
:held,
|
52
|
+
:processing,
|
53
|
+
:stopped,
|
54
|
+
:canceled,
|
55
|
+
:aborted,
|
56
|
+
:completed]
|
57
|
+
|
58
|
+
class CupsJobS < FFI::Struct
|
59
|
+
layout :id, :int,
|
60
|
+
:dest, :string,
|
61
|
+
:title, :string,
|
62
|
+
:user, :string,
|
63
|
+
:format, :string,
|
64
|
+
:state, IppJState,
|
65
|
+
:size, :int,
|
66
|
+
:priority, :int,
|
67
|
+
:completed_time, :long,
|
68
|
+
:creation_time, :long,
|
69
|
+
:processing_time, :long
|
70
|
+
end
|
71
|
+
|
72
|
+
HttpStatus = enum [:http_error, -1,
|
73
|
+
:http_continue, 100,
|
74
|
+
:http_switching_protocols,
|
75
|
+
:http_ok, 200,
|
76
|
+
:http_created,
|
77
|
+
:http_accepted,
|
78
|
+
:http_not_authoritative,
|
79
|
+
:http_no_content,
|
80
|
+
:http_reset_content,
|
81
|
+
:http_partial_content,
|
82
|
+
:http_multiple_choices, 300,
|
83
|
+
:http_moved_permanently,
|
84
|
+
:http_moved_temporarily,
|
85
|
+
:http_see_other,
|
86
|
+
:http_not_modified,
|
87
|
+
:http_use_proxy,
|
88
|
+
:http_bad_request, 400,
|
89
|
+
:http_unauthorized,
|
90
|
+
:http_payment_required,
|
91
|
+
:http_forbidden,
|
92
|
+
:http_not_found,
|
93
|
+
:http_method_not_allowed,
|
94
|
+
:http_not_acceptable,
|
95
|
+
:http_proxy_authentication,
|
96
|
+
:http_request_timeout,
|
97
|
+
:http_conflict,
|
98
|
+
:http_gone,
|
99
|
+
:http_length_required,
|
100
|
+
:http_precondition,
|
101
|
+
:http_request_too_large,
|
102
|
+
:http_uri_too_long,
|
103
|
+
:http_unsupported_mediatype,
|
104
|
+
:http_requested_range,
|
105
|
+
:http_expectation_failed,
|
106
|
+
:http_upgrade_required, 426,
|
107
|
+
:http_server_error, 500,
|
108
|
+
:http_not_implemented,
|
109
|
+
:http_bad_gateway,
|
110
|
+
:http_service_unavailable,
|
111
|
+
:http_gateway_timeout,
|
112
|
+
:http_not_supported,
|
113
|
+
:http_authorization_canceled, 1000]
|
114
|
+
|
115
|
+
IppStatus = enum [:ipp_ok, 0,
|
116
|
+
:ipp_ok_subst,
|
117
|
+
:ipp_ok_conflict,
|
118
|
+
:ipp_ok_ignored_subscriptions,
|
119
|
+
:ipp_ok_ignored_notifications,
|
120
|
+
:ipp_ok_too_many_events,
|
121
|
+
:ipp_ok_but_cancel_subscription,
|
122
|
+
:ipp_ok_events_complete,
|
123
|
+
:ipp_redirection_other_site, 512,
|
124
|
+
:cups_see_other, 640,
|
125
|
+
:ipp_bad_request, 1024,
|
126
|
+
:ipp_forbidden,
|
127
|
+
:ipp_not_authenticated,
|
128
|
+
:ipp_not_authorized,
|
129
|
+
:ipp_not_possible,
|
130
|
+
:ipp_timeout,
|
131
|
+
:ipp_not_found,
|
132
|
+
:ipp_gone,
|
133
|
+
:ipp_request_entity,
|
134
|
+
:ipp_request_value,
|
135
|
+
:ipp_document_format,
|
136
|
+
:ipp_attributes,
|
137
|
+
:ipp_uri_scheme,
|
138
|
+
:ipp_charset,
|
139
|
+
:ipp_conflict,
|
140
|
+
:ipp_compression_not_supported,
|
141
|
+
:ipp_compression_error,
|
142
|
+
:ipp_document_format_error,
|
143
|
+
:ipp_document_access_error,
|
144
|
+
:ipp_attributes_not_settable,
|
145
|
+
:ipp_ignored_all_subscriptions,
|
146
|
+
:ipp_too_many_subscriptions,
|
147
|
+
:ipp_ignored_all_notifications,
|
148
|
+
:ipp_print_support_file_not_found,
|
149
|
+
:ipp_internal_error, 1280,
|
150
|
+
:ipp_operation_not_supported,
|
151
|
+
:ipp_service_unavailable,
|
152
|
+
:ipp_version_not_supported,
|
153
|
+
:ipp_device_error,
|
154
|
+
:ipp_temporary_error,
|
155
|
+
:ipp_not_accepting,
|
156
|
+
:ipp_printer_busy,
|
157
|
+
:ipp_error_job_canceled,
|
158
|
+
:ipp_multiple_jobs_not_supported,
|
159
|
+
:ipp_printer_is_deactivated
|
160
|
+
]
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
attach_function 'cupsGetDests', [ :pointer ], :int
|
167
|
+
|
168
|
+
# :int is the number of CupsDestS structs to free
|
169
|
+
# :pointer is the first one
|
170
|
+
attach_function 'cupsFreeDests', [ :int, :pointer ], :void
|
171
|
+
|
172
|
+
# Parameters:
|
173
|
+
# - printer name
|
174
|
+
# - file name
|
175
|
+
# - job title
|
176
|
+
# - number of options
|
177
|
+
# - a pointer to a CupsOptionS struct
|
178
|
+
# Returns
|
179
|
+
# - job number or 0 on error
|
180
|
+
attach_function 'cupsPrintFile', [ :string, :string, :string, :int, :pointer ], :int
|
181
|
+
|
182
|
+
attach_function 'cupsLastErrorString', [], :string
|
183
|
+
|
184
|
+
# Parameters
|
185
|
+
# - printer name
|
186
|
+
# - job id
|
187
|
+
attach_function 'cupsCancelJob', [:string, :int], :void
|
188
|
+
|
189
|
+
# Parameters
|
190
|
+
# - pointer to struct CupsJobS to populate
|
191
|
+
# - printer name
|
192
|
+
# - myjobs (0 == all users, 1 == mine)
|
193
|
+
# - whichjobs (CUPS_WHICHJOBS_ALL, CUPS_WHICHJOBS_ACTIVE, or CUPS_WHICHJOBS_COMPLETED)
|
194
|
+
# Returns:
|
195
|
+
# - number of jobs
|
196
|
+
attach_function 'cupsGetJobs', [:pointer, :string, :int, :int], :int
|
197
|
+
|
198
|
+
# Parameters
|
199
|
+
# - number of jobs
|
200
|
+
# - pointer to the first CupsJobS to free
|
201
|
+
attach_function 'cupsFreeJobs', [:int, :pointer ], :void
|
202
|
+
|
203
|
+
# Parameters
|
204
|
+
# - pointer to http connection to server or CUPS_HTTP_DEFAULT
|
205
|
+
# - printer name
|
206
|
+
# - title of job
|
207
|
+
# - number of options
|
208
|
+
# - pointer to a CupsOptionS struct
|
209
|
+
# Returns
|
210
|
+
# - job number or 0 on error
|
211
|
+
attach_function 'cupsCreateJob', [:pointer, :string, :string, :int, :pointer], :int
|
212
|
+
|
213
|
+
# Parameters
|
214
|
+
# - pointer to http connection to server or CUPS_HTTP_DEFAULT
|
215
|
+
# - printer name
|
216
|
+
# - job id
|
217
|
+
# - name of document
|
218
|
+
# - mime type format
|
219
|
+
# - last document (1 for last document in job, 0 otherwise)
|
220
|
+
# Returns
|
221
|
+
# - HttpStatus
|
222
|
+
attach_function 'cupsStartDocument', [:pointer, :string, :int, :string, :string, :int], HttpStatus
|
223
|
+
|
224
|
+
# Parameters
|
225
|
+
# - pointer to http connection to server or CUPS_HTTP_DEFAULT
|
226
|
+
# - data in a character string
|
227
|
+
# - length of data
|
228
|
+
# Returns
|
229
|
+
# - HttpStatus
|
230
|
+
attach_function 'cupsWriteRequestData', [:pointer, :string, :size_t], HttpStatus
|
231
|
+
|
232
|
+
# Parameters
|
233
|
+
# - pointer to http connection to server or CUPS_HTTP_DEFAULT
|
234
|
+
# - printer name
|
235
|
+
# Returns
|
236
|
+
# - IppStatus
|
237
|
+
attach_function 'cupsFinishDocument', [:pointer, :string], IppStatus
|
238
|
+
|
239
|
+
# Parameters
|
240
|
+
# - printer name
|
241
|
+
# Returns
|
242
|
+
# - filename for PPD file
|
243
|
+
attach_function 'cupsGetPPD', [:string], :string
|
244
|
+
|
245
|
+
# Parameters
|
246
|
+
# - option name
|
247
|
+
# - option value
|
248
|
+
# - number of options
|
249
|
+
# - pointer to options
|
250
|
+
# Returns
|
251
|
+
# - number of options
|
252
|
+
attach_function 'cupsAddOption', [:string, :string, :int, :pointer], :int
|
253
|
+
|
254
|
+
# Parameters
|
255
|
+
# - number of options
|
256
|
+
# - pointer to options
|
257
|
+
attach_function 'cupsFreeOptions', [:int, :pointer], :void
|
258
|
+
|
259
|
+
|
260
|
+
|
261
|
+
### ppd.h API
|
262
|
+
PPD_MAX_NAME = 41
|
263
|
+
PPD_MAX_TEXT = 81
|
264
|
+
|
265
|
+
PPDCSE = enum [:ppd_cs_cmyk, -4,
|
266
|
+
:ppd_cs_cmy,
|
267
|
+
:ppd_cs_gray, 1,
|
268
|
+
:ppd_cs_rgb, 3,
|
269
|
+
:ppd_cs_rgbk,
|
270
|
+
:ppd_cs_n]
|
271
|
+
PPDUIE = enum [:boolean, :pickone, :pickmany]
|
272
|
+
PPDSectionE = enum [:any, :document, :exit, :jcl, :page, :prolog]
|
273
|
+
|
274
|
+
class PPDFileS < FFI::ManagedStruct
|
275
|
+
layout :language_level, :int,
|
276
|
+
:color_device, :int,
|
277
|
+
:variable_sizes, :int,
|
278
|
+
:accurate_screens, :int,
|
279
|
+
:contone_only, :int,
|
280
|
+
:landscape, :int,
|
281
|
+
:model_number, :int,
|
282
|
+
:manual_copies, :int,
|
283
|
+
:throughput, :int,
|
284
|
+
:colorspace, PPDCSE,
|
285
|
+
:patches, :string,
|
286
|
+
:num_emulations, :int,
|
287
|
+
:emulations, :pointer,
|
288
|
+
:jcl_begin, :string,
|
289
|
+
:jcl_ps, :string,
|
290
|
+
:jcl_end, :string,
|
291
|
+
:lang_encoding, :string,
|
292
|
+
:lang_version, :string,
|
293
|
+
:modelname, :string,
|
294
|
+
:ttrasterizer, :string,
|
295
|
+
:manufacturer, :string,
|
296
|
+
:product, :string,
|
297
|
+
:nickname, :string,
|
298
|
+
:short_nickname, :string,
|
299
|
+
:num_groups, :int,
|
300
|
+
:groups, :pointer,
|
301
|
+
:num_sizes, :int,
|
302
|
+
:sizes, :pointer,
|
303
|
+
:custom_min, [:float, 2],
|
304
|
+
:custom_max, [:float, 2],
|
305
|
+
:custom_margins, [:float, 4],
|
306
|
+
:num_consts, :int,
|
307
|
+
:consts, :pointer,
|
308
|
+
:num_fonts, :int,
|
309
|
+
:fonts, :pointer, # **char
|
310
|
+
:num_profiles, :int,
|
311
|
+
:profiles, :pointer,
|
312
|
+
:num_filters, :int,
|
313
|
+
:filters, :pointer, # **char
|
314
|
+
:flip_duplex, :int,
|
315
|
+
:protocols, :string,
|
316
|
+
:pcfilename, :string,
|
317
|
+
:num_attrs, :int,
|
318
|
+
:cur_attr, :int,
|
319
|
+
:attrs, :pointer,
|
320
|
+
:sorted_attrs, :pointer,
|
321
|
+
:options, :pointer,
|
322
|
+
:coptions, :pointer,
|
323
|
+
:marked, :pointer,
|
324
|
+
:cups_uiconstraints, :pointer
|
325
|
+
|
326
|
+
def self.release(ptr)
|
327
|
+
CupsFFI::ppdClose(ptr)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
class PPDChoiceS < FFI::Struct
|
332
|
+
layout :marked, :char,
|
333
|
+
:choice, [:char, PPD_MAX_NAME],
|
334
|
+
:text, [:char, PPD_MAX_TEXT],
|
335
|
+
:code, :string,
|
336
|
+
:option, :pointer
|
337
|
+
end
|
338
|
+
|
339
|
+
class PPDOptionS < FFI::Struct
|
340
|
+
layout :conflicted, :char,
|
341
|
+
:keyword, [:char, PPD_MAX_NAME],
|
342
|
+
:defchoice, [:char, PPD_MAX_NAME],
|
343
|
+
:text, [:char, PPD_MAX_TEXT],
|
344
|
+
:ui, PPDUIE,
|
345
|
+
:section, PPDSectionE,
|
346
|
+
:order, :float,
|
347
|
+
:num_choices, :int,
|
348
|
+
:choices, :pointer
|
349
|
+
end
|
350
|
+
|
351
|
+
# Parameters
|
352
|
+
# - filename for PPD file
|
353
|
+
# Returns
|
354
|
+
# - pointer to PPDFileS struct
|
355
|
+
attach_function 'ppdOpenFile', [:string], :pointer
|
356
|
+
|
357
|
+
# Parameters
|
358
|
+
# - pointer to PPDFileS struct
|
359
|
+
attach_function 'ppdClose', [:pointer], :void
|
360
|
+
|
361
|
+
# Parameters
|
362
|
+
# - pointer to PPDFileS struct
|
363
|
+
# Returns
|
364
|
+
# - pointer to PPDOptionS struct
|
365
|
+
attach_function 'ppdFirstOption', [:pointer], :pointer
|
366
|
+
|
367
|
+
# Parameters
|
368
|
+
# - pointer to PPDFileS struct
|
369
|
+
# Returns
|
370
|
+
# - pointer to PPDOptionS struct
|
371
|
+
attach_function 'ppdNextOption', [:pointer], :pointer
|
372
|
+
|
373
|
+
|
374
|
+
|
375
|
+
|
376
|
+
|
377
|
+
### array.h API
|
378
|
+
|
379
|
+
# Parameters
|
380
|
+
# - pointer to _cups_array_s struct
|
381
|
+
# Returns
|
382
|
+
# - void pointer to first element
|
383
|
+
attach_function 'cupsArrayFirst', [:pointer], :pointer
|
384
|
+
|
385
|
+
# Parameters
|
386
|
+
# - pointer to _cups_array_s struct
|
387
|
+
# Returns
|
388
|
+
# - void pointer to first element
|
389
|
+
attach_function 'cupsArrayNext', [:pointer], :pointer
|
390
|
+
end
|
data/lib/cupsffi/ppd.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# The MIT License
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011 Nathan Ehresman
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
class CupsPPD
|
24
|
+
def initialize(printer_name)
|
25
|
+
@file = CupsFFI::cupsGetPPD(printer_name)
|
26
|
+
raise "No PPD found for #{printer_name}" if @file.nil?
|
27
|
+
|
28
|
+
@pointer = CupsFFI::ppdOpenFile(@file)
|
29
|
+
raise "Unable to open PPD #{file}" if @pointer.null?
|
30
|
+
|
31
|
+
@ppd_file_s = CupsFFI::PPDFileS.new(@pointer)
|
32
|
+
end
|
33
|
+
|
34
|
+
def close
|
35
|
+
CupsFFI::ppdClose(@pointer)
|
36
|
+
File.unlink(@file)
|
37
|
+
end
|
38
|
+
|
39
|
+
def options
|
40
|
+
options = []
|
41
|
+
option_pointer = CupsFFI::ppdFirstOption(@pointer)
|
42
|
+
while !option_pointer.null?
|
43
|
+
option = CupsFFI::PPDOptionS.new(option_pointer)
|
44
|
+
choices = []
|
45
|
+
option[:num_choices].times do |i|
|
46
|
+
choice = CupsFFI::PPDChoiceS.new(option[:choices] + (CupsFFI::PPDChoiceS.size * i))
|
47
|
+
choices.push({
|
48
|
+
:text => String.new(choice[:text]),
|
49
|
+
:choice => String.new(choice[:choice])
|
50
|
+
})
|
51
|
+
end
|
52
|
+
options.push({
|
53
|
+
:keyword => String.new(option[:keyword]),
|
54
|
+
:default_choice => String.new(option[:defchoice]),
|
55
|
+
:text => String.new(option[:text]),
|
56
|
+
:ui => String.new(option[:ui].to_s),
|
57
|
+
:section => String.new(option[:section].to_s),
|
58
|
+
:order => option[:order],
|
59
|
+
:choices => choices
|
60
|
+
})
|
61
|
+
|
62
|
+
option_pointer = CupsFFI::ppdNextOption(@pointer)
|
63
|
+
end
|
64
|
+
options
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# The MIT License
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011 Nathan Ehresman
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
class CupsPrinter
|
24
|
+
attr_reader :name
|
25
|
+
|
26
|
+
def initialize(name)
|
27
|
+
raise "Printer not found" unless CupsPrinter.get_all_printer_names.include? name
|
28
|
+
@name = name
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_all_printer_names
|
32
|
+
p = FFI::MemoryPointer.new :pointer
|
33
|
+
dest_count = CupsFFI::cupsGetDests(p)
|
34
|
+
ary = []
|
35
|
+
dest_count.times do |i|
|
36
|
+
d = CupsFFI::CupsDestS.new(p.get_pointer(0) + (CupsFFI::CupsDestS.size * i))
|
37
|
+
ary.push(d[:name].dup)
|
38
|
+
end
|
39
|
+
CupsFFI::cupsFreeDests(dest_count, p.get_pointer(0))
|
40
|
+
ary
|
41
|
+
end
|
42
|
+
|
43
|
+
def attributes
|
44
|
+
p = FFI::MemoryPointer.new :pointer
|
45
|
+
dest_count = CupsFFI::cupsGetDests(p)
|
46
|
+
hash = {}
|
47
|
+
dest_count.times do |i|
|
48
|
+
dest = CupsFFI::CupsDestS.new(p.get_pointer(0) + (CupsFFI::CupsDestS.size * i))
|
49
|
+
next unless dest[:name] == @name
|
50
|
+
dest[:num_options].times do |j|
|
51
|
+
options = CupsFFI::CupsOptionS.new(dest[:options] + (CupsFFI::CupsOptionS.size * j))
|
52
|
+
hash[options[:name].dup] = options[:value].dup
|
53
|
+
end
|
54
|
+
end
|
55
|
+
CupsFFI::cupsFreeDests(dest_count, p.get_pointer(0))
|
56
|
+
hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def state
|
60
|
+
o = attributes
|
61
|
+
|
62
|
+
{
|
63
|
+
:state =>
|
64
|
+
case o['printer-state']
|
65
|
+
when "3" then :idle
|
66
|
+
when "4" then :printing
|
67
|
+
when "5" then :stopped
|
68
|
+
else :unknown
|
69
|
+
end,
|
70
|
+
:reasons => o['printer-state-reasons'].split(/,/)
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def print_file(file_name, options = {})
|
75
|
+
raise "File not found: #{file_name}" unless File.exists? file_name
|
76
|
+
|
77
|
+
options_pointer = nil
|
78
|
+
num_options = 0
|
79
|
+
unless options.empty?
|
80
|
+
validate_options(options)
|
81
|
+
options_pointer = FFI::MemoryPointer.new :pointer
|
82
|
+
options.map do |key,value|
|
83
|
+
num_options = CupsFFI::cupsAddOption(key.to_s, value.to_s, num_options, options_pointer)
|
84
|
+
end
|
85
|
+
options_pointer = options_pointer.get_pointer(0)
|
86
|
+
end
|
87
|
+
|
88
|
+
job_id = CupsFFI::cupsPrintFile(@name, file_name, file_name, num_options, options_pointer)
|
89
|
+
|
90
|
+
if job_id == 0
|
91
|
+
last_error = CupsFFI::cupsLastErrorString()
|
92
|
+
CupsFFI::cupsFreeOptions(num_options, options_pointer) unless options_pointer.nil?
|
93
|
+
raise last_error
|
94
|
+
end
|
95
|
+
|
96
|
+
CupsFFI::cupsFreeOptions(num_options, options_pointer) unless options_pointer.nil?
|
97
|
+
CupsJob.new(job_id, self)
|
98
|
+
end
|
99
|
+
|
100
|
+
def print_data(data, mime_type, options = {})
|
101
|
+
options_pointer = nil
|
102
|
+
num_options = 0
|
103
|
+
unless options.empty?
|
104
|
+
validate_options(options)
|
105
|
+
options_pointer = FFI::MemoryPointer.new :pointer
|
106
|
+
options.map do |key,value|
|
107
|
+
num_options = CupsFFI::cupsAddOption(key.to_s, value.to_s, num_options, options_pointer)
|
108
|
+
end
|
109
|
+
options_pointer = options_pointer.get_pointer(0)
|
110
|
+
end
|
111
|
+
|
112
|
+
job_id = CupsFFI::cupsCreateJob(CupsFFI::CUPS_HTTP_DEFAULT, @name, 'data job', num_options, options_pointer)
|
113
|
+
if job_id == 0
|
114
|
+
last_error = CupsFFI::cupsLastErrorString()
|
115
|
+
CupsFFI::cupsFreeOptions(num_options, options_pointer) unless options_pointer.nil?
|
116
|
+
raise last_error
|
117
|
+
end
|
118
|
+
|
119
|
+
http_status = CupsFFI::cupsStartDocument(CupsFFI::CUPS_HTTP_DEFAULT, @name,
|
120
|
+
job_id, 'my doc', mime_type, 1)
|
121
|
+
|
122
|
+
http_status = CupsFFI::cupsWriteRequestData(CupsFFI::CUPS_HTTP_DEFAULT, data, data.length)
|
123
|
+
|
124
|
+
ipp_status = CupsFFI::cupsFinishDocument(CupsFFI::CUPS_HTTP_DEFAULT, @name)
|
125
|
+
|
126
|
+
unless ipp_status == :ipp_ok
|
127
|
+
CupsFFI::cupsFreeOptions(num_options, options_pointer) unless options_pointer.nil?
|
128
|
+
raise ipp_status.to_s
|
129
|
+
end
|
130
|
+
|
131
|
+
CupsFFI::cupsFreeOptions(num_options, options_pointer) unless options_pointer.nil?
|
132
|
+
CupsJob.new(job_id, self)
|
133
|
+
end
|
134
|
+
|
135
|
+
def cancel_all_jobs
|
136
|
+
r = CupsFFI::cupsCancelJob(@name, CupsFFI::CUPS_JOBID_ALL)
|
137
|
+
raise CupsFFI::cupsLastErrorString() if r == 0
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
private
|
142
|
+
def validate_options(options)
|
143
|
+
ppd = CupsPPD.new(@name)
|
144
|
+
|
145
|
+
# Build a hash of the ppd options for quick lookup
|
146
|
+
ppd_options = {}
|
147
|
+
ppd.options.each do |ppd_option|
|
148
|
+
ppd_options[ppd_option[:keyword]] = ppd_option
|
149
|
+
end
|
150
|
+
|
151
|
+
# Examine each input option to make sure that both the key and value are
|
152
|
+
# found in the ppd options.
|
153
|
+
options.each do |key,value|
|
154
|
+
raise "Invalid option #{key} for printer #{@name}" if ppd_options[key].nil?
|
155
|
+
choices = ppd_options[key][:choices].map{|c| c[:choice]}
|
156
|
+
raise "Invalid value #{value} for option #{key}" unless choices.include?(value)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/cupsffi.rb
ADDED
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cupsffi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Nathan Ehresman
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-03-05 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: ffi
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
description: Simple wrapper around libcups to give CUPS printing capabilities to Ruby apps.
|
34
|
+
email:
|
35
|
+
- nehresma@gmail.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- Rakefile
|
46
|
+
- cupsffi.gemspec
|
47
|
+
- lib/cupsffi.rb
|
48
|
+
- lib/cupsffi/job.rb
|
49
|
+
- lib/cupsffi/lib.rb
|
50
|
+
- lib/cupsffi/ppd.rb
|
51
|
+
- lib/cupsffi/printer.rb
|
52
|
+
- lib/cupsffi/version.rb
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: ""
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project: cupsffi
|
81
|
+
rubygems_version: 1.3.7
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: FFI wrapper around libcups
|
85
|
+
test_files: []
|
86
|
+
|