mini_magick 3.8.0 → 3.8.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mini_magick might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7708f032a0b010ff31ed437bd5f28c5bdf4b269b
4
- data.tar.gz: 866153c83f7e87dfe3d1564ca32821221240d6a4
3
+ metadata.gz: 4f4ae6b4f7d224af6275ad37b108c978f3fd8817
4
+ data.tar.gz: 69d6e3f3b1289af2dd54a0309166e71fce1e2f45
5
5
  SHA512:
6
- metadata.gz: f1af9965e59c483a1566299d984f1657a2fc673c398a07e2fc07fb8e3d821eb9ec4f4f6fbcbcbb2f4497714f703f410ab64bc2b02ded74cb952b019171663ef7
7
- data.tar.gz: efe209dfdd7bedd32e7d44dc49cdda24e339a68e49fe4cfbfa900edb60c7a7b9c9627316df330d8ab90a6054e03f6db213c6360313d4ce26d35c925094437a11
6
+ metadata.gz: 248c08f2af654156c2a422bb3bb3e48a9b97087bab6dacace044bbc2175465c9a2e5de09eac05cb4110cc4efcc8a47f25424f03f6effa6df2cbb2fda85524557
7
+ data.tar.gz: 3c000c0861b8e66312d875b8858cdc3fe0b32c842332b70c979675acbf9b4fc62fcb4732c5c49db96240e0bf400974a90ee01e90a34b1c3d817eaf60444030fb
data/lib/mini_magick.rb CHANGED
@@ -1,8 +1,3 @@
1
- require 'tempfile'
2
- require 'subexec'
3
- require 'stringio'
4
- require 'pathname'
5
- require 'shellwords'
6
1
  require 'mini_magick/command_builder'
7
2
  require 'mini_magick/errors'
8
3
  require 'mini_magick/image'
@@ -16,23 +11,20 @@ module MiniMagick
16
11
  attr_accessor :processor
17
12
  attr_accessor :processor_path
18
13
  attr_accessor :timeout
14
+ attr_accessor :debug
19
15
  attr_accessor :validate_on_create
20
16
  attr_accessor :validate_on_write
21
17
 
22
18
  ##
23
- # Tries to detect the current processor based if any of the processors exist.
24
- # Mogrify have precedence over gm by default.
19
+ # Tries to detect the current processor based if any of the processors
20
+ # exist. Mogrify have precedence over gm by default.
25
21
  #
26
22
  # === Returns
27
- # * [String] The detected procesor
28
- def choose_processor
29
- self.processor = if MiniMagick::Utilities.which('mogrify')
30
- :mogrify
31
- elsif MiniMagick::Utilities.which('gm')
32
- :gm
33
- else
34
- nil
35
- end
23
+ # * [Symbol] The detected procesor
24
+ def processor
25
+ @processor ||= [:mogrify, :gm].detect do |processor|
26
+ MiniMagick::Utilities.which(processor.to_s)
27
+ end
36
28
  end
37
29
 
38
30
  ##
@@ -63,27 +55,21 @@ module MiniMagick
63
55
  end
64
56
 
65
57
  ##
66
- # Picks the right processor if it isn't set and returns whether it's mogrify or not.
58
+ # Checks whether the current processory is mogrify.
67
59
  #
68
60
  # === Returns
69
61
  # * [Boolean]
70
62
  def mogrify?
71
- choose_processor if processor.nil?
72
-
73
- return processor.to_s.downcase.to_sym == :mogrify unless processor.nil?
74
- false
63
+ processor && processor.to_sym == :mogrify
75
64
  end
76
65
 
77
66
  ##
78
- # Picks the right processor if it isn't set and returns whether it's graphicsmagick or not.
67
+ # Checks whether the current processor is graphicsmagick.
79
68
  #
80
69
  # === Returns
81
70
  # * [Boolean]
82
71
  def gm?
83
- choose_processor if processor.nil?
84
-
85
- return processor.to_s.downcase.to_sym == :gm unless processor.nil?
86
- false
72
+ processor && processor.to_sym == :gm
87
73
  end
88
74
  end
89
75
  end
@@ -13,16 +13,12 @@ module MiniMagick
13
13
  com = "#{@tool} #{args.join(' ')}".strip
