ffi-cups 0.1.1 → 0.1.9
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +27 -13
- data/lib/ffi-cups.rb +1 -0
- data/lib/ffi-cups/connection.rb +8 -8
- data/lib/ffi-cups/ffi/cups.rb +9 -1
- data/lib/ffi-cups/job.rb +1 -3
- data/lib/ffi-cups/printer.rb +110 -39
- data/lib/ffi-cups/version.rb +1 -1
- metadata +2 -3
- data/Gemfile.lock +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cd36e5fc525b811e3fedc0ef3b015aa47e04cf93528510a539ae3562a0e0357
|
4
|
+
data.tar.gz: ca00e58337c11b4291dc1debd2908e21e3bb772d4127779fc28937043fd77aa6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f751bb3648d4b66926dc962a469f6446de032c0d309892ff8defae327051c5425bdb54ad7bf52f557e11d4f03e120269abc63b17a0091ac47652042e6350bfa
|
7
|
+
data.tar.gz: 5726071fba04a620382afc78954c3f0790c496befdecc0cad3a3c8327cf440c4fd9f68e03a96d695fa6c2ef737bbcdb709f49cbe89889feb7eaa20292b1c2f14
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# ffi-cups
|
2
|
+
[](https://badge.fury.io/rb/ffi-cups)
|
3
|
+
|
2
4
|
ffi-cups is a FFI wrapper around libcups providing access to the Cups API.
|
3
5
|
It was written using Ruby 2.7.0.
|
4
6
|
|
@@ -20,38 +22,50 @@ gem install ffi-cups
|
|
20
22
|
ffi-cups requires libcups2 to be installed
|
21
23
|
|
22
24
|
## TODO
|
23
|
-
-
|
25
|
+
- Job's state
|
26
|
+
- Cancel jobs
|
27
|
+
- Get all jobs with filters
|
24
28
|
|
25
29
|
## Example usage
|
26
30
|
```ruby
|
27
31
|
require 'ffi-cups'
|
28
32
|
|
29
33
|
printers = Cups::Printer.get_destinations
|
30
|
-
# [#<Cups::Printer:
|
34
|
+
# [#<Cups::Printer:0x000055fe50b15798 @name="Virtual_PDF_Printer",
|
35
|
+
# @options={"copies"=>"1", "device-uri"=>"cups-pdf:/", "finishings"=>"3"
|
36
|
+
# "job-cancel-after"=>"10800", "job-hold-until"=>"no-hold", ...
|
31
37
|
|
32
38
|
printer = Cups::Printer.get_destination("Virtual_PDF_Printer")
|
33
|
-
|
39
|
+
# <Cups::Printer:0x0000560f1d4e0958 @name="Virtual_PDF_Printer", @options={"copies"=>"1"
|
40
|
+
# "device-uri"=>"cups-pdf:/", ...
|
41
|
+
|
42
|
+
printer.state
|
43
|
+
# :idle
|
44
|
+
|
45
|
+
printer.state_reasons
|
46
|
+
# ["none"]
|
47
|
+
|
48
|
+
# Print a file (PDF, JPG, etc) you can pass a hash of printing options if you
|
49
|
+
# want to override the printer's default. See Cups::Constants for more options
|
50
|
+
options = {
|
51
|
+
Cups::CUPS_MEDIA => Cups::CUPS_MEDIA_A4,
|
52
|
+
Cups::CUPS_ORIENTATION => Cups::CUPS_ORIENTATION_LANDSCAPE
|
53
|
+
}
|
54
|
+
|
55
|
+
job = printer.print_file('/tmp/example.jpg', 'Title', options)
|
34
56
|
|
35
57
|
```
|
36
58
|
|
37
59
|
## Remote CUPS Server
|
38
60
|
You may create a connection object passing a :hostname and/or :port arguments.
|
39
61
|
|
40
|
-
You are in charge of the connection object, remember to close the
|
41
|
-
connection once you stop using it.
|
42
|
-
|
43
62
|
```ruby
|
44
63
|
# Create a Connection object with hostname and/or port
|
45
|
-
connection = Cups::Connection.instance(
|
46
|
-
|
47
|
-
# Create a http pointer with CUPS API
|
48
|
-
http = connection.httpConnect2
|
64
|
+
connection = Cups::Connection.instance('print.example.com')
|
49
65
|
|
50
66
|
# Get all printers from the remote connection
|
51
|
-
remote_printers = Cups::Printer.get_destinations(
|
67
|
+
remote_printers = Cups::Printer.get_destinations(connection)
|
52
68
|
|
53
|
-
# Close connection and release pointer
|
54
|
-
Cups::Connection.close(http)
|
55
69
|
```
|
56
70
|
|
57
71
|
## License
|
data/lib/ffi-cups.rb
CHANGED
data/lib/ffi-cups/connection.rb
CHANGED
@@ -8,19 +8,20 @@ module Cups
|
|
8
8
|
|
9
9
|
private_class_method :new
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
@hostname =
|
13
|
-
@port =
|
11
|
+
def initialize(hostname, port=nil)
|
12
|
+
@hostname = hostname
|
13
|
+
@port = port.nil? ? 631 : port
|
14
14
|
end
|
15
15
|
|
16
16
|
# Returns or creates a singleton instance
|
17
|
-
# @param
|
17
|
+
# @param hostname [String]
|
18
|
+
# @param port [Integer]
|
18
19
|
# @return [Object] a connection object
|
19
|
-
def self.instance(
|
20
|
+
def self.instance(hostname, port=nil)
|
20
21
|
return @instance if @instance
|
21
22
|
|
22
23
|
@instance_mutex.synchronize do
|
23
|
-
@instance ||= new(
|
24
|
+
@instance ||= new(hostname, port)
|
24
25
|
end
|
25
26
|
|
26
27
|
@instance
|
@@ -49,8 +50,7 @@ module Cups
|
|
49
50
|
# Wrapper around {::FFI::Cups::Http#httpClose}
|
50
51
|
# @param http (Pointer)
|
51
52
|
def self.close(http)
|
52
|
-
FFI::Cups::Http.httpClose(
|
53
|
-
pointer.autorelease = true
|
53
|
+
FFI::Cups::Http.httpClose(http)
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
data/lib/ffi-cups/ffi/cups.rb
CHANGED
@@ -23,6 +23,14 @@ module FFI::Cups
|
|
23
23
|
# {https://www.cups.org/doc/cupspm.html#cupsCheckDestSupported}
|
24
24
|
attach_function 'cupsCheckDestSupported', [:pointer, :pointer, :pointer, :string, :string], :int, blocking: true
|
25
25
|
|
26
|
+
# Get the supported values/capabilities for the destination.
|
27
|
+
# @overload cupsCopyDestInfo(pointer, pointer)
|
28
|
+
# @param http [Pointer]
|
29
|
+
# @param dest [Pointer]
|
30
|
+
# @return [Pointer] Destination information
|
31
|
+
# {https://www.cups.org/doc/cupspm.html#cupsCopyDestInfo}
|
32
|
+
attach_function 'cupsCopyDestInfo', [:pointer, :pointer], :pointer, blocking: true
|
33
|
+
|
26
34
|
# Get the named destination from the list.
|
27
35
|
# @overload cupsGetDest(string, string, int, pointer)
|
28
36
|
# @param name [String]
|
@@ -46,7 +54,7 @@ module FFI::Cups
|
|
46
54
|
# @param number [Integer] of destinations
|
47
55
|
# @param pointer [Pointer] to destinations
|
48
56
|
# {https://www.cups.org/doc/cupspm.html#cupsFreeDests}
|
49
|
-
attach_function 'cupsFreeDests', [
|
57
|
+
attach_function 'cupsFreeDests', [:int, :pointer], :void, blocking: true
|
50
58
|
|
51
59
|
# Prints a File
|
52
60
|
# @overload cupsPrintFile(string, string, string, int, pointer)
|
data/lib/ffi-cups/job.rb
CHANGED
data/lib/ffi-cups/printer.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
module Cups
|
2
2
|
class Printer
|
3
|
-
attr_reader :name, :options
|
3
|
+
attr_reader :name, :options, :connection
|
4
4
|
|
5
5
|
# @param name [String] printer's name
|
6
6
|
# @param options [Hash] printer's options
|
7
|
-
def initialize(name, options={})
|
7
|
+
def initialize(name, options={}, connection=nil)
|
8
8
|
@name = name
|
9
9
|
@options = options
|
10
|
+
@connection = connection
|
10
11
|
end
|
11
12
|
|
12
13
|
# Returns the printer state
|
@@ -26,36 +27,80 @@ module Cups
|
|
26
27
|
options['printer-state-reasons'].split(/,/)
|
27
28
|
end
|
28
29
|
|
30
|
+
def print_file(filename, title, options={})
|
31
|
+
raise "File not found: #{filename}" unless File.exists? filename
|
32
|
+
|
33
|
+
http = @connection.nil? ? nil : @connection.httpConnect2
|
34
|
+
# Get all destinations with cupsGetDests2
|
35
|
+
dests = FFI::MemoryPointer.new :pointer
|
36
|
+
num_dests = FFI::Cups.cupsGetDests2(http, dests)
|
37
|
+
|
38
|
+
# Get the destination from name with cupsGetDest
|
39
|
+
p_dest = FFI::Cups.cupsGetDest(@name, nil, num_dests, dests.get_pointer(0))
|
40
|
+
dest = Cups::Struct::Destination.new(p_dest)
|
41
|
+
raise "Destination with name: #{@name} not found!" if dest.null?
|
42
|
+
|
43
|
+
p_options = nil
|
44
|
+
num_options = 0
|
45
|
+
unless options.empty?
|
46
|
+
p_options = FFI::MemoryPointer.new :pointer
|
47
|
+
options.each do |k, v|
|
48
|
+
unless self.class.cupsCheckDestSupported(p_dest, k, v, http)
|
49
|
+
raise "Option:#{k} #{v if v} not supported for printer: #{@name}"
|
50
|
+
end
|
51
|
+
num_options = FFI::Cups.cupsAddOption(k, v, num_options, p_options)
|
52
|
+
end
|
53
|
+
p_options = p_options.get_pointer(0)
|
54
|
+
end
|
55
|
+
|
56
|
+
job_id = FFI::Cups.cupsPrintFile2(http, @name, filename, title, num_options, p_options)
|
57
|
+
job = Cups::Job.new(job_id, title, self)
|
58
|
+
|
59
|
+
if job_id.zero?
|
60
|
+
last_error = Cups.cupsLastErrorString()
|
61
|
+
self.class.cupsFreeOptions(num_options, p_options) unless options.empty?
|
62
|
+
raise last_error
|
63
|
+
end
|
64
|
+
|
65
|
+
self.class.cupsFreeOptions(num_options, p_options) unless options.empty?
|
66
|
+
self.class.cupsFreeDests(num_dests, dests)
|
67
|
+
Cups::Connection.close(http)
|
68
|
+
return job
|
69
|
+
end
|
70
|
+
|
29
71
|
# Get all destinations (printer devices)
|
30
72
|
# @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
|
31
73
|
def self.get_destinations(connection=nil)
|
32
74
|
pointer = FFI::MemoryPointer.new :pointer
|
33
|
-
|
75
|
+
dests = cupsGetDests2(pointer, connection)
|
34
76
|
printers = []
|
35
|
-
|
36
|
-
|
37
|
-
options = {}
|
38
|
-
|
39
|
-
destination_opt.each do |k, o|
|
40
|
-
options[o[:name].dup] = o[:value].dup
|
41
|
-
end
|
42
|
-
|
43
|
-
printer = Cups::Printer.new(d[:name].dup, options)
|
77
|
+
dests.each do |d|
|
78
|
+
printer = Cups::Printer.new(d[:name].dup, printer_options(d), connection)
|
44
79
|
printers.push(printer)
|
45
80
|
end
|
46
|
-
cupsFreeDests(
|
81
|
+
cupsFreeDests(dests.count, pointer)
|
47
82
|
return printers
|
48
83
|
end
|
49
84
|
|
50
85
|
# Get a destination by name
|
51
86
|
# @param name [String] name of the printer
|
52
87
|
# @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
|
88
|
+
# @return [Printer] a printer object
|
53
89
|
def self.get_destination(name, connection=nil)
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
90
|
+
http = connection.nil? ? nil : connection.httpConnect2
|
91
|
+
# Get all destinations with cupsGetDests2
|
92
|
+
dests = FFI::MemoryPointer.new :pointer
|
93
|
+
num_dests = FFI::Cups.cupsGetDests2(http, dests)
|
94
|
+
|
95
|
+
# Get the destination from name with cupsGetDest
|
96
|
+
p_dest = FFI::Cups.cupsGetDest(name, nil, num_dests, dests.get_pointer(0))
|
97
|
+
dest = Cups::Struct::Destination.new(p_dest)
|
98
|
+
raise "Destination with name: #{name} not found!" if dest.null?
|
99
|
+
|
100
|
+
printer = Cups::Printer.new(dest[:name].dup, printer_options(dest), connection)
|
101
|
+
cupsFreeDests(num_dests, dests)
|
102
|
+
Cups::Connection.close(http) if http
|
103
|
+
return printer
|
59
104
|
end
|
60
105
|
|
61
106
|
private
|
@@ -63,43 +108,69 @@ module Cups
|
|
63
108
|
# @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
|
64
109
|
# @param pointer [Pointer] pointer to the destinations
|
65
110
|
# @return [Hash] hashmap of destination structs
|
66
|
-
def self.cupsGetDests2(connection=nil
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
111
|
+
def self.cupsGetDests2(pointer, connection=nil)
|
112
|
+
http = connection.nil? ? nil : connection.httpConnect2
|
113
|
+
num_dests = FFI::Cups.cupsGetDests2(http, pointer)
|
114
|
+
size = Cups::Struct::Destination.size
|
115
|
+
destinations = []
|
116
|
+
num_dests.times do |i|
|
117
|
+
destination = Cups::Struct::Destination.new(pointer.get_pointer(0) + (size * i))
|
118
|
+
destinations.push(destination)
|
73
119
|
end
|
120
|
+
Cups::Connection.close(http) if http
|
74
121
|
return destinations
|
75
122
|
end
|
76
123
|
|
124
|
+
# Wrapper around {::FFI::Cups#cupsCheckDestSupported}
|
125
|
+
# @param dest [Pointer] pointer to the destination
|
126
|
+
# @param option [String]
|
127
|
+
# @param value [String]
|
128
|
+
# @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
|
129
|
+
# @return [Boolean] true if supported, false otherwise
|
130
|
+
def self.cupsCheckDestSupported(dest, option, value, connection=nil)
|
131
|
+
info = FFI::Cups.cupsCopyDestInfo(connection, dest)
|
132
|
+
i = FFI::Cups.cupsCheckDestSupported(connection, dest, info, option, value)
|
133
|
+
return !i.zero?
|
134
|
+
end
|
135
|
+
|
77
136
|
# Returns a destination's options
|
78
|
-
# @param
|
79
|
-
# @return [Hash]
|
80
|
-
def self.
|
81
|
-
|
82
|
-
options =
|
137
|
+
# @param dest [Object] {Cups::Struct::Destination} object
|
138
|
+
# @return [Hash] hash of destination' options as {Cups::Struct::Option}
|
139
|
+
def self.cups_options(dest)
|
140
|
+
size = Cups::Struct::Option.size
|
141
|
+
options = []
|
142
|
+
|
143
|
+
dest[:num_options].times do |i|
|
144
|
+
option = Cups::Struct::Option.new(dest[:options] + (size * i))
|
145
|
+
options.push(option)
|
146
|
+
end
|
147
|
+
return options
|
148
|
+
end
|
83
149
|
|
84
|
-
|
85
|
-
|
86
|
-
|
150
|
+
# Returns a destination's options as hash
|
151
|
+
# @param dest [Object] {Cups::Struct::Destination} object
|
152
|
+
# @return [Hash] hash of destination' options
|
153
|
+
def self.printer_options(dest)
|
154
|
+
dest_opts = cups_options(dest)
|
155
|
+
options = {}
|
156
|
+
dest_opts.each do |o|
|
157
|
+
options[o[:name].dup] = o[:value].dup
|
87
158
|
end
|
88
159
|
return options
|
89
160
|
end
|
90
161
|
|
91
162
|
# Wrapper around {::FFI::Cups#cupsFreeDests}
|
92
|
-
# @param
|
163
|
+
# @param num_dests [Integer]
|
93
164
|
# @param pointer [Pointer] pointer to the destinations
|
94
|
-
def self.cupsFreeDests(
|
95
|
-
FFI::Cups.cupsFreeDests(
|
165
|
+
def self.cupsFreeDests(num_dests, pointer)
|
166
|
+
FFI::Cups.cupsFreeDests(num_dests, pointer.get_pointer(0))
|
96
167
|
end
|
97
168
|
|
98
169
|
# Wrapper around {::FFI::Cups#cupsFreeOptions}
|
99
|
-
# @param
|
170
|
+
# @param num_opts [Integer]
|
100
171
|
# @param pointer [Pointer] pointer to the options
|
101
|
-
def self.cupsFreeOptions(
|
102
|
-
FFI::Cups.cupsFreeOptions(
|
172
|
+
def self.cupsFreeOptions(num_opts, pointer)
|
173
|
+
FFI::Cups.cupsFreeOptions(num_opts, pointer)
|
103
174
|
end
|
104
175
|
end
|
105
176
|
end
|
data/lib/ffi-cups/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-cups
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hugo Marquez
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-04-
|
12
|
+
date: 2021-04-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -35,7 +35,6 @@ extra_rdoc_files: []
|
|
35
35
|
files:
|
36
36
|
- ".gitignore"
|
37
37
|
- Gemfile
|
38
|
-
- Gemfile.lock
|
39
38
|
- LICENSE
|
40
39
|
- README.md
|
41
40
|
- Rakefile
|
data/Gemfile.lock
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
ffi-cups (0.1.0)
|
5
|
-
ffi (~> 1.15.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: http://rubygems.org/
|
9
|
-
specs:
|
10
|
-
byebug (11.1.3)
|
11
|
-
diff-lcs (1.4.4)
|
12
|
-
ffi (1.15.0)
|
13
|
-
rake (12.3.3)
|
14
|
-
rspec (3.10.0)
|
15
|
-
rspec-core (~> 3.10.0)
|
16
|
-
rspec-expectations (~> 3.10.0)
|
17
|
-
rspec-mocks (~> 3.10.0)
|
18
|
-
rspec-core (3.10.1)
|
19
|
-
rspec-support (~> 3.10.0)
|
20
|
-
rspec-expectations (3.10.1)
|
21
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
-
rspec-support (~> 3.10.0)
|
23
|
-
rspec-mocks (3.10.2)
|
24
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
25
|
-
rspec-support (~> 3.10.0)
|
26
|
-
rspec-support (3.10.2)
|
27
|
-
|
28
|
-
PLATFORMS
|
29
|
-
ruby
|
30
|
-
|
31
|
-
DEPENDENCIES
|
32
|
-
byebug
|
33
|
-
ffi-cups!
|
34
|
-
rake (~> 12.0)
|
35
|
-
rspec (~> 3.0)
|
36
|
-
|
37
|
-
BUNDLED WITH
|
38
|
-
2.1.2
|