cupsffi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cupsffi.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Cupsffi
2
+ VERSION = "0.0.1"
3
+ end
data/lib/cupsffi.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'ffi'
2
+
3
+ require 'cupsffi/lib'
4
+ require 'cupsffi/printer'
5
+ require 'cupsffi/job'
6
+ require 'cupsffi/ppd'
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
+