14
14
  com = "#{MiniMagick.processor} #{com}" unless MiniMagick.mogrify?
15
15
 
16
- com = File.join MiniMagick.processor_path, com unless MiniMagick.processor_path.nil?
16
+ com = File.join MiniMagick.processor_path, com if MiniMagick.processor_path
17
17
  com.strip
18
18
  end
19
19
 
20
20
  def args
21
- if !MiniMagick::Utilities.windows?
22
- @args.map(&:shellescape)
23
- else
24
- @args.map { |arg| Utilities.windows_escape(arg) }
25
- end
21
+ @args.map { |arg| Utilities.escape(arg) }
26
22
  end
27
23
 
28
24
  # Add each mogrify command in both underscore and dash format
@@ -75,30 +71,18 @@ module MiniMagick
75
71
  end
76
72
 
77
73
  def +(*options)
78
- push(@args.pop.gsub(/^-/, '+'))
79
- if options.any?
80
- options.each do |o|
81
- push o
82
- end
83
- end
74
+ push(@args.pop.gsub(/\A-/, '+'))
75
+ options.to_a.each { |option| push(option) }
84
76
  end
85
77
 
86
78
  def add_command(command, *options)
87
79
  push "-#{command}"
88
- if options.any?
89
- options.each do |o|
90
- push o
91
- end
92
- end
80
+ options.to_a.each { |option| push(option) }
93
81
  end
94
82
 
95
83
  def add_creation_operator(command, *options)
96
84
  creation_command = command
97
- if options.any?
98
- options.each do |option|
99
- creation_command << ":#{option}"
100
- end
101
- end
85
+ options.to_a.each { |option| creation_command << ":#{option}" }
102
86
  push creation_command
103
87
  end
104
88
 
@@ -1,35 +1,37 @@
1
+ require 'tempfile'
2
+ require 'subexec'
3
+ require 'stringio'
4
+ require 'pathname'
5
+
1
6
  module MiniMagick
2
7
  class Image
3
8
  # @return [String] The location of the current working file
4
9
  attr_writer :path
5
10
 
6
- def path_for_windows_quote_space(path)
7
- path = Pathname.new(@path).to_s
8
- # For Windows, if a path contains space char, you need to quote it, otherwise you SHOULD NOT quote it.
9
- # If you quote a path that does not contains space, it will not work.
10
- @path.include?(' ') ? path.inspect : path
11
- end
12
-
13
11
  def path
14
12
  run_queue if @command_queued
15
- MiniMagick::Utilities.windows? ? path_for_windows_quote_space(@path) : @path
13
+ MiniMagick::Utilities.path(@path)
16
14
  end
17
15
 
18
16
  # Class Methods
19
17
  # -------------
20
18
  class << self
21
- # This is the primary loading method used by all of the other class methods.
19
+ # This is the primary loading method used by all of the other class
20
+ # methods.
22
21
  #
23
- # Use this to pass in a stream object. Must respond to Object#read(size) or be a binary string object (BLOBBBB)
22
+ # Use this to pass in a stream object. Must respond to Object#read(size)
23
+ # or be a binary string object (BLOBBBB)
24
24
  #
25
- # As a change from the old API, please try and use IOStream objects.
26
- # They are much, much better and more efficient!
25
+ # As a change from the old API, please try and use IOStream objects. They
26
+ # are much, much better and more efficient!
27
27
  #
28
- # Probably easier to use the #open method if you want to open a file or a URL.
28
+ # Probably easier to use the #open method if you want to open a file or a
29
+ # URL.
29
30
  #
30
- # @param stream [IOStream, String] Some kind of stream object that needs to be read or is a binary String blob!
31
- # @param ext [String] A manual extension to use for reading the file. Not required, but if you are having issues,
32
- # give this a try.
31
+ # @param stream [IOStream, String] Some kind of stream object that needs
32
+ # to be read or is a binary String blob!
33
+ # @param ext [String] A manual extension to use for reading the file. Not
34
+ # required, but if you are having issues, give this a try.
33
35
  # @return [Image]
34
36
  def read(stream, ext = nil)
35
37
  if stream.is_a?(String)
@@ -57,31 +59,32 @@ module MiniMagick
57
59
  create(ext) { |f| f.write(blob) }
