ffi-cups 0.1.1 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/ffi-cups.svg)](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
|