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 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
+