58
60
  end
59
61
 
60
- # Creates an image object from a binary string blob which contains raw pixel data (i.e. no header data).
61
- #
62
- # === Returns
62
+ # Creates an image object from a binary string blob which contains raw
63
+ # pixel data (i.e. no header data).
63
64
  #
64
- # * [Image] The loaded image.
65
- #
66
- # === Parameters
67
- #
68
- # * [blob] <tt>String</tt> -- Binary string blob containing raw pixel data.
69
- # * [columns] <tt>Integer</tt> -- Number of columns.
70
- # * [rows] <tt>Integer</tt> -- Number of rows.
71
- # * [depth] <tt>Integer</tt> -- Bit depth of the encoded pixel data.
72
- # * [map] <tt>String</tt> -- A code for the mapping of the pixel data. Example: 'gray' or 'rgb'.
73
- # * [format] <tt>String</tt> -- The file extension of the image format to be used when creating the image object.
65
+ # @param blob [String] Binary string blob containing raw pixel data.
66
+ # @param columns [Integer] Number of columns.
67
+ # @param rows [Integer] Number of rows.
68
+ # @param depth [Integer] Bit depth of the encoded pixel data.
69
+ # @param map [String] A code for the mapping of the pixel data. Example:
70
+ # 'gray' or 'rgb'.
71
+ # @param format [String] The file extension of the image format to be
72
+ # used when creating the image object.
74
73
  # Defaults to 'png'.
74
+ # @return [Image] The loaded image.
75
75
  #
76
76
  def import_pixels(blob, columns, rows, depth, map, format = 'png')
77
77
  # Create an image object with the raw pixel data string:
78
78
  image = create('.dat', false) { |f| f.write(blob) }
79
- # Use ImageMagick to convert the raw data file to an image file of the desired format:
79
+ # Use ImageMagick to convert the raw data file to an image file of the
80
+ # desired format:
80
81
  converted_image_path = image.path[0..-4] + format
81
82
  arguments = ['-size', "#{columns}x#{rows}", '-depth', "#{depth}", "#{map}:#{image.path}", "#{converted_image_path}"]
82
- cmd = CommandBuilder.new('convert', *arguments) # Example: convert -size 256x256 -depth 16 gray:blob.dat blob.png
83
+ # Example: convert -size 256x256 -depth 16 gray:blob.dat blob.png
84
+ cmd = CommandBuilder.new('convert', *arguments)
83
85
  image.run(cmd)
84
- # Update the image instance with the path of the properly formatted image, and return:
86
+ # Update the image instance with the path of the properly formatted
87
+ # image, and return:
85
88
  image.path = converted_image_path
86
89
  image
87
90
  end
@@ -90,15 +93,18 @@ module MiniMagick
90
93
  #
91
94
  # Use this if you don't want to overwrite the image file.
92
95
  #
93
- # Extension is either guessed from the path or you can specify it as a second parameter.
96
+ # Extension is either guessed from the path or you can specify it as a
97
+ # second parameter.
94
98
  #
95
- # If you pass in what looks like a URL, we require 'open-uri' before opening it.
99
+ # If you pass in what looks like a URL, we require 'open-uri' before
100
+ # opening it.
96
101
  #
97
- # @param file_or_url [String] Either a local file path or a URL that open-uri can read
102
+ # @param file_or_url [String] Either a local file path or a URL that
103
+ # open-uri can read
98
104
  # @param ext [String] Specify the extension you want to read it as
99
105
  # @return [Image] The loaded image
100
106
  def open(file_or_url, ext = nil)
101
- file_or_url = file_or_url.to_s # Force it to be a String... Hell or high water
107
+ file_or_url = file_or_url.to_s # Force String... Hell or high water
102
108
  if file_or_url.include?('://')
103
109
  require 'open-uri'
104
110
  ext ||= File.extname(URI.parse(file_or_url).path)
@@ -119,43 +125,47 @@ module MiniMagick
119
125
  open(file, ext)
120
126
  end
121
127
 
122
- # Used to create a new Image object data-copy. Not used to "paint" or that kind of thing.
128
+ # Used to create a new Image object data-copy. Not used to "paint" or
129
+ # that kind of thing.
123
130
  #
