ffi-cups 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f2e82ad0fb30fa3e5c587658a3eef2af2044b3e06564e581c68e3b6d2d7e18db
4
+ data.tar.gz: c0f9f05991f788f5e4f2e10cf2e603fea2bb3eb45061d8318bd769e92be2b2f0
5
+ SHA512:
6
+ metadata.gz: 4d1d560770915cf815dd32172d4707068401b9b7892babc5c3d099d99de7e10a567dd4a59a7543510f20d0a266361cf73b1e0abf23a971d04c4795d6be6d7c70
7
+ data.tar.gz: 6e3400f9edef9223e67fc46fbe4c51b1d7492c0e99d3a9e60360d3bb7745df9c6a79eb2d35ca7a805740dbe158f0848b3b4d4f2a3094ac140f1a7bb58ab49656
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ .bundle/
2
+ log/
3
+ pkg/
4
+ *.gem
5
+
6
+ # YARD
7
+ .yardopts
8
+ .yardoc
9
+ doc/*
10
+
11
+ # Byebug
12
+ .byebug_history
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ffi-cups.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
8
+ gem 'byebug'
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
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
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2021 Hugo Marquez & Contributors
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.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # ffi-cups
2
+ ffi-cups is a FFI wrapper around libcups providing access to the Cups API.
3
+ It was written using Ruby 2.7.0.
4
+
5
+ ## About
6
+ This is a complete rewrite of the cupsffi gem, it will deprecate
7
+ functions deprecated by the CUPS API, if you wish to continue to use them,
8
+ use the cupsffi gem.
9
+
10
+ ## Authors
11
+ - Hugo Marquez @ www.hugomarquez.mx
12
+ - Contributors @ https://github.com/hugomarquez/ffi-cups/graphs/contributors
13
+
14
+ ## Installation
15
+ ```bash
16
+ gem install ffi-cups
17
+ ```
18
+
19
+ ## Setup
20
+ ffi-cups requires libcups2 to be installed
21
+
22
+ ## TODO
23
+ - send print jobs by creating the job itself first.
24
+
25
+ ## Example usage
26
+ ```ruby
27
+ require 'ffi-cups'
28
+
29
+ printers = Cups::Printer.get_destinations
30
+ # [#<Cups::Printer:0x000055fe50b178e0 @name="HP_Officejet_J4500_series_ubuntu", @options={"device-uri"=>"ipps://HP%20Officejet%20J4500%20series%20%40%20ubuntu._ipps._tcp.local/cups", "printer-info"=>"HP Officejet J4500 series @ ubuntu", "printer-location"=>"", "printer-make-and-model"=>"HP Officejet j4585 All-in-one Printer", "printer-type"=>"16814110"}>, #<Cups::Printer:0x000055fe50b15798 @name="Virtual_PDF_Printer", @options={"copies"=>"1", "device-uri"=>"cups-pdf:/", "finishings"=>"3", "job-cancel-after"=>"10800", "job-hold-until"=>"no-hold", "job-priority"=>"50", "job-sheets"=>"none,none", "marker-change-time"=>"0", "number-up"=>"1", "printer-commands"=>"AutoConfigure,Clean,PrintSelfTestPage", "printer-info"=>"Virtual PDF Printer", "printer-is-accepting-jobs"=>"true", "printer-is-shared"=>"false", "printer-is-temporary"=>"false", "printer-location"=>"", "printer-make-and-model"=>"Generic CUPS-PDF Printer (w/ options)", "printer-state"=>"3", "printer-state-change-time"=>"1617884232", "printer-state-reasons"=>"none", "printer-type"=>"10547276", "printer-uri-supported"=>"ipp://localhost/printers/Virtual_PDF_Printer"}>]
31
+
32
+ printer = Cups::Printer.get_destination("Virtual_PDF_Printer")
33
+ #<Cups::Printer:0x0000560f1d4e0958 @name="Virtual_PDF_Printer", @options={"copies"=>"1", "device-uri"=>"cups-pdf:/", "finishings"=>"3", "job-cancel-after"=>"10800", "job-hold-until"=>"no-hold", "job-priority"=>"50", "job-sheets"=>"none,none", "marker-change-time"=>"0", "number-up"=>"1", "printer-commands"=>"AutoConfigure,Clean,PrintSelfTestPage", "printer-info"=>"Virtual PDF Printer", "printer-is-accepting-jobs"=>"true", "printer-is-shared"=>"false", "printer-is-temporary"=>"false", "printer-location"=>"", "printer-make-and-model"=>"Generic CUPS-PDF Printer (w/ options)", "printer-state"=>"3", "printer-state-change-time"=>"1617884232", "printer-state-reasons"=>"none", "printer-type"=>"10547276", "printer-uri-supported"=>"ipp://localhost/printers/Virtual_PDF_Printer"}>
34
+
35
+ ```
36
+
37
+ ## Remote CUPS Server
38
+ You may create a connection object passing a :hostname and/or :port arguments.
39
+
40
+ You are in charge of the connection object, remember to close the
41
+ connection once you stop using it.
42
+
43
+ ```ruby
44
+ # Create a Connection object with hostname and/or port
45
+ connection = Cups::Connection.instance(hostname:'print.example.com')
46
+
47
+ # Create a http pointer with CUPS API
48
+ http = connection.httpConnect2
49
+
50
+ # Get all printers from the remote connection
51
+ remote_printers = Cups::Printer.get_destinations(http)
52
+
53
+ # Close connection and release pointer
54
+ Cups::Connection.close(http)
55
+ ```
56
+
57
+ ## License
58
+ The MIT License
59
+
60
+ Copyright (c) 2021 Hugo Marquez & Contributors
61
+
62
+ Permission is hereby granted, free of charge, to any person obtaining a copy
63
+ of this software and associated documentation files (the "Software"), to deal
64
+ in the Software without restriction, including without limitation the rights
65
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
66
+ copies of the Software, and to permit persons to whom the Software is
67
+ furnished to do so, subject to the following conditions:
68
+
69
+ The above copyright notice and this permission notice shall be included in
70
+ all copies or substantial portions of the Software.
71
+
72
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
73
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
74
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
75
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
76
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
77
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
78
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ffi-cups"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/ffi-cups.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ require_relative 'lib/ffi-cups/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "ffi-cups"
5
+ s.version = Cups::VERSION
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["Hugo Marquez", "Nathan Ehresman"]
8
+ s.email = ["hugomarquez.dev@gmail.com"]
9
+ s.homepage = "https://github.com/hugomarquez/ffi-cups"
10
+ s.summary = "FFI wrapper around libcups"
11
+ s.description = "Simple wrapper around libcups to give CUPS printing capabilities to Ruby apps."
12
+ s.license = "MIT"
13
+ s.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ s.add_runtime_dependency "ffi", "~> 1.15.0"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ s.bindir = "exe"
23
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+
25
+ s.require_paths = ["lib"]
26
+ end
data/lib/ffi-cups.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'ffi'
2
+ require 'byebug'
3
+
4
+ module Cups
5
+ extend FFI::Library
6
+
7
+ begin
8
+ if ENV['CUPS_LIB']
9
+ ffi_lib(ENV['CUPS_LIB'])
10
+ else
11
+ ffi_lib('cups')
12
+ end
13
+ rescue LoadError => e
14
+ raise LoadError, "Didn't find libcups on your system."
15
+ end
16
+ end
17
+
18
+ # Constants
19
+ require 'ffi-cups/constants'
20
+
21
+ # Enums
22
+ require 'ffi-cups/enum/http/status'
23
+ # IPP
24
+ require 'ffi-cups/enum/ipp/status'
25
+ require 'ffi-cups/enum/ipp/job_state'
26
+
27
+ # Structs
28
+ require 'ffi-cups/struct/option'
29
+ require 'ffi-cups/struct/destination'
30
+ require 'ffi-cups/struct/job'
31
+
32
+ # Functions
33
+ require 'ffi-cups/ffi/cups'
34
+ require 'ffi-cups/ffi/http'
35
+ require 'ffi-cups/ffi/array'
36
+
37
+ # wrappers
38
+ require 'ffi-cups/connection'
39
+ require 'ffi-cups/printer'
@@ -0,0 +1,57 @@
1
+ module Cups
2
+ class Connection
3
+ extend Gem::Deprecate
4
+
5
+ attr_accessor :hostname, :port
6
+
7
+ @instance_mutex = Mutex.new
8
+
9
+ private_class_method :new
10
+
11
+ def initialize(args={})
12
+ @hostname = args.fetch(:hostname) { Cups::CUPS_HTTP_DEFAULT }
13
+ @port = args.fetch(:port) { 631 }
14
+ end
15
+
16
+ # Returns or creates a singleton instance
17
+ # @param args [Hash] hash argument with hostname and port
18
+ # @return [Object] a connection object
19
+ def self.instance(args)
20
+ return @instance if @instance
21
+
22
+ @instance_mutex.synchronize do
23
+ @instance ||= new(args)
24
+ end
25
+
26
+ @instance
27
+ end
28
+
29
+ # Wrapper around {::FFI::Cups::Http#httpConnectEncrypt}
30
+ # @deprecated Use {#httpConnect2} instead
31
+ # @return [Pointer] a http pointer
32
+ def httpConnectEncrypt
33
+ http = FFI::Cups::Http.httpConnectEncrypt(hostname, port, FFI::Cups.cupsEncryption())
34
+ raise "Print server at #{hostname}:#{port} is not available" if http.null?
35
+ return http
36
+ end
37
+ deprecate :httpConnectEncrypt, "This function is deprecated by CUPS, please use httpConnect2 instead", 2025, 12
38
+
39
+ # Wrapper around {::FFI::Cups::Http#httpConnect2}
40
+ # Creates a http connection to a print server
41
+ # @return [Pointer] a http pointer
42
+ def httpConnect2
43
+ http = FFI::Cups::Http.httpConnect2(hostname, port, nil, 0, FFI::Cups.cupsEncryption(), 1, 30000, nil)
44
+ raise "Print server at #{hostname}:#{port} is not available" if http.null?
45
+ return http
46
+ end
47
+
48
+ # Closes the http connection and autoreleases the pointer
49
+ # Wrapper around {::FFI::Cups::Http#httpClose}
50
+ # @param http (Pointer)
51
+ def self.close(http)
52
+ FFI::Cups::Http.httpClose(pointer)
53
+ pointer.autorelease = true
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,95 @@
1
+ module Cups
2
+ # cups.h
3
+ CUPS_FORMAT_JPEG = "image/jpeg"
4
+ CUPS_FORMAT_PDF = "application/pdf"
5
+ CUPS_FORMAT_TEXT = "text/plain"
6
+ CUPS_JOBID_ALL = -1
7
+ CUPS_WHICHJOBS_ALL = -1
8
+ CUPS_WHICHJOBS_ACTIVE = 0
9
+ CUPS_WHICHJOBS_COMPLETED = 1
10
+ CUPS_HTTP_DEFAULT = nil
11
+
12
+ # Options and Values
13
+ CUPS_COPIES = "copies"
14
+ CUPS_COPIES_SUPPORTED = "copies-supported"
15
+
16
+ CUPS_FINISHINGS = "CUPS_FINISHINGS"
17
+ CUPS_FINISHINGS_SUPPORTED = "finishings-supported"
18
+
19
+ CUPS_FINISHINGS_BIND = "7"
20
+ CUPS_FINISHINGS_COVER = "6"
21
+ CUPS_FINISHINGS_FOLD = "10"
22
+ CUPS_FINISHINGS_NONE = "3"
23
+ CUPS_FINISHINGS_PUNCH = "5"
24
+ CUPS_FINISHINGS_STAPLE = "4"
25
+ CUPS_FINISHINGS_TRIM = "11"
26
+
27
+ CUPS_MEDIA = "media"
28
+ CUPS_MEDIA_READY = "media-ready"
29
+ CUPS_MEDIA_SUPPORTED = "media-supported"
30
+
31
+ CUPS_MEDIA_3X5 = "na_index-3x5_3x5in"
32
+ CUPS_MEDIA_4X6 = "na_index-4x6_4x6in"
33
+ CUPS_MEDIA_5X7 = "na_5x7_5x7in"
34
+ CUPS_MEDIA_8X10 = "na_govt-letter_8x10in"
35
+ CUPS_MEDIA_A3 = "iso_a3_297x420mm"
36
+ CUPS_MEDIA_A4 = "iso_a4_210x297mm"
37
+ CUPS_MEDIA_A5 = "iso_a5_148x210mm"
38
+ CUPS_MEDIA_A6 = "iso_a6_105x148mm"
39
+ CUPS_MEDIA_ENV10 = "na_number-10_4.125x9.5in"
40
+ CUPS_MEDIA_ENVDL = "iso_dl_110x220mm"
41
+ CUPS_MEDIA_LEGAL = "na_legal_8.5x14in"
42
+ CUPS_MEDIA_LETTER = "na_letter_8.5x11in"
43
+ CUPS_MEDIA_PHOTO_L = "oe_photo-l_3.5x5in"
44
+ CUPS_MEDIA_SUPERBA3 = "na_super-b_13x19in"
45
+ CUPS_MEDIA_TABLOID = "na_ledger_11x17in"
46
+
47
+ CUPS_MEDIA_SOURCE = "media-source"
48
+ CUPS_MEDIA_SOURCE_SUPPORTED = "media-source-supported"
49
+
50
+ CUPS_MEDIA_SOURCE_AUTO = "auto"
51
+ CUPS_MEDIA_SOURCE_MANUAL = "manual"
52
+
53
+ CUPS_MEDIA_TYPE = "media-type"
54
+ CUPS_MEDIA_TYPE_SUPPORTED = "media-type-supported"
55
+
56
+ CUPS_MEDIA_TYPE_AUTO = "auto"
57
+ CUPS_MEDIA_TYPE_ENVELOPE = "envelope"
58
+ CUPS_MEDIA_TYPE_LABELS = "labels"
59
+ CUPS_MEDIA_TYPE_LETTERHEAD = "stationery-letterhead"
60
+ CUPS_MEDIA_TYPE_PHOTO = "photographic"
61
+ CUPS_MEDIA_TYPE_PHOTO_GLOSSY = "photographic-glossy"
62
+ CUPS_MEDIA_TYPE_PHOTO_MATTE = "photographic-matte"
63
+ CUPS_MEDIA_TYPE_PLAIN = "stationery"
64
+ CUPS_MEDIA_TYPE_TRANSPARENCY = "transparency"
65
+
66
+ CUPS_NUMBER_UP = "number-up"
67
+ CUPS_NUMBER_UP_SUPPORTED = "number-up-supported"
68
+
69
+ CUPS_ORIENTATION = "orientation-requested"
70
+ CUPS_ORIENTATION_SUPPORTED = "orientation-requested-supported"
71
+
72
+ CUPS_ORIENTATION_PORTRAIT = "3"
73
+ CUPS_ORIENTATION_LANDSCAPE = "4"
74
+
75
+ CUPS_PRINT_COLOR_MODE = "print-color-mode"
76
+ CUPS_PRINT_COLOR_MODE_SUPPORTED = "print-color-mode-supported"
77
+
78
+ CUPS_PRINT_COLOR_MODE_AUTO = "auto"
79
+ CUPS_PRINT_COLOR_MODE_MONOCHROME = "monochrome"
80
+ CUPS_PRINT_COLOR_MODE_COLOR = "color"
81
+
82
+ CUPS_PRINT_QUALITY = "print-quality"
83
+ CUPS_PRINT_QUALITY_SUPPORTED = "print-quality-supported"
84
+
85
+ CUPS_PRINT_QUALITY_DRAFT = "3"
86
+ CUPS_PRINT_QUALITY_NORMAL = "4"
87
+ CUPS_PRINT_QUALITY_HIGH = "5"
88
+
89
+ CUPS_SIDES = "sides"
90
+ CUPS_SIDES_SUPPORTED = "sides-supported"
91
+
92
+ CUPS_SIDES_ONE_SIDED = "one-sided"
93
+ CUPS_SIDES_TWO_SIDED_PORTRAIT = "two-sided-long-edge"
94
+ CUPS_SIDES_TWO_SIDED_LANDSCAPE = "two-sided-short-edge"
95
+ end
@@ -0,0 +1,50 @@
1
+ module Cups::Enum
2
+ module HTTP
3
+ extend FFI::Library
4
+
5
+ Status = enum [
6
+ :http_error, -1,
7
+ :http_continue, 100,
8
+ :http_switching_protocols,
9
+ :http_ok, 200,
10
+ :http_created,
11
+ :http_accepted,
12
+ :http_not_authoritative,
13
+ :http_no_content,
14
+ :http_reset_content,
15
+ :http_partial_content,
16
+ :http_multiple_choices, 300,
17
+ :http_moved_permanently,
18
+ :http_moved_temporarily,
19
+ :http_see_other,
20
+ :http_not_modified,
21
+ :http_use_proxy,
22
+ :http_bad_request, 400,
23
+ :http_unauthorized,
24
+ :http_payment_required,
25
+ :http_forbidden,
26
+ :http_not_found,
27
+ :http_method_not_allowed,
28
+ :http_not_acceptable,
29
+ :http_proxy_authentication,
30
+ :http_request_timeout,
31
+ :http_conflict,
32
+ :http_gone,
33
+ :http_length_required,
34
+ :http_precondition,
35
+ :http_request_too_large,
36
+ :http_uri_too_long,
37
+ :http_unsupported_mediatype,
38
+ :http_requested_range,
39
+ :http_expectation_failed,
40
+ :http_upgrade_required, 426,
41
+ :http_server_error, 500,
42
+ :http_not_implemented,
43
+ :http_bad_gateway,
44
+ :http_service_unavailable,
45
+ :http_gateway_timeout,
46
+ :http_not_supported,
47
+ :http_authorization_canceled, 1000
48
+ ]
49
+ end
50
+ end
@@ -0,0 +1,15 @@
1
+ module Cups::Enum
2
+ module IPP
3
+ extend FFI::Library
4
+
5
+ JobState = enum [
6
+ :pending, 3,
7
+ :held,
8
+ :processing,
9
+ :stopped,
10
+ :canceled,
11
+ :aborted,
12
+ :completed
13
+ ]
14
+ end
15
+ end
@@ -0,0 +1,53 @@
1
+ module Cups::Enum
2
+ module IPP
3
+ extend FFI::Library
4
+
5
+ Status = enum [
6
+ :ipp_ok, 0,
7
+ :ipp_ok_subst,
8
+ :ipp_ok_conflict,
9
+ :ipp_ok_ignored_subscriptions,
10
+ :ipp_ok_ignored_notifications,
11
+ :ipp_ok_too_many_events,
12
+ :ipp_ok_but_cancel_subscription,
13
+ :ipp_ok_events_complete,
14
+ :ipp_redirection_other_site, 512,
15
+ :cups_see_other, 640,
16
+ :ipp_bad_request, 1024,
17
+ :ipp_forbidden,
18
+ :ipp_not_authenticated,
19
+ :ipp_not_authorized,
20
+ :ipp_not_possible,
21
+ :ipp_timeout,
22
+ :ipp_not_found,
23
+ :ipp_gone,
24
+ :ipp_request_entity,
25
+ :ipp_request_value,
26
+ :ipp_document_format,
27
+ :ipp_attributes,
28
+ :ipp_uri_scheme,
29
+ :ipp_charset,
30
+ :ipp_conflict,
31
+ :ipp_compression_not_supported,
32
+ :ipp_compression_error,
33
+ :ipp_document_format_error,
34
+ :ipp_document_access_error,
35
+ :ipp_attributes_not_settable,
36
+ :ipp_ignored_all_subscriptions,
37
+ :ipp_too_many_subscriptions,
38
+ :ipp_ignored_all_notifications,
39
+ :ipp_print_support_file_not_found,
40
+ :ipp_internal_error, 1280,
41
+ :ipp_operation_not_supported,
42
+ :ipp_service_unavailable,
43
+ :ipp_version_not_supported,
44
+ :ipp_device_error,
45
+ :ipp_temporary_error,
46
+ :ipp_not_accepting,
47
+ :ipp_printer_busy,
48
+ :ipp_error_job_canceled,
49
+ :ipp_multiple_jobs_not_supported,
50
+ :ipp_printer_is_deactivated
51
+ ]
52
+ end
53
+ end
@@ -0,0 +1,17 @@
1
+ module FFI::Cups
2
+ module Array
3
+ extend FFI::Library
4
+
5
+ ffi_lib('cups')
6
+
7
+ # @overload cupsArrayFirst(pointer)
8
+ # @param pointer [Pointer] to _cups_array_s struct
9
+ # @return [Pointer] void pointer to first element
10
+ attach_function 'cupsArrayFirst', [:pointer], :pointer, blocking: true
11
+
12
+ # @overload cupsArrayNext(pointer)
13
+ # @param pointer [Pointer] to _cups_array_s struct
14
+ # @return [Pointer] void pointer to first element
15
+ attach_function 'cupsArrayNext', [:pointer], :pointer, blocking: true
16
+ end
17
+ end
@@ -0,0 +1,176 @@
1
+ # cups.h
2
+ # {https://www.cups.org/doc/cupspm.html Programming Manual}
3
+ # {https://github.com/apple/cups/blob/master/cups/cups.h cups.h source}
4
+
5
+ module FFI::Cups
6
+ extend FFI::Library
7
+
8
+ ffi_lib('cups')
9
+
10
+ # Get the current encryption settings.
11
+ # @return [Integer] encryption settings
12
+ # {https://www.cups.org/doc/cupspm.html#cupsEncryption}
13
+ attach_function 'cupsEncryption', [], :int, blocking: true
14
+
15
+ # Check that the option and value are supported by the destination.
16
+ # @overload cupsCheckDestSupported(pointer, pointer, pointer, string, string)
17
+ # @param http [Pointer]
18
+ # @param destination [Pointer]
19
+ # @param dinfo [Pointer]
20
+ # @param option [String]
21
+ # @param value [String]
22
+ # @return [Integer] 1 if supported, 0 otherwise
23
+ # {https://www.cups.org/doc/cupspm.html#cupsCheckDestSupported}
24
+ attach_function 'cupsCheckDestSupported', [:pointer, :pointer, :pointer, :string, :string], :int, blocking: true
25
+
26
+ # Get the named destination from the list.
27
+ # @overload cupsGetDest(string, string, int, pointer)
28
+ # @param name [String]
29
+ # @param instance [String] instance name or NULL
30
+ # @param num_dests [Integer] number of destinations
31
+ # @param destinations [Pointer]
32
+ # @return [Pointer, NULL] destination or NULL
33
+ # {https://www.cups.org/doc/cupspm.html#cupsGetDest}
34
+ attach_function 'cupsGetDest', [:string, :string, :int, :pointer], :pointer, blocking: true
35
+
36
+ # Get the list of destinations from the specified server.
37
+ # @overload cupsGetDests2(pointer, pointer)
38
+ # @param pointer [Pointer] to http connection for server or CUPS_HTTP_DEFAULT
39
+ # @param pointer [Pointer] to destinations
40
+ # @return [Integer] number of destinations
41
+ # {https://www.cups.org/doc/cupspm.html#cupsGetDests2}
42
+ attach_function 'cupsGetDests2', [:pointer, :pointer], :int, blocking: true
43
+
44
+ # Free the memory used by the list of destinations.
45
+ # @overload cupsFreeDests(int, pointer)
46
+ # @param number [Integer] of destinations
47
+ # @param pointer [Pointer] to destinations
48
+ # {https://www.cups.org/doc/cupspm.html#cupsFreeDests}
49
+ attach_function 'cupsFreeDests', [ :int, :pointer ], :void, blocking: true
50
+
51
+ # Prints a File
52
+ # @overload cupsPrintFile(string, string, string, int, pointer)
53
+ # @param name [String] of the printer
54
+ # @param name [String] of the file
55
+ # @param title [String] of the job
56
+ # @param number [Integer] of options
57
+ # @param pointer [Pointer] to a CupsOptionS struct
58
+ # @return [Integer] job number or 0 on error
59
+ attach_function 'cupsPrintFile', [ :string, :string, :string, :int, :pointer ], :int, blocking: true
60
+
61
+ # Prints a file from a specific connection
62
+ # @overload cupsPrintFile2(pointer, string, string, string, int, pointer)
63
+ # @param pointer [Pointer] to an http connection
64
+ # @param name [String] of the printer
65
+ # @param name [String] of the file
66
+ # @param title [String] of the title
67
+ # @param number [Integer] of options
68
+ # @param pointer [Pointer] to a CupsOptionS struct
69
+ # @return [Integer] number of the job or 0 on error
70
+ attach_function 'cupsPrintFile2', [ :pointer, :string, :string, :string, :int, :pointer ], :int, blocking: true
71
+
72
+ # Returns the last error in string format
73
+ # @return [String] error
74
+ attach_function 'cupsLastErrorString', [], :string, blocking: true
75
+
76
+ # Cancel a job on a destination.
77
+ # @overload cupsCancelDestJob(string, int)
78
+ # @param connection [Pointer] to a http server
79
+ # @param pointer [Pointer] to a destination
80
+ # @param id [Integer] of the job
81
+ # @return [Integer] IPP status
82
+ # {https://www.cups.org/doc/cupspm.html#cupsCancelDestJob}
83
+ attach_function 'cupsCancelDestJob', [:string, :int], :void, blocking: true
84
+
85
+ # Cancel a job on a destination name
86
+ # @overload cupsCancelJob(string, int)
87
+ # @param name [String] of the destination
88
+ # @param id [Ingeger] of the job
89
+ attach_function 'cupsCancelJob', [:string, :int], :void, blocking: true
90
+
91
+ # Cancel a job on a destination
92
+ # @overload cupsCancelJob2(pointer, string, int)
93
+ # @param pointer [Pointer] of the http connection
94
+ # @param name [String] of the destination
95
+ # @param id [Ingeger] of the job
96
+ attach_function 'cupsCancelJob2', [:pointer, :string, :int], :void, blocking: true
97
+
98
+ # Get the jobs from a connection
99
+ # @overload cupsGetJobs2(pointer, pointer, string, int, int)
100
+ # @param pointer [Pointer] to a http connection object
101
+ # @param pointer [Pointer] to Cups::Struct::Job
102
+ # @param name [String] of the printer
103
+ # @param number [Integer] of type of job (0 == all users, 1 == mine)
104
+ # @param which [Integer] jobs (CUPS_WHICHJOBS_ALL, CUPS_WHICHJOBS_ACTIVE, or CUPS_WHICHJOBS_COMPLETED)
105
+ # @return [Integer] number of jobs
106
+ # {https://www.cups.org/doc/cupspm.html#cupsGetJobs2}
107
+ attach_function 'cupsGetJobs2', [:pointer, :pointer, :string, :int, :int], :int, blocking: true
108
+
109
+ # Get the jobs
110
+ # @overload cupsGetJobs(pointer, string, int, int)
111
+ # @param pointer [Pointer] to Cups::Struct::Job to populate
112
+ # @param name [String] of the printer
113
+ # @param number [Integer] of type of job (0 == all users, 1 == mine)
114
+ # @param which [Integer] jobs (CUPS_WHICHJOBS_ALL, CUPS_WHICHJOBS_ACTIVE, or CUPS_WHICHJOBS_COMPLETED)
115
+ # @return [Integer] number of jobs
116
+ attach_function 'cupsGetJobs', [:pointer, :string, :int, :int], :int, blocking: true
117
+
118
+ # Free memory used by job data.
119
+ # @overload cupsFreeJobs(int, pointer)
120
+ # @param number [Integer] of jobs
121
+ # @param pointer [Pointer] to the first Cups::Struct::Job to free
122
+ # {https://www.cups.org/doc/cupspm.html#cupsFreeJobs}
123
+ attach_function 'cupsFreeJobs', [:int, :pointer ], :void, blocking: true
124
+
125
+ # Create a job on a destination.
126
+ # @overload cupsCreateJob(pointer, string, string, int, pointer)
127
+ # @param pointer [Pointer] to http connection to server or CUPS_HTTP_DEFAULT
128
+ # @param name [String] of the printer
129
+ # @param title [String] of the job
130
+ # @param number [Integer] of options
131
+ # @param pointer [Pointer] to a Cups::Struct::Option object
132
+ # @return [Integer] job number or 0 on error
133
+ attach_function 'cupsCreateJob', [:pointer, :string, :string, :int, :pointer], :int, blocking: true
134
+
135
+ # Start a document
136
+ # @overload cupsStartDocument(pointer, string, int, string, string, int)
137
+ # @param pointer [Pointer] to http connection to server or CUPS_HTTP_DEFAULT
138
+ # @param name [String] of the printer
139
+ # @param id [Integer] of the job
140
+ # @param name [String] of the document
141
+ # @param mime_type [String] format of the document
142
+ # @param number [Integer] of the last document (1 for last document in job, 0 otherwise)
143
+ # @return [Cups::Enum::HTTP::Status] HttpStatus
144
+ # {https://www.cups.org/doc/cupspm.html#submitting-a-print-job}
145
+ attach_function 'cupsStartDocument', [:pointer, :string, :int, :string, :string, :int], Cups::Enum::HTTP::Status, blocking: true
146
+
147
+ # @overload cupsWriteRequestData(pointer, pointer, bytesize)
148
+ # @param pointer [Pointer] to http connection to server or CUPS_HTTP_DEFAULT
149
+ # @param data [Pointer] in a character string pointer
150
+ # @param length [Byte] of data
151
+ # @return [Cups::Enum::HTTP::Status] HttpStatus
152
+ attach_function 'cupsWriteRequestData', [:pointer, :pointer, :size_t], Cups::Enum::HTTP::Status, blocking: true
153
+
154
+ # @overload cupsFinishDocument(pointer, string)
155
+ # @param pointer [Pointer] to http connection to server or CUPS_HTTP_DEFAULT
156
+ # @param name [String] of the printer
157
+ # @return [Cups::Enum::IPP::Status] IppStatus
158
+ attach_function 'cupsFinishDocument', [:pointer, :string], Cups::Enum::IPP::Status, blocking: true
159
+
160
+ # Add an option to an option array.
161
+ # @overload cupsAddOption(string, string, int, pointer)
162
+ # @param name [String] of option
163
+ # @param value [String] of option
164
+ # @param number [Integer] of options
165
+ # @param pointer [Pointer] to options
166
+ # @return [Integer] number of options
167
+ # {https://www.cups.org/doc/cupspm.html#cupsAddOption}
168
+ attach_function 'cupsAddOption', [:string, :string, :int, :pointer], :int, blocking: true
169
+
170
+ # Free all memory used by options.
171
+ # @overload cupsFreeOptions(int, pointer)
172
+ # @param number [Integer] of options
173
+ # @param pointer [Pointer] to options
174
+ # {https://www.cups.org/doc/cupspm.html#cupsFreeOptions}
175
+ attach_function 'cupsFreeOptions', [:int, :pointer], :void, blocking: true
176
+ end
@@ -0,0 +1,40 @@
1
+ module FFI::Cups
2
+ module Http
3
+ extend FFI::Library
4
+
5
+ ffi_lib('cups')
6
+
7
+ # @overload httpConnectEncrypt(string, int, int)
8
+ # @param hostname [String]
9
+ # @param port [int]
10
+ # @param encryption_settings [int]
11
+ # @return [Pointer] pointer to a http_t object
12
+ # @deprecated Please use httpConnect2 instead
13
+ attach_function 'httpConnectEncrypt', [ :string, :int, :int], :pointer, blocking: true
14
+
15
+ # @overload httpClose(pointer)
16
+ # @param pointer [Pointer] to a http_t object
17
+ attach_function 'httpClose', [ :pointer ], :void, blocking: true
18
+
19
+ # Get a list of addresses for a hostname.
20
+ # @overload httpAddrGetList(string, int, string)
21
+ # @param hostname [String]
22
+ # @param family [Integer]
23
+ # @param port [String]
24
+ # @return [Pointer] List of addresses or NULL
25
+ attach_function 'httpAddrGetList', [:string, :int, :string], :pointer, blocking: true
26
+
27
+ # Connect to a HTTP server.
28
+ # @overload httpConnect2(string, int, pointer, int, int, int, int, pointer)
29
+ # @param hostname [String]
30
+ # @param port [Integer]
31
+ # @param addrlist [Pointer] can be NULL
32
+ # @param family [Integer] (AF_UNSPEC=0)
33
+ # @param encryption_settings [Integer] from FFI::Cups.cupsEncryption()
34
+ # @param blocking [Integer]
35
+ # @param msec [Integer] use 5000 or less
36
+ # @param cancel[Pointer] integer pointer, can be NULL
37
+ # @return [Pointer] http_t object
38
+ attach_function 'httpConnect2', [:string, :int, :pointer, :int, :int, :int, :int, :pointer], :pointer, blocking: true
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ module Cups
2
+ class Job
3
+ attr_accessor :id, :title, :printer
4
+
5
+ def initialize(id, title, printer=nil)
6
+ @id = id
7
+ @title = title
8
+ @printer = printer
9
+ end
10
+
11
+ end
12
+ end
13
+
@@ -0,0 +1,105 @@
1
+ module Cups
2
+ class Printer
3
+ attr_reader :name, :options
4
+
5
+ # @param name [String] printer's name
6
+ # @param options [Hash] printer's options
7
+ def initialize(name, options={})
8
+ @name = name
9
+ @options = options
10
+ end
11
+
12
+ # Returns the printer state
13
+ # @return [Symbol] printer state from options
14
+ def state
15
+ case options['printer-state']
16
+ when '3' then :idle
17
+ when '4' then :printing
18
+ when '5' then :stopped
19
+ else :unkown
20
+ end
21
+ end
22
+
23
+ # Returns the reason for the printer state
24
+ # @return [Array] array of reasons in string format
25
+ def state_reasons
26
+ options['printer-state-reasons'].split(/,/)
27
+ end
28
+
29
+ # Get all destinations (printer devices)
30
+ # @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
31
+ def self.get_destinations(connection=nil)
32
+ pointer = FFI::MemoryPointer.new :pointer
33
+ destinations = cupsGetDests2(connection, pointer)
34
+ printers = []
35
+ destinations.each do |k, d|
36
+ destination_opt = cupsOptions(d)
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)
44
+ printers.push(printer)
45
+ end
46
+ cupsFreeDests(destinations.keys.count, pointer)
47
+ return printers
48
+ end
49
+
50
+ # Get a destination by name
51
+ # @param name [String] name of the printer
52
+ # @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
53
+ def self.get_destination(name, connection=nil)
54
+ printers = get_destinations(connection)
55
+ printers.each do |p|
56
+ return p if p.name == name
57
+ end
58
+ raise "Destination with name: #{name} not found!"
59
+ end
60
+
61
+ private
62
+ # Wrapper around {::FFI::Cups#cupsGetDests2}
63
+ # @param connection [Pointer] http pointer from {Cups::Connection#httpConnect2}
64
+ # @param pointer [Pointer] pointer to the destinations
65
+ # @return [Hash] hashmap of destination structs
66
+ def self.cupsGetDests2(connection=nil, pointer)
67
+ count_destinations = FFI::Cups.cupsGetDests2(connection, pointer)
68
+ struct_size = Cups::Struct::Destination.size
69
+ destinations = {}
70
+ count_destinations.times do |index|
71
+ destination = Cups::Struct::Destination.new(pointer.get_pointer(0) + (struct_size * index))
72
+ destinations[index] = destination
73
+ end
74
+ return destinations
75
+ end
76
+
77
+ # Returns a destination's options
78
+ # @param destination [Object] {Cups::Struct::Destination} object
79
+ # @return [Hash] hashmap of destination' options as {Cups::Struct::Option}
80
+ def self.cupsOptions(destination)
81
+ struct_size = Cups::Struct::Option.size
82
+ options = {}
83
+
84
+ destination[:num_options].times do |index|
85
+ option = Cups::Struct::Option.new(destination[:options] + (struct_size * index))
86
+ options[index] = option
87
+ end
88
+ return options
89
+ end
90
+
91
+ # Wrapper around {::FFI::Cups#cupsFreeDests}
92
+ # @param number_destinations [Integer]
93
+ # @param pointer [Pointer] pointer to the destinations
94
+ def self.cupsFreeDests(number_destinations, pointer)
95
+ FFI::Cups.cupsFreeDests(number_destinations, pointer.get_pointer(0))
96
+ end
97
+
98
+ # Wrapper around {::FFI::Cups#cupsFreeOptions}
99
+ # @param number_options [Integer]
100
+ # @param pointer [Pointer] pointer to the options
101
+ def self.cupsFreeOptions(number_options, pointer)
102
+ FFI::Cups.cupsFreeOptions(number_options, pointer.get_pointer(0))
103
+ end
104
+ end
105
+ 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,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,3 @@
1
+ module Cups
2
+ VERSION = "0.1.1"
3
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ffi-cups
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Hugo Marquez
8
+ - Nathan Ehresman
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2021-04-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 1.15.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 1.15.0
28
+ description: Simple wrapper around libcups to give CUPS printing capabilities to Ruby
29
+ apps.
30
+ email:
31
+ - hugomarquez.dev@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - Gemfile
38
+ - Gemfile.lock
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - bin/console
43
+ - bin/setup
44
+ - ffi-cups.gemspec
45
+ - lib/ffi-cups.rb
46
+ - lib/ffi-cups/connection.rb
47
+ - lib/ffi-cups/constants.rb
48
+ - lib/ffi-cups/enum/http/status.rb
49
+ - lib/ffi-cups/enum/ipp/job_state.rb
50
+ - lib/ffi-cups/enum/ipp/status.rb
51
+ - lib/ffi-cups/ffi/array.rb
52
+ - lib/ffi-cups/ffi/cups.rb
53
+ - lib/ffi-cups/ffi/http.rb
54
+ - lib/ffi-cups/job.rb
55
+ - lib/ffi-cups/printer.rb
56
+ - lib/ffi-cups/struct/destination.rb
57
+ - lib/ffi-cups/struct/job.rb
58
+ - lib/ffi-cups/struct/option.rb
59
+ - lib/ffi-cups/version.rb
60
+ homepage: https://github.com/hugomarquez/ffi-cups
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.3.0
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.1.2
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: FFI wrapper around libcups
83
+ test_files: []