ffi-cups2 0.4.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,342 @@
1
+ module Cups
2
+ class Printer
3
+ attr_reader :name, :is_default, :options, :connection
4
+
5
+ # @param name [String] printer's name
6
+ # @param options [Hash] printer's options
7
+ def initialize(name, is_default=nil, options={}, connection=nil)
8
+ @name = name
9
+ @is_default = is_default
10
+ @options = options
11
+ @connection = connection
12
+ end
13
+
14
+ # Returns the printer state
15
+ # @return [Symbol] printer state from options
16
+ def state
17
+ case options['printer-state']
18
+ when '3' then :idle
19
+ when '4' then :printing
20
+ when '5' then :stopped
21
+ else :unknown
22
+ end
23
+ end
24
+
25
+ # Returns the reason for the printer state
26
+ # @return [Array] array of reasons in string format
27
+ def state_reasons
28
+ options['printer-state-reasons'].split(/,/)
29
+ end
30
+
31
+ # Print a file (pdf, jpg, ps, etc.).
32
+ #
33
+ # You can pass in a hash of printing options if you want to override the printer's defaults.
34
+ #
35
+ # @return [Cups::Job] can be used to check on the job status.
36
+ def print_file(filename, title, options={})
37
+ raise "File not found: #{filename}" unless File.exist? filename
38
+
39
+ http = @connection.nil? ? nil : @connection.httpConnect2
40
+ with_destination(http) do |dest|
41
+ p_options = nil
42
+ num_options = 0
43
+ unless options.empty?
44
+ p_options = FFI::MemoryPointer.new :pointer
45
+ options.each do |k, v|
46
+ # Do not raise invalid options, since cupsCheckDestSupported states valid PPD options as unsupported.
47
+ # unless self.class.cupsCheckDestSupported(dest.to_ptr, k, v, http)
48
+ # raise "Option:#{k} #{v if v} not supported for printer: #{@name}"
49
+ #end
50
+ num_options = Cups.cupsAddOption(k, v, num_options, p_options)
51
+ end
52
+ p_options = p_options.get_pointer(0)
53
+ end
54
+
55
+ job_id = Cups.cupsPrintFile2(http, @name, filename, title, num_options, p_options)
56
+
57
+ if job_id == 0
58
+ raise Cups.cupsLastErrorString()
59
+ end
60
+ #Cups::Job.new(job_id, title, @name)
61
+ Cups::Job.get_job(job_id, @name, 0, @connection)
62
+ ensure
63
+ self.class.cupsFreeOptions(num_options, p_options) if p_options
64
+ end
65
+ ensure
66
+ Cups::Connection.close(http) if http
67
+ end
68
+
69
+ # Print a binary blob of data provided as a String.
70
+ #
71
+ # You must also specify the mime type.
72
+ # Like {print_file}, printing options are optional.
73
+ #
74
+ # @return [Cups::Job] can be used to check on the job status.
75
+ def print_data(data, mime_type, title, options = {})
76
+ http = @connection.nil? ? nil : @connection.httpConnect2
77
+ with_destination(http) do |dest|
78
+ p_options = nil
79
+ num_options = 0
80
+ unless options.empty?
81
+ p_options = FFI::MemoryPointer.new :pointer
82
+ options.each do |k, v|
83
+ num_options = Cups.cupsAddOption(k, v, num_options, p_options)
84
+ end
85
+ p_options = p_options.get_pointer(0)
86
+ end
87
+
88
+ job_id = Cups.cupsCreateJob(http, @name, title, num_options, p_options)
89
+ if job_id == 0
90
+ raise CupsFFI::cupsLastErrorString()
91
+ end
92
+
93
+ # Copy the data into a char pointer to allow null bytes
94
+ data_pointer = FFI::MemoryPointer.new(:char, data.bytesize)
95
+ data_pointer.put_bytes(0, data)
96
+
97
+ http_status = Cups.cupsStartDocument(@connection, @name,
98
+ job_id, title, mime_type, 1)
99
+
100
+ http_status = Cups.cupsWriteRequestData(@connection, data_pointer, data.bytesize)
101
+
102
+ ipp_status = Cups.cupsFinishDocument(@connection, @name)
103
+
104
+ data_pointer.free
105
+
106
+ raise ipp_status.to_s unless ipp_status == :ipp_ok
107
+
108
+ Cups::Job.get_job(job_id, @name, 0, @connection)
109
+ ensure
110
+ self.class.cupsFreeOptions(num_options, p_options) if p_options
111
+ end
112
+ ensure
113
+ Cups::Connection.close(http) if http
114
+ end
115
+
116
+ private def with_destination(http, &block)
117
+ self.class.with_destination(http, @name, &block)
118
+ end
119
+
120
+ def self.with_destination(http, name)
121
+ # Get all destinations with cupsGetDests2
122
+ dests = FFI::MemoryPointer.new :pointer
123
+ num_dests = Cups.cupsGetDests2(http, dests)
124
+
125
+ # Get the destination from name with cupsGetDest
126
+ p_dest = Cups.cupsGetDest(name, nil, num_dests, dests.get_pointer(0))
127
+ dest = Cups::Struct::Destination.new(p_dest)
128
+ raise "Destination with name: #{name} not found!" if dest.null?
129
+ yield dest
130
+ ensure
131
+ Cups.cupsFreeDests(num_dests, dests.get_pointer(0))
132
+ end
133
+
134
+ class IppGetResolutionParams < FFI::Struct
135
+ layout yres: :int, :units => Enum::IPP::Res
136
+ end
137
+ private_constant :IppGetResolutionParams
138
+
139
+ # Find the supported value(s) for the given option.
140
+ #
141
+ # If no option is given then the available options are returned.
142
+ #
143
+ # @return [Hash, nil] hash with keys :type and :values or nil when option is not supported
144
+ def find_dest_supported(option = "job-creation-attributes")
145
+ http = @connection.nil? ? nil : @connection.httpConnect2
146
+ with_destination(http) do |dest|
147
+ attr = self.class.cupsFindDestSupported(dest.to_ptr,
148
+ option, http);
149
+ return nil if attr.null?
150
+
151
+ count = IppAttibute.ippGetCount(attr)
152
+ name = IppAttibute.ippGetName(attr)
153
+ type = IppAttibute.ippGetValueTag(attr)
154
+ values = count.times.map do |idx|
155
+ case type
156
+ when :ipp_tag_resolution then
157
+ params = IppGetResolutionParams.new
158
+ xres = IppAttibute.ippGetResolution(attr, idx, params.to_ptr + params.offset_of(:yres), params.to_ptr + params.offset_of(:units))
159
+ if xres != 0
160
+ {xres: xres, yres: params[:yres], units: params[:units]}
161
+ end
162
+ when :ipp_tag_range then
163
+ p_uppervalue = FFI::MemoryPointer.new :int
164
+ low = IppAttibute.ippGetRange(attr, idx, p_uppervalue)
165
+ if low != 0
166
+ {lowervalue: low, uppervalue: p_uppervalue.read_int}
167
+ end
168
+ when :ipp_tag_enum then
169
+ int = IppAttibute.ippGetInteger(attr, idx)
170
+ name = IppAttibute.ippEnumString(option, int)
171
+ {int: int, name: name}
172
+ else
173
+ IppAttibute.ippGetString(attr, idx, nil) || IppAttibute.ippGetInteger(attr, idx)
174
+ end
175
+ end
176
+ { type: type, values: values, name: name }
177
+ end
178
+ end
179
+
180
+ # Get all destinations (printer devices)
181
+ # @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
182
+ def self.get_destinations(connection=nil)
183
+ pointer = FFI::MemoryPointer.new :pointer
184
+ dests = cupsGetDests2(pointer, connection)
185
+ printers = []
186
+ dests.each do |d|
187
+ printer = Cups::Printer.new(d[:name].dup, d[:is_default]!=0, printer_options(d), connection)
188
+ printers.push(printer)
189
+ end
190
+ cupsFreeDests(dests.count, pointer)
191
+ return printers
192
+ end
193
+
194
+ # Get a destination by name
195
+ # @param name [String] name of the printer
196
+ # @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
197
+ # @return [Printer] a printer object
198
+ def self.get_destination(name, connection=nil)
199
+ http = connection.nil? ? nil : connection.httpConnect2
200
+ printer = with_destination(http, name) do |dest|
201
+ Cups::Printer.new(dest[:name].dup, dest[:is_default]!=0, printer_options(dest), connection)
202
+ end
203
+ Cups::Connection.close(http) if http
204
+ return printer
205
+ end
206
+
207
+ # Get all possible options of the PPD file of the printer.
208
+ #
209
+ # These options can be passed to {print_file}.
210
+ # CUPS's PPD functions are deprecated, but many PPD based printers can be controlled only by PPD options, instead of IPP options.
211
+ #
212
+ # @return [Array<Hash>] List of accepted options and their attributes.
213
+ def ppd_options
214
+ ppd_path = Cups::Ppd.cupsGetPPD(name)
215
+ if ppd_path.nil? || ppd_path.empty?
216
+ raise "Error: Could not retrieve PPD for printer '#{name}'"
217
+ end
218
+
219
+ ppd_ptr = Cups::Ppd.ppdOpenFile(ppd_path)
220
+ if ppd_ptr.null?
221
+ raise "Error: Failed to open PPD file at #{ppd_path}"
222
+ end
223
+
224
+ # Let CUPS evaluate current printer system runtime option defaults
225
+ # Cups::Ppd.cupsMarkOptions(ppd_ptr, 0, nil)
226
+
227
+ options = []
228
+
229
+ opt_ptr = Cups::Ppd.ppdFirstOption(ppd_ptr)
230
+
231
+ while !opt_ptr.null?
232
+ option = Cups::Struct::PpdOption.new(opt_ptr)
233
+ choices_array = []
234
+
235
+ # Safe pointer stepping through contiguous C memory slots of choice definitions
236
+ option[:num_choices].times do |c_idx|
237
+ choice_stride = Cups::Struct::PpdChoice.size
238
+ choice_element_ptr = option[:choices] + (c_idx * choice_stride)
239
+ choice = Cups::Struct::PpdChoice.new(choice_element_ptr)
240
+
241
+ choices_array << {
242
+ choice: choice[:choice].to_s,
243
+ marked: choice[:marked] != 0,
244
+ }
245
+ end
246
+
247
+ options << {
248
+ keyword: option[:keyword].to_s,
249
+ text: option[:text].to_s,
250
+ choices: choices_array,
251
+ defchoice: option[:defchoice].to_s,
252
+ ui: option[:ui],
253
+ section: option[:section],
254
+ }
255
+
256
+ opt_ptr = Cups::Ppd.ppdNextOption(ppd_ptr)
257
+ end
258
+
259
+ options
260
+ ensure
261
+ if ppd_ptr && !ppd_ptr.null?
262
+ Cups::Ppd.ppdClose(ppd_ptr)
263
+ end
264
+ File.delete(ppd_path) if ppd_path && File.exist?(ppd_path)
265
+ end
266
+
267
+ private
268
+ # Wrapper around {::Cups#cupsGetDests2}
269
+ # @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
270
+ # @param pointer [Pointer] pointer to the destinations
271
+ # @return [Hash] hashmap of destination structs
272
+ def self.cupsGetDests2(pointer, connection=nil)
273
+ http = connection.nil? ? nil : connection.httpConnect2
274
+ num_dests = Cups.cupsGetDests2(http, pointer)
275
+ size = Cups::Struct::Destination.size
276
+ destinations = []
277
+ num_dests.times do |i|
278
+ destination = Cups::Struct::Destination.new(pointer.get_pointer(0) + (size * i))
279
+ destinations.push(destination)
280
+ end
281
+ Cups::Connection.close(http) if http
282
+ return destinations
283
+ end
284
+
285
+ # Wrapper around {::Cups#cupsCheckDestSupported}
286
+ # @param dest [Pointer] pointer to the destination
287
+ # @param option [String]
288
+ # @param value [String]
289
+ # @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
290
+ # @return [Boolean] true if supported, false otherwise
291
+ def self.cupsCheckDestSupported(dest, option, value, connection=nil)
292
+ info = Cups.cupsCopyDestInfo(connection, dest)
293
+ i = Cups.cupsCheckDestSupported(connection, dest, info, option, value)
294
+ return !i.zero?
295
+ end
296
+
297
+ def self.cupsFindDestSupported(dest, option, connection=nil)
298
+ info = Cups.cupsCopyDestInfo(connection, dest)
299
+ Cups.cupsFindDestSupported(connection, dest, info, option)
300
+ end
301
+
302
+ # Returns a destination's options
303
+ # @param dest [Object] {Cups::Struct::Destination} object
304
+ # @return [Hash] hash of destination' options as {Cups::Struct::Option}
305
+ def self.cups_options(dest)
306
+ size = Cups::Struct::Option.size
307
+ options = []
308
+
309
+ dest[:num_options].times do |i|
310
+ option = Cups::Struct::Option.new(dest[:options] + (size * i))
311
+ options.push(option)
312
+ end
313
+ return options
314
+ end
315
+
316
+ # Returns a destination's options as hash
317
+ # @param dest [Object] {Cups::Struct::Destination} object
318
+ # @return [Hash] hash of destination' options
319
+ def self.printer_options(dest)
320
+ dest_opts = cups_options(dest)
321
+ options = {}
322
+ dest_opts.each do |o|
323
+ options[o[:name].dup] = o[:value].dup
324
+ end
325
+ return options
326
+ end
327
+
328
+ # Wrapper around {::Cups#cupsFreeDests}
329
+ # @param num_dests [Integer]
330
+ # @param pointer [Pointer] pointer to the destinations
331
+ def self.cupsFreeDests(num_dests, pointer)
332
+ Cups.cupsFreeDests(num_dests, pointer.get_pointer(0))
333
+ end
334
+
335
+ # Wrapper around {::Cups#cupsFreeOptions}
336
+ # @param num_opts [Integer]
337
+ # @param pointer [Pointer] pointer to the options
338
+ def self.cupsFreeOptions(num_opts, pointer)
339
+ Cups.cupsFreeOptions(num_opts, pointer)
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,9 @@
1
+ module Cups::Struct
2
+ class Destination < FFI::Struct
3
+ layout :name, :string,
4
+ :instance, :string,
5
+ :is_default, :int,
6
+ :num_options, :int,
7
+ :options, :pointer
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Cups::Struct
2
+ class IppAttribute < FFI::Struct
3
+ # ipp_attribute_t is an opaque pointer
4
+ layout :dummy_dont_use, :char
5
+ end
6
+ end
@@ -0,0 +1,15 @@
1
+ module Cups::Struct
2
+ class Job < FFI::Struct
3
+ layout :id, :int,
4
+ :dest, :string,
5
+ :title, :string,
6
+ :user, :string,
7
+ :format, :string,
8
+ :state, Cups::Enum::IPP::JobState,
9
+ :size, :int,
10
+ :priority, :int,
11
+ :completed_time, :long,
12
+ :creation_time, :long,
13
+ :processing_time, :long
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Cups::Struct
2
+ class Option < FFI::Struct
3
+ layout :name, :string, :value, :string
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module Cups::Struct
2
+ class PpdChoice < FFI::Struct
3
+ layout :marked, :char, # 0 if unselected, 1 if active
4
+ :choice, [:char, Cups::PPD_MAX_NAME], # Computer key (e.g., "Letter")
5
+ :text, [:char, Cups::PPD_MAX_TEXT], # Human readable text
6
+ :code, :pointer, # Pointer to raw PostScript snippet string
7
+ :option, :pointer # Pointer to parent PpdOption struct
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Cups::Struct
2
+ class PpdFile < FFI::Struct
3
+ # we treat ppd_file_t as an opaque pointer
4
+ layout :dummy_dont_use, :char
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ module Cups::Struct
2
+ class PpdOption < FFI::Struct
3
+ layout :conflicted, :char, # Conflict status code
4
+ :keyword, [:char, Cups::PPD_MAX_NAME], # Main option key (e.g., "PageSize")
5
+ :defchoice, [:char, Cups::PPD_MAX_NAME], # Default option fallback choice
6
+ :text, [:char, Cups::PPD_MAX_TEXT], # Human readable descriptive flag
7
+ :ui, Cups::Enum::Ppd::Ui, # UI type enum (PickOne, PickMany, etc.)
8
+ :section, Cups::Enum::Ppd::Section, # Order dependency section code
9
+ :order, :float, # Sequence execution priority
10
+ :num_choices, :int, # Choices element scale length
11
+ :choices, :pointer, # Points to sequential array block of PpdChoice
12
+ :subgroup, :pointer # PpdSubgroup relationship tracking link
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Cups
2
+ VERSION = "0.4.0"
3
+ end
data/lib/ffi-cups.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'ffi'
2
+
3
+ module Cups
4
+
5
+ # Custom or default path for libcups.so
6
+ def self.libcups
7
+ if ENV['CUPS_LIB']
8
+ ENV['CUPS_LIB']
9
+ else
10
+ FFI::LibraryPath.new('cups', abi_number: 2)
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ # Constants
17
+ require 'ffi-cups/constants'
18
+
19
+ # Enums
20
+ require 'ffi-cups/enum/http/status'
21
+ require 'ffi-cups/enum/http/ppd'
22
+
23
+ # IPP
24
+ require 'ffi-cups/enum/ipp/status'
25
+ require 'ffi-cups/enum/ipp/job_state'
26
+ require 'ffi-cups/enum/ipp/res'
27
+ require 'ffi-cups/enum/ipp/tag'
28
+
29
+ # Structs
30
+ require 'ffi-cups/struct/option'
31
+ require 'ffi-cups/struct/destination'
32
+ require 'ffi-cups/struct/job'
33
+ require 'ffi-cups/struct/ipp_attribute'
34
+ require 'ffi-cups/struct/ppd_choice'
35
+ require 'ffi-cups/struct/ppd_file'
36
+ require 'ffi-cups/struct/ppd_option'
37
+
38
+ # Functions
39
+ require 'ffi-cups/ffi/cups'
40
+ require 'ffi-cups/ffi/http'
41
+ require 'ffi-cups/ffi/array'
42
+ require 'ffi-cups/ffi/ipp_attribute'
43
+ require 'ffi-cups/ffi/ppd'
44
+
45
+ # wrappers
46
+ require 'ffi-cups/connection'
47
+ require 'ffi-cups/job'
48
+ require 'ffi-cups/printer'
data/lib/ffi-cups2.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative 'ffi-cups'
data.tar.gz.sig ADDED
@@ -0,0 +1,4 @@
1
+ ���K�AD@<jG7�E�W�KD�I +�EI��_%�C .�4�I6㟠�x��7�D ���4�Ԋ��IlVC�K1e�L�x�W�=���L�#��pa>������ �|��b��y|
2
+ 4�
3
+ �ݿփ�9�q"hvD�c�m�j��QJn�W،���ז���&���z��ó<Cr"���hN�kۓn��n=;m�\��@�q�늠��J��
4
+ z�yˮ��ʧ?������iB��j�b���K� 0����(9������(
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ffi-cups2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Hugo Marquez
8
+ - Nathan Ehresman
9
+ - Lars Kanis
10
+ bindir: exe
11
+ cert_chain:
12
+ - |
13
+ -----BEGIN CERTIFICATE-----
14
+ MIIEBDCCAmygAwIBAgIBBDANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1sYXJz
15
+ L0RDPWdyZWl6LXJlaW5zZG9yZi9EQz1kZTAeFw0yNjAxMDUxMTE5MzRaFw0yNzAx
16
+ MDUxMTE5MzRaMCgxJjAkBgNVBAMMHWxhcnMvREM9Z3JlaXotcmVpbnNkb3JmL0RD
17
+ PWRlMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwum6Y1KznfpzXOT/
18
+ mZgJTBbxZuuZF49Fq3K0WA67YBzNlDv95qzSp7V/7Ek3NCcnT7G+2kSuhNo1FhdN
19
+ eSDO/moYebZNAcu3iqLsuzuULXPLuoU0GsMnVMqV9DZPh7cQHE5EBZ7hlzDBK7k/
20
+ 8nBMvR0mHo77kIkapHc26UzVq/G0nKLfDsIHXVylto3PjzOumjG6GhmFN4r3cP6e
21
+ SDfl1FSeRYVpt4kmQULz/zdSaOH3AjAq7PM2Z91iGwQvoUXMANH2v89OWjQO/NHe
22
+ JMNDFsmHK/6Ji4Kk48Z3TyscHQnipAID5GhS1oD21/WePdj7GhmbF5gBzkV5uepd
23
+ eJQPgWGwrQW/Z2oPjRuJrRofzWfrMWqbOahj9uth6WSxhNexUtbjk6P8emmXOJi5
24
+ chQPnWX+N3Gj+jjYxqTFdwT7Mj3pv1VHa+aNUbqSPpvJeDyxRIuo9hvzDaBHb/Cg
25
+ 9qRVcm8a96n4t7y2lrX1oookY6bkBaxWOMtWlqIprq8JZXM9AgMBAAGjOTA3MAkG
26
+ A1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQ4h1tIyvdUWtMI739xMzTR
27
+ 7EfMFzANBgkqhkiG9w0BAQsFAAOCAYEAg/1LuXN3X8xwHVnhRvmBCZYkNErko40y
28
+ juq/qPF9vkPGxLEGaeZF1rv/obTs5SgIGEUkWdRsOl3F6bQN4MYUFlN/n6rPx19g
29
+ CqUMkmRtuFD+9EFBI4twoF2IJS0pYr9tkjUq8mb0VXblvfbEEUf460BJmuBXZTc6
30
+ OoLQHxjhhqhKBoP4lHXg/OMK9dnGn8+5DoKcsyWQvqq/GdK8pi4E47M/egzw54Go
31
+ g+oeyGDsNKOQlnIA9gbyPpExD1+akJ4v88T8jOUnsOkhBBUkpSktC2QmEYIU5e9j
32
+ N7muw6O5Bz3CQuSWIEjj9mMUS2fEEfq2km6zO5vs5eD6TdFnt8AC/HlOrPaxGcUX
33
+ Hjw/K7ZTvZLEb4awRijdy4wx3OlL9FAyZPjYOh8TdJrLl076c7GyimAkgnc8BrL5
34
+ g1vGc759o2t3KNnlUg8VwqkhFJp4gCko1QO/8Io5hPQZOvJw8dmqNiiiHqaDupfl
35
+ CI4mk2K8yQa8xtQ/RmFZxYS5XbHhDJCw
36
+ -----END CERTIFICATE-----
37
+ date: 1980-01-02 00:00:00.000000000 Z
38
+ dependencies:
39
+ - !ruby/object:Gem::Dependency
40
+ name: ffi
41
+ requirement: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '1.16'
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '1.16'
53
+ description: Simple wrapper around libcups to give CUPS printing capabilities to Ruby
54
+ apps.
55
+ email:
56
+ - hugomarquez.dev@gmail.com
57
+ - n_github@ehresman.org
58
+ - lars@greiz-reinsdorf.de
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - ffi-cups.gemspec
71
+ - lib/ffi-cups.rb
72
+ - lib/ffi-cups/connection.rb
73
+ - lib/ffi-cups/constants.rb
74
+ - lib/ffi-cups/enum/http/ppd.rb
75
+ - lib/ffi-cups/enum/http/status.rb
76
+ - lib/ffi-cups/enum/ipp/job_state.rb
77
+ - lib/ffi-cups/enum/ipp/res.rb
78
+ - lib/ffi-cups/enum/ipp/status.rb
79
+ - lib/ffi-cups/enum/ipp/tag.rb
80
+ - lib/ffi-cups/ffi/array.rb
81
+ - lib/ffi-cups/ffi/cups.rb
82
+ - lib/ffi-cups/ffi/http.rb
83
+ - lib/ffi-cups/ffi/ipp_attribute.rb
84
+ - lib/ffi-cups/ffi/ppd.rb
85
+ - lib/ffi-cups/job.rb
86
+ - lib/ffi-cups/printer.rb
87
+ - lib/ffi-cups/struct/destination.rb
88
+ - lib/ffi-cups/struct/ipp_attribute.rb
89
+ - lib/ffi-cups/struct/job.rb
90
+ - lib/ffi-cups/struct/option.rb
91
+ - lib/ffi-cups/struct/ppd_choice.rb
92
+ - lib/ffi-cups/struct/ppd_file.rb
93
+ - lib/ffi-cups/struct/ppd_option.rb
94
+ - lib/ffi-cups/version.rb
95
+ - lib/ffi-cups2.rb
96
+ homepage: https://github.com/larskanis/ffi-cups
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '3.2'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 4.0.16
115
+ specification_version: 4
116
+ summary: FFI wrapper around libcups2
117
+ test_files: []
metadata.gz.sig ADDED
Binary file