124
- # Takes an extension in a block and can be used to build a new Image object. Used
125
- # by both #open and #read to create a new object! Ensures we have a good tempfile!
131
+ # Takes an extension in a block and can be used to build a new Image
132
+ # object. Used by both #open and #read to create a new object! Ensures we
133
+ # have a good tempfile!
126
134
  #
127
135
  # @param ext [String] Specify the extension you want to read it as
128
- # @param validate [Boolean] If false, skips validation of the created image. Defaults to true.
129
- # @yield [IOStream] You can #write bits to this object to create the new Image
136
+ # @param validate [Boolean] If false, skips validation of the created
137
+ # image. Defaults to true.
138
+ # @yield [IOStream] You can #write bits to this object to create the new
139
+ # Image
130
140
  # @return [Image] The created image
131
141
  def create(ext = nil, validate = MiniMagick.validate_on_create, &block)
132
- begin
133
- tempfile = Tempfile.new(['mini_magick', ext.to_s.downcase])
134
- tempfile.binmode
135
- block.call(tempfile)
136
- tempfile.close
137
-
138
- image = new(tempfile.path, tempfile)
139
-
140
- fail MiniMagick::Invalid if validate && !image.valid?
141
- return image
142
- ensure
143
- tempfile.close if tempfile
144
- end
142
+ tempfile = Tempfile.new(['mini_magick', ext.to_s.downcase])
143
+ tempfile.binmode
144
+ block.call(tempfile)
145
+ tempfile.close
146
+
147
+ image = new(tempfile.path, tempfile)
148
+
149
+ fail MiniMagick::Invalid if validate && !image.valid?
150
+ return image
151
+ ensure
152
+ tempfile.close if tempfile
145
153
  end
146
154
  end
147
155
 
148
156
  # Create a new MiniMagick::Image object
149
157
  #
150
- # _DANGER_: The file location passed in here is the *working copy*. That is, it gets *modified*.
151
- # you can either copy it yourself or use the MiniMagick::Image.open(path) method which creates a
152
- # temporary file for you and protects your original!
158
+ # _DANGER_: The file location passed in here is the *working copy*. That
159
+ # is, it gets *modified*. you can either copy it yourself or use the
160
+ # MiniMagick::Image.open(path) method which creates a temporary file for
161
+ # you and protects your original!
153
162
  #
154
163
  # @param input_path [String] The location of an image file
155
- # @todo Allow this to accept a block that can pass off to Image#combine_options
164
+ # @todo Allow this to accept a block that can pass off to
165
+ # Image#combine_options
156
166
  def initialize(input_path, tempfile = nil)
157
167
  @path = input_path
158
- @tempfile = tempfile # ensures that the tempfile will stick around until this image is garbage collected.
168
+ @tempfile = tempfile
159
169
  @info = {}
160
170
  reset_queue
161
171
  end
@@ -168,16 +178,16 @@ module MiniMagick
168
178
 
169
179
  def run_queue
170
180
  return nil unless @command_queued
171
- @queue << (MiniMagick::Utilities.windows? ? path_for_windows_quote_space(@path) : @path)
181
+ @queue << MiniMagick::Utilities.path(@path)
172
182
  run(@queue)
173
183
  reset_queue
174
184
  end
175
185
 
176
186
  # Checks to make sure that MiniMagick can read the file and understand it.
177
187
  #
178
- # This uses the 'identify' command line utility to check the file. If you are having
179
- # issues with this, then please work directly with the 'identify' command and see if you
180
- # can figure out what the issue is.
188
+ # This uses the 'identify' command line utility to check the file. If you
189
+ # are having issues with this, then please work directly with the
190
+ # 'identify' command and see if you can figure out what the issue is.
181
191
  #
182
192
  # @return [Boolean]
183
193
  def valid?
@@ -192,8 +202,10 @@ module MiniMagick
192
202
 
193
203
  @info[key]
194
204
  end
195
- # A rather low-level way to interact with the "identify" command. No nice API here, just
196
- # the crazy stuff you find in ImageMagick. See the examples listed!
205
+
206
+ # A rather low-level way to interact with the "identify" command. No nice
207
+ # API here, just the crazy stuff you find in ImageMagick. See the examples
208
+ # listed!
197
209
  #
