mini_magick 3.7.0 → 4.11.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,81 @@
1
+ require "timeout"
2
+ require "benchmark"
3
+
4
+ module MiniMagick
5
+ ##
6
+ # Sends commands to the shell (more precisely, it sends commands directly to
7
+ # the operating system).
8
+ #
9
+ # @private
10
+ #
11
+ class Shell
12
+
13
+ def run(command, options = {})
14
+ stdout, stderr, status = execute(command, stdin: options[:stdin])
15
+
16
+ if status != 0 && options.fetch(:whiny, MiniMagick.whiny)
17
+ fail MiniMagick::Error, "`#{command.join(" ")}` failed with error:\n#{stderr}"
18
+ end
19
+
20
+ $stderr.print(stderr) unless options[:stderr] == false
21
+
22
+ [stdout, stderr, status]
23
+ end
24
+
25
+ def execute(command, options = {})
26
+ stdout, stderr, status =
27
+ log(command.join(" ")) do
28
+ send("execute_#{MiniMagick.shell_api.gsub("-", "_")}", command, options)
29
+ end
30
+
31
+ [stdout, stderr, status.exitstatus]
32
+ rescue Errno::ENOENT, IOError
33
+ ["", "executable not found: \"#{command.first}\"", 127]
34
+ end
35
+
36
+ private
37
+
38
+ def execute_open3(command, options = {})
39
+ require "open3"
40
+
41
+ # We would ideally use Open3.capture3, but it wouldn't allow us to
42
+ # terminate the command after timing out.
43
+ Open3.popen3(*command) do |in_w, out_r, err_r, thread|
44
+ [in_w, out_r, err_r].each(&:binmode)
45
+ stdout_reader = Thread.new { out_r.read }
46
+ stderr_reader = Thread.new { err_r.read }
47
+ begin
48
+ in_w.write options[:stdin].to_s
49
+ rescue Errno::EPIPE
50
+ end
51
+ in_w.close
52
+
53
+ begin
54
+ Timeout.timeout(MiniMagick.timeout) { thread.join }
55
+ rescue Timeout::Error
56
+ Process.kill("TERM", thread.pid) rescue nil
57
+ Process.waitpid(thread.pid) rescue nil
58
+ raise Timeout::Error, "MiniMagick command timed out: #{command}"
59
+ end
60
+
61
+ [stdout_reader.value, stderr_reader.value, thread.value]
62
+ end
63
+ end
64
+
65
+ def execute_posix_spawn(command, options = {})
66
+ require "posix-spawn"
67
+ child = POSIX::Spawn::Child.new(*command, input: options[:stdin].to_s, timeout: MiniMagick.timeout)
68
+ [child.out, child.err, child.status]
69
+ rescue POSIX::Spawn::TimeoutExceeded
70
+ raise Timeout::Error, "MiniMagick command timed out: #{command}"
71
+ end
72
+
73
+ def log(command, &block)
74
+ value = nil
75
+ duration = Benchmark.realtime { value = block.call }
76
+ MiniMagick.logger.debug "[%.2fs] %s" % [duration, command]
77
+ value
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/animate.php
5
+ #
6
+ class Animate < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("animate", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/compare.php
5
+ #
6
+ class Compare < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("compare", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/composite.php
5
+ #
6
+ class Composite < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("composite", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/conjure.php
5
+ #
6
+ class Conjure < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("conjure", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/convert.php
5
+ #
6
+ class Convert < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("convert", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/display.php
5
+ #
6
+ class Display < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("display", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/identify.php
5
+ #
6
+ class Identify < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("identify", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/import.php
5
+ #
6
+ class Import < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("import", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/command-line-processing.php
5
+ #
6
+ class Magick < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("magick", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/mogrify.php
5
+ #
6
+ class Mogrify < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("mogrify", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ require "mini_magick/tool/mogrify"
2
+
3
+ module MiniMagick
4
+ class Tool
5
+ ##
6
+ # @see http://www.imagemagick.org/script/mogrify.php
7
+ #
8
+ class MogrifyRestricted < Mogrify
9
+ def format(*args)
10
+ fail NoMethodError,
11
+ "you must call #format on a MiniMagick::Image directly"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/montage.php
5
+ #
6
+ class Montage < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("montage", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MiniMagick
2
+ class Tool
3
+ ##
4
+ # @see http://www.imagemagick.org/script/stream.php
5
+ #
6
+ class Stream < MiniMagick::Tool
7
+
8
+ def initialize(*args)
9
+ super("stream", *args)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,307 @@
1
+ require "mini_magick/shell"
2
+
3
+ module MiniMagick
4
+ ##
5
+ # Abstract class that wraps command-line tools. It shouldn't be used directly,
6
+ # but through one of its subclasses (e.g. {MiniMagick::Tool::Mogrify}). Use
7
+ # this class if you want to be closer to the metal and execute ImageMagick
8
+ # commands directly, but still with a nice Ruby interface.
9
+ #
10
+ # @example
11
+ # MiniMagick::Tool::Mogrify.new do |builder|
12
+ # builder.resize "500x500"
13
+ # builder << "path/to/image.jpg"
14
+ # end
15
+ #
16
+ class Tool
17
+
18
+ CREATION_OPERATORS = %w[xc canvas logo rose gradient radial-gradient plasma
19
+ pattern text pango]
20
+
21
+ ##
22
+ # Aside from classic instantiation, it also accepts a block, and then
23
+ # executes the command in the end.
24
+ #
25
+ # @example
26
+ # version = MiniMagick::Tool::Identify.new { |b| b.version }
27
+ # puts version
28
+ #
29
+ # @return [MiniMagick::Tool, String] If no block is given, returns an
30
+ # instance of the tool, if block is given, returns the output of the
31
+ # command.
32
+ #
33
+ def self.new(*args)
34
+ instance = super(*args)
35
+
36
+ if block_given?
37
+ yield instance
38
+ instance.call
39
+ else
40
+ instance
41
+ end
42
+ end
43
+
44
+ # @private
45
+ attr_reader :name, :args
46
+
47
+ # @param name [String]
48
+ # @param options [Hash]
49
+ # @option options [Boolean] :whiny Whether to raise errors on non-zero
50
+ # exit codes.
51
+ # @example
52
+ # MiniMagick::Tool::Identify.new(whiny: false) do |identify|
53
+ # identify.help # returns exit status 1, which would otherwise throw an error
54
+ # end
55
+ def initialize(name, options = {})
56
+ warn "MiniMagick::Tool.new(false) is deprecated and will be removed in MiniMagick 5, use MiniMagick::Tool.new(whiny: false) instead." if !options.is_a?(Hash)
57
+
58
+ @name = name
59
+ @args = []
60
+ @whiny = options.is_a?(Hash) ? options.fetch(:whiny, MiniMagick.whiny) : options
61
+ end
62
+
63
+ ##
64
+ # Executes the command that has been built up.
65
+ #
66
+ # @example
67
+ # mogrify = MiniMagick::Tool::Mogrify.new
68
+ # mogrify.resize("500x500")
69
+ # mogrify << "path/to/image.jpg"
70
+ # mogrify.call # executes `mogrify -resize 500x500 path/to/image.jpg`
71
+ #
72
+ # @example
73
+ # mogrify = MiniMagick::Tool::Mogrify.new
74
+ # # build the command
75
+ # mogrify.call do |stdout, stderr, status|
76
+ # # ...
77
+ # end
78
+ #
79
+ # @yield [Array] Optionally yields stdout, stderr, and exit status
80
+ #
81
+ # @return [String] Returns the output of the command
82
+ #
83
+ def call(*args)
84
+ options = args[-1].is_a?(Hash) ? args.pop : {}
85
+ warn "Passing whiny to MiniMagick::Tool#call is deprecated and will be removed in MiniMagick 5, use MiniMagick::Tool.new(whiny: false) instead." if args.any?
86
+ whiny = args.fetch(0, @whiny)
87
+
88
+ options[:whiny] = whiny
89
+ options[:stderr] = false if block_given?
90
+
91
+ shell = MiniMagick::Shell.new
92
+ stdout, stderr, status = shell.run(command, options)
93
+ yield stdout, stderr, status if block_given?
94
+
95
+ stdout.chomp("\n")
96
+ end
97
+
98
+ ##
99
+ # The currently built-up command.
100
+ #
101
+ # @return [Array<String>]
102
+ #
103
+ # @example
104
+ # mogrify = MiniMagick::Tool::Mogrify.new
105
+ # mogrify.resize "500x500"
106
+ # mogrify.contrast
107
+ # mogrify.command #=> ["mogrify", "-resize", "500x500", "-contrast"]
108
+ #
109
+ def command
110
+ [*executable, *args]
111
+ end
112
+
113
+ ##
114
+ # The executable used for this tool. Respects
115
+ # {MiniMagick::Configuration#cli}, {MiniMagick::Configuration#cli_path},
116
+ # and {MiniMagick::Configuration#cli_prefix}.
117
+ #
118
+ # @return [Array<String>]
119
+ #
120
+ # @example
121
+ # MiniMagick.configure { |config| config.cli = :graphicsmagick }
122
+ # identify = MiniMagick::Tool::Identify.new
123
+ # identify.executable #=> ["gm", "identify"]
124
+ #
125
+ # @example
126
+ # MiniMagick.configure do |config|
127
+ # config.cli = :graphicsmagick
128
+ # config.cli_prefix = ['firejail', '--force']
129
+ # end
130
+ # identify = MiniMagick::Tool::Identify.new
131
+ # identify.executable #=> ["firejail", "--force", "gm", "identify"]
132
+ #
133
+ def executable
134
+ exe = [name]
135
+ exe.unshift "magick" if MiniMagick.imagemagick7? && name != "magick"
136
+ exe.unshift "gm" if MiniMagick.graphicsmagick?
137
+ exe.unshift File.join(MiniMagick.cli_path, exe.shift) if MiniMagick.cli_path
138
+ Array(MiniMagick.cli_prefix).reverse_each { |p| exe.unshift p } if MiniMagick.cli_prefix
139
+ exe
140
+ end
141
+
142
+ ##
143
+ # Appends raw options, useful for appending image paths.
144
+ #
145
+ # @return [self]
146
+ #
147
+ def <<(arg)
148
+ args << arg.to_s
149
+ self
150
+ end
151
+
152
+ ##
153
+ # Merges a list of raw options.
154
+ #
155
+ # @return [self]
156
+ #
157
+ def merge!(new_args)
158
+ new_args.each { |arg| self << arg }
159
+ self
160
+ end
161
+
162
+ ##
163
+ # Changes the last operator to its "plus" form.
164
+ #
165
+ # @example
166
+ # MiniMagick::Tool::Mogrify.new do |mogrify|
167
+ # mogrify.antialias.+
168
+ # mogrify.distort.+("Perspective", "0,0,4,5 89,0,45,46")
169
+ # end
170
+ # # executes `mogrify +antialias +distort Perspective '0,0,4,5 89,0,45,46'`
171
+ #
172
+ # @return [self]
173
+ #
174
+ def +(*values)
175
+ args[-1] = args[-1].sub(/^-/, '+')
176
+ self.merge!(values)
177
+ self
178
+ end
179
+
180
+ ##
181
+ # Create an ImageMagick stack in the command (surround.
182
+ #
183
+ # @example
184
+ # MiniMagick::Tool::Convert.new do |convert|
185
+ # convert << "wand.gif"
186
+ # convert.stack do |stack|
187
+ # stack << "wand.gif"
188
+ # stack.rotate(30)
189
+ # end
190
+ # convert.append.+
191
+ # convert << "images.gif"
192
+ # end
193
+ # # executes `convert wand.gif \( wizard.gif -rotate 30 \) +append images.gif`
194
+ #
195
+ def stack(*args)
196
+ self << "("
197
+ args.each do |value|
198
+ case value
199
+ when Hash then value.each { |key, value| send(key, *value) }
200
+ when String then self << value
201
+ end
202
+ end
203
+ yield self if block_given?
204
+ self << ")"
205
+ end
206
+
207
+ ##
208
+ # Adds ImageMagick's pseudo-filename `-` for standard input.
209
+ #
210
+ # @example
211
+ # identify = MiniMagick::Tool::Identify.new
212
+ # identify.stdin
213
+ # identify.call(stdin: image_content)
214
+ # # executes `identify -` with the given standard input
215
+ #
216
+ def stdin
217
+ self << "-"
218
+ end
219
+
220
+ ##
221
+ # Adds ImageMagick's pseudo-filename `-` for standard output.
222
+ #
223
+ # @example
224
+ # content = MiniMagick::Tool::Convert.new do |convert|
225
+ # convert << "input.jpg"
226
+ # convert.auto_orient
227
+ # convert.stdout
228
+ # end
229
+ # # executes `convert input.jpg -auto-orient -` which returns file contents
230
+ #
231
+ def stdout
232
+ self << "-"
233
+ end
234
+
235
+ ##
236
+ # Define creator operator methods
237
+ #
238
+ # @example
239
+ # mogrify = MiniMagick::Tool.new("mogrify")
240
+ # mogrify.canvas("khaki")
241
+ # mogrify.command.join(" ") #=> "mogrify canvas:khaki"
242
+ #
243
+ CREATION_OPERATORS.each do |operator|
244
+ define_method(operator.gsub('-', '_')) do |value = nil|
245
+ self << "#{operator}:#{value}"
246
+ self
247
+ end
248
+ end
249
+
250
+ ##
251
+ # This option is a valid ImageMagick option, but it's also a Ruby method,
252
+ # so we need to override it so that it correctly acts as an option method.
253
+ #
254
+ def clone(*args)
255
+ self << '-clone'
256
+ self.merge!(args)
257
+ self
258
+ end
259
+
260
+ ##
261
+ # Any undefined method will be transformed into a CLI option
262
+ #
263
+ # @example
264
+ # mogrify = MiniMagick::Tool.new("mogrify")
265
+ # mogrify.adaptive_blur("...")
266
+ # mogrify.foo_bar
267
+ # mogrify.command.join(" ") # => "mogrify -adaptive-blur ... -foo-bar"
268
+ #
269
+ def method_missing(name, *args)
270
+ option = "-#{name.to_s.tr('_', '-')}"
271
+ self << option
272
+ self.merge!(args)
273
+ self
274
+ end
275
+
276
+ def self.option_methods
277
+ @option_methods ||= (
278
+ tool = new(whiny: false)
279
+ tool << "-help"
280
+ help_page = tool.call(stderr: false)
281
+
282
+ cli_options = help_page.scan(/^\s+-[a-z\-]+/).map(&:strip)
283
+ if tool.name == "mogrify" && MiniMagick.graphicsmagick?
284
+ # These options were undocumented before 2015-06-14 (see gm bug 302)
285
+ cli_options |= %w[-box -convolve -gravity -linewidth -mattecolor -render -shave]
286
+ end
287
+
288
+ cli_options.map { |o| o[1..-1].tr('-','_') }
289
+ )
290
+ end
291
+
292
+ end
293
+ end
294
+
295
+ require "mini_magick/tool/animate"
296
+ require "mini_magick/tool/compare"
297
+ require "mini_magick/tool/composite"
298
+ require "mini_magick/tool/conjure"
299
+ require "mini_magick/tool/convert"
300
+ require "mini_magick/tool/display"
301
+ require "mini_magick/tool/identify"
302
+ require "mini_magick/tool/import"
303
+ require "mini_magick/tool/magick"
304
+ require "mini_magick/tool/mogrify"
305
+ require "mini_magick/tool/mogrify_restricted"
306
+ require "mini_magick/tool/montage"
307
+ require "mini_magick/tool/stream"
@@ -1,31 +1,35 @@
1
- require 'rbconfig'
1
+ require "tempfile"
2
2
 
3
3
  module MiniMagick
4
+ # @private
4
5
  module Utilities
5
- class << self
6
- # Cross-platform way of finding an executable in the $PATH.
7
- #
8
- # which('ruby') #=> /usr/bin/ruby
9
- def which(cmd)
10
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
11
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
12
- exts.each { |ext|
13
- exe = File.join(path, "#{cmd}#{ext}")
14
- return exe if File.executable? exe
15
- }
16
- end
17
- return nil
18
- end
19
6
 
20
- # Finds out if the host OS is windows
21
- def windows?
22
- RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
7
+ module_function
8
+
9
+ ##
10
+ # Cross-platform way of finding an executable in the $PATH.
11
+ #
12
+ # @example
13
+ # MiniMagick::Utilities.which('ruby') #=> "/usr/bin/ruby"
14
+ #
15
+ def which(cmd)
16
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
17
+ ENV.fetch('PATH').split(File::PATH_SEPARATOR).each do |path|
18
+ exts.each do |ext|
19
+ exe = File.join(path, "#{cmd}#{ext}")
20
+ return exe if File.executable? exe
21
+ end
23
22
  end
23
+ nil
24
+ end
24
25
 
25
- def windows_escape(cmdline)
26
- '"' + cmdline.gsub(/\\(?=\\*\")/, "\\\\\\").gsub(/\"/, "\\\"").gsub(/\\$/, "\\\\\\").gsub("%", "%%") + '"'
26
+ def tempfile(extension)
27
+ Tempfile.new(["mini_magick", extension]).tap do |tempfile|
28
+ tempfile.binmode
29
+ yield tempfile if block_given?
30
+ tempfile.close
27
31
  end
28
32
  end
33
+
29
34
  end
30
35
  end
31
-
@@ -1,3 +1,17 @@
1
1
  module MiniMagick
2
- VERSION = "3.7.0"
2
+ ##
3
+ # @return [Gem::Version]
4
+ #
5
+ def self.version
6
+ Gem::Version.new VERSION::STRING
7
+ end
8
+
9
+ module VERSION
10
+ MAJOR = 4
11
+ MINOR = 11
12
+ TINY = 0
13
+ PRE = nil
14
+
15
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
16
+ end
3
17
  end