198
210
  # @example
199
211
  # image["format"] #=> "TIFF"
@@ -206,13 +218,15 @@ module MiniMagick
206
218
  # image["EXIF:ExifVersion"] #=> "0220" (Can read anything from Exif)
207
219
  #
208
220
  # @param format [String] A format for the "identify" command
209
- # @see For reference see http://www.imagemagick.org/script/command-line-options.php#format
210
- # @return [String, Numeric, Array, Time, Object] Depends on the method called! Defaults to String for unknown commands
221
+ # @see http://www.imagemagick.org/script/command-line-options.php#format
222
+ # @return [String, Numeric, Array, Time, Object] Depends on the method
223
+ # called! Defaults to String for unknown commands
211
224
  def [](value)
212
225
  retrieved = info(value)
213
226
  return retrieved unless retrieved.nil?
214
227
 
215
- # Why do I go to the trouble of putting in newlines? Because otherwise animated gifs screw everything up
228
+ # Why do I go to the trouble of putting in newlines? Because otherwise
229
+ # animated gifs screw everything up
216
230
  retrieved = case value.to_s
217
231
  when 'colorspace'
218
232
  run_command('identify', '-format', '%r\n', path).split("\n")[0].strip
@@ -226,7 +240,7 @@ module MiniMagick
226
240
  @info[:width] = width_height[0]
227
241
  @info[:height] = width_height[1]
228
242
  @info[:dimensions] = width_height
229
- nil
243
+ @info[value.to_sym]
230
244
  when 'size'
231
245
  File.size(path) # Do this because calling identify -format "%b" on an animated gif fails!
232
246
  when 'original_at'
@@ -247,33 +261,38 @@ module MiniMagick
247
261
  @info[value]
248
262
  end
249
263
 
250
- # Sends raw commands to imagemagick's `mogrify` command. The image path is automatically appended to the command.
264
+ # Sends raw commands to imagemagick's `mogrify` command. The image path is
265
+ # automatically appended to the command.
251
266
  #
252
- # Remember, we are always acting on this instance of the Image when messing with this.
267
+ # Remember, we are always acting on this instance of the Image when messing
268
+ # with this.
253
269
  #
254
- # @return [String] Whatever the result from the command line is. May not be terribly useful.
270
+ # @return [String] Whatever the result from the command line is. May not be
271
+ # terribly useful.
255
272
  def <<(*args)
256
273
  run_command('mogrify', *args << path)
257
274
  end
258
275
 
259
- # This is used to change the format of the image. That is, from "tiff to jpg" or something like that.
260
- # Once you run it, the instance is pointing to a new file with a new extension!
276
+ # This is used to change the format of the image. That is, from "tiff to
277
+ # jpg" or something like that. Once you run it, the instance is pointing to
278
+ # a new file with a new extension!
261
279
  #
262
- # *DANGER*: This renames the file that the instance is pointing to. So, if you manually opened the
263
- # file with Image.new(file_path)... Then that file is DELETED! If you used Image.open(file) then
264
- # you are OK. The original file will still be there. But, any changes to it might not be...
280
+ # *DANGER*: This renames the file that the instance is pointing to. So, if
281
+ # you manually opened the file with Image.new(file_path)... Then that file
282
+ # is DELETED! If you used Image.open(file) then you are OK. The original
283
+ # file will still be there. But, any changes to it might not be...
265
284
  #
266
- # Formatting an animation into a non-animated type will result in ImageMagick creating multiple
267
- # pages (starting with 0). You can choose which page you want to manipulate. We default to the
268
- # first page.
285
+ # Formatting an animation into a non-animated type will result in
286
+ # ImageMagick creating multiple pages (starting with 0). You can choose
287
+ # which page you want to manipulate. We default to the first page.
269
288
  #
270
289
  # If you would like to convert between animated formats, pass nil as your
271
290
  # page and ImageMagick will copy all of the pages.
272
291
  #
273
- # @param format [String] The target format... Like 'jpg', 'gif', 'tiff', etc.
274
- # @param page [Integer] If this is an animated gif, say which 'page' you want
275
- # with an integer. Default 0 will convert only the first page; 'nil' will
276
- # convert all pages.
292
+ # @param format [String] The target format... Like 'jpg', 'gif', 'tiff' etc.
293
+ # @param page [Integer] If this is an animated gif, say which 'page' you
294
+ # want with an integer. Default 0 will convert only the first page; 'nil'
295
+ # will convert all pages.
277
296
  # @return [nil]
278
297
  def format(format, page = 0)
279
298
  run_queue if @command_queued
@@ -300,12 +319,14 @@ module MiniMagick
300
319
  run_command('mogrify', '-quality', '100', "#{path}[0]")
301
320
  end
302
321
 
303
- # Writes the temporary file out to either a file location (by passing in a String) or by
304
- # passing in a Stream that you can #write(chunk) to repeatedly
322
+ # Writes the temporary file out to either a file location (by passing in a
323
+ # String) or by passing in a Stream that you can #write(chunk) to
324
+ # repeatedly
305
325
  #
306
- # @param output_to [IOStream, String] Some kind of stream object that needs to be read or a file path as a String
307
- # @return [IOStream, Boolean] If you pass in a file location [String] then you get a success boolean. If its a stream, you get it back.
308
- # Writes the temporary image that we are using for processing to the output path
326
+ # @param output_to [IOStream, String] Some kind of stream object that needs
327
+ # to be read or a file path as a String
328
+ # @return [IOStream, Boolean] If you pass in a file location [String] then
329
+ # you get a success boolean. If its a stream, you get it back.
309
330
  def write(output_to)
310
331
  run_queue if @command_queued
311
332
 
@@ -313,7 +334,7 @@ module MiniMagick
313
334
  FileUtils.copy_file path, output_to
314
335
  if MiniMagick.validate_on_write
315
336
  run_command(
316
- 'identify', MiniMagick::Utilities.windows? ? path_for_windows_quote_space(output_to.to_s) : output_to.to_s
337
+ 'identify', MiniMagick::Utilities.path(output_to.to_s)
317
338
  ) # Verify that we have a good image
318
339
  end
319
340
  else # stream
@@ -344,14 +365,17 @@ module MiniMagick
344
365
  'image/' + format.to_s.downcase
345
366
  end
346
367
 
347
- # If an unknown method is called then it is sent through the mogrify program
348
- # Look here to find all the commands (http://www.imagemagick.org/script/mogrify.php)
368
+ # If an unknown method is called then it is sent through the mogrify
369
+ # program.
370
+ #
371
+ # @see http://www.imagemagick.org/script/mogrify.php
349
372
  def method_missing(symbol, *args)
350
373
  @queue.send(symbol, *args)
351
374
  @command_queued = true
352
375
  end
353
376
 
354
- # You can use multiple commands together using this method. Very easy to use!
377
+ # You can use multiple commands together using this method. Very easy to
378
+ # use!
355
379
  #
356
380
  # @example
357
381
  # image.combine_options do |c|
@@ -393,7 +417,7 @@ module MiniMagick
393
417
 
394
418
  if command == 'identify'
395
419
  args.unshift '-ping' # -ping "efficiently determine image characteristics."
396
- args.unshift '-quiet' if MiniMagick.mogrify? # graphicsmagick has no -quiet option.
420
+ args.unshift '-quiet' if MiniMagick.mogrify? && !MiniMagick.debug # graphicsmagick has no -quiet option.
397
421
  end
398
422
 
399
423
  run(CommandBuilder.new(command, *args))
@@ -423,20 +447,15 @@ module MiniMagick
423
447
 
424
448
  def destroy!
425
449
  return if @tempfile.nil?
426
- File.unlink(path) if File.exist?(path)
450
+ File.unlink(@path) if File.exist?(@path)
427
451
  @tempfile = nil
428
452
  end
429
453
 
430
454
  private
431
455
 
432
456
  # Sometimes we get back a list of character values
433
- def read_character_data(list_of_characters)
434
- chars = list_of_characters.gsub(' ', '').split(',')
435
- result = ''
436
- chars.each do |val|
437
- result << ('%c' % val.to_i)
438
- end
439
- result
457
+ def read_character_data(string)
458
+ string.scan(/\d+/).map(&:to_i).map(&:chr).join
440
459
  end
441
460
  end
442
461
  end