tupalo-mini_magick 1.2.5 → 3.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,7 +1,8 @@
1
1
  = MiniMagick
2
2
 
3
- A ruby wrapper for ImageMagick command line.
3
+ A ruby wrapper for ImageMagick or GraphicsMagick command line.
4
4
 
5
+ Tested on both Ruby 1.9.2 and Ruby 1.8.7.
5
6
 
6
7
  == Why?
7
8
 
@@ -31,24 +32,43 @@ has (Found here http://www.imagemagick.org/script/mogrify.php)
31
32
 
32
33
  Want to make a thumbnail from a file...
33
34
 
34
- image = MiniMagick::Image.from_file("input.jpg")
35
+ image = MiniMagick::Image.open("input.jpg")
35
36
  image.resize "100x100"
36
- image.write("output.jpg")
37
+ image.write "output.jpg"
37
38
 
38
39
  Want to make a thumbnail from a blob...
39
40
 
40
- image = MiniMagick::Image.from_blob(blob)
41
+ image = MiniMagick::Image.read(blob)
41
42
  image.resize "100x100"
42
- image.write("output.jpg")
43
+ image.write "output.jpg"
44
+
45
+ Got an incoming IOStream?
46
+
47
+ image = MiniMagick::Image.read(stream)
48
+
49
+ Want to make a thumbnail of a remote image?
50
+
51
+ image = MiniMagick::Image.open("http://www.google.com/images/logos/logo.png")
52
+ image.resize "5x5"
53
+ image.format "gif"
54
+ image.write "localcopy.gif"
43
55
 
44
56
  Need to combine several options?
45
57
 
46
- image = MiniMagick::Image.from_file("input.jpg")
58
+ image = MiniMagick::Image.open("input.jpg")
47
59
  image.combine_options do |c|
48
60
  c.sample "50%"
49
61
  c.rotate "-90>"
50
62
  end
51
- image.write("output.jpg")
63
+ image.write "output.jpg"
64
+
65
+ Want to composite two images? Super easy! (Aka, put a watermark on!)
66
+
67
+ image = Image.from_file("original.png")
68
+ result = image.composite(Image.open("watermark.png", "jpg") do |c|
69
+ c.gravity "center"
70
+ end
71
+ result.write "my_output_file.jpg"
52
72
 
53
73
  Want to manipulate an image at its source (You won't have to write it
54
74
  out because the transformations are done on that file)
@@ -58,7 +78,7 @@ out because the transformations are done on that file)
58
78
 
59
79
  Want to get some meta-information out?
60
80
 
61
- image = MiniMagick::Image.from_file("input.jpg")
81
+ image = MiniMagick::Image.open("input.jpg")
62
82
  image[:width] # will get the width (you can also use :height and :format)
63
83
  image["EXIF:BitsPerSample"] # It also can get all the EXIF tags
64
84
  image["%m:%f %wx%h"] # Or you can use one of the many options of the format command
@@ -69,4 +89,4 @@ http://www.imagemagick.org/script/command-line-options.php#format
69
89
 
70
90
  == Requirements
71
91
 
72
- You must have ImageMagick installed.
92
+ You must have ImageMagick or GraphicsMagick installed.
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
4
5
 
5
6
  $:.unshift(File.dirname(__FILE__) + "/lib")
6
7
  require 'mini_magick'
@@ -10,7 +11,7 @@ task :default => :test
10
11
 
11
12
  desc 'Test the mini_magick plugin.'
12
13
  Rake::TestTask.new(:test) do |t|
13
- t.libs << 'lib'
14
+ t.libs << 'test'
14
15
  t.pattern = 'test/**/*_test.rb'
15
16
  t.verbose = true
16
17
  end
@@ -25,15 +26,7 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
25
26
  rdoc.rdoc_files.include('lib/**/*.rb')
26
27
  end
27
28
 
28
- begin
29
- require 'jeweler'
30
- Jeweler::Tasks.new do |gemspec|
31
- gemspec.name = "mini_magick"
32
- gemspec.summary = "Manipulate images with minimal use of memory."
33
- gemspec.email = "probablycorey@gmail.com"
34
- gemspec.homepage = "http://github.com/probablycorey/mini_magick"
35
- gemspec.authors = ["Corey Johnson"]
36
- end
37
- rescue LoadError
38
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
39
- end
29
+ spec = eval(File.read('mini_magick.gemspec'))
30
+ Rake::GemPackageTask.new(spec) do |pkg|
31
+ pkg.gem_spec = spec
32
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.5
1
+ 3.1
@@ -0,0 +1,2 @@
1
+ require 'mini_magick'
2
+ MiniMagick.processor = :gm
data/lib/mini_magick.rb CHANGED
@@ -1,97 +1,232 @@
1
- require "open-uri"
2
- require "stringio"
3
- require "fileutils"
4
- require "open3"
5
-
6
- require File.join(File.dirname(__FILE__), '/image_temp_file')
1
+ require 'tempfile'
2
+ require 'subexec'
3
+ require 'pathname'
7
4
 
8
5
  module MiniMagick
9
- class MiniMagickError < RuntimeError; end
6
+ class << self
7
+ attr_accessor :processor
8
+ attr_accessor :timeout
9
+
10
+
11
+ # Experimental method for automatically selecting a processor
12
+ # such as gm. Only works on *nix.
13
+ #
14
+ # TODO: Write tests for this and figure out what platforms it supports
15
+ def choose_processor
16
+ if `type -P mogrify`.size > 0
17
+ return
18
+ elsif `type -P gm`.size > 0
19
+ self.processor = "gm"
20
+ end
21
+ end
22
+ end
23
+
24
+ MOGRIFY_COMMANDS = %w{adaptive-blur adaptive-resize adaptive-sharpen adjoin affine alpha annotate antialias append authenticate auto-gamma auto-level auto-orient background bench iterations bias black-threshold blue-primary point blue-shift factor blur border bordercolor brightness-contrast caption string cdl filename channel type charcoal radius chop clip clamp clip-mask filename clip-path id clone index clut contrast-stretch coalesce colorize color-matrix colors colorspace type combine comment string compose operator composite compress type contrast convolve coefficients crop cycle amount decipher filename debug events define format:option deconstruct delay delete index density depth despeckle direction type display server dispose method distort type coefficients dither method draw string edge radius emboss radius encipher filename encoding type endian type enhance equalize evaluate operator evaluate-sequence operator extent extract family name fft fill filter type flatten flip floodfill flop font name format string frame function name fuzz distance fx expression gamma gaussian-blur geometry gravity type green-primary point help identify ifft implode amount insert index intent type interlace type interline-spacing interpolate method interword-spacing kerning label string lat layers method level limit type linear-stretch liquid-rescale log format loop iterations mask filename mattecolor median radius modulate monitor monochrome morph morphology method kernel motion-blur negate noise radius normalize opaque ordered-dither NxN orient type page paint radius ping pointsize polaroid angle posterize levels precision preview type print string process image-filter profile filename quality quantizespace quiet radial-blur angle raise random-threshold low,high red-primary point regard-warnings region remap filename render repage resample resize respect-parentheses roll rotate degrees sample sampling-factor scale scene seed segments selective-blur separate sepia-tone threshold set attribute shade degrees shadow sharpen shave shear sigmoidal-contrast size sketch solarize threshold splice spread radius strip stroke strokewidth stretch type style type swap indexes swirl degrees texture filename threshold thumbnail tile filename tile-offset tint transform transparent transparent-color transpose transverse treedepth trim type type undercolor unique-colors units type unsharp verbose version view vignette virtual-pixel method wave weight type white-point point white-threshold write filename}
25
+
26
+ class Error < RuntimeError; end
27
+ class Invalid < StandardError; end
10
28
 
11
29
  class Image
30
+ # @return [String] The location of the current working file
12
31
  attr :path
13
- attr :tempfile
14
- attr :output
15
32
 
16
33
  # Class Methods
17
34
  # -------------
18
35
  class << self
36
+ # This is the primary loading method used by all of the other class methods.
37
+ #
38
+ # Use this to pass in a stream object. Must respond to Object#read(size) or be a binary string object (BLOBBBB)
39
+ #
40
+ # As a change from the old API, please try and use IOStream objects. They are much, much better and more efficient!
41
+ #
42
+ # Probably easier to use the #open method if you want to open a file or a URL.
43
+ #
44
+ # @param stream [IOStream, String] Some kind of stream object that needs to be read or is a binary String blob!
45
+ # @param ext [String] A manual extension to use for reading the file. Not required, but if you are having issues, give this a try.
46
+ # @return [Image]
47
+ def read(stream, ext = nil)
48
+ if stream.is_a?(String)
49
+ stream = StringIO.new(stream)
50
+ end
51
+
52
+ create(ext) do |f|
53
+ while chunk = stream.read(8192)
54
+ f.write(chunk)
55
+ end
56
+ end
57
+ end
58
+
59
+ # @deprecated Please use Image.read instead!
19
60
  def from_blob(blob, ext = nil)
20
- begin
21
- tempfile = ImageTempFile.new(ext)
22
- tempfile.binmode
23
- tempfile.write(blob)
24
- ensure
25
- tempfile.close if tempfile
61
+ warn "Warning: MiniMagick::Image.from_blob method is deprecated. Instead, please use Image.read"
62
+ create(ext) { |f| f.write(blob) }
63
+ end
64
+
65
+ # Opens a specific image file either on the local file system or at a URI.
66
+ #
67
+ # Use this if you don't want to overwrite the image file.
68
+ #
69
+ # Extension is either guessed from the path or you can specify it as a second parameter.
70
+ #
71
+ # If you pass in what looks like a URL, we require 'open-uri' before opening it.
72
+ #
73
+ # @param file_or_url [String] Either a local file path or a URL that open-uri can read
74
+ # @param ext [String] Specify the extension you want to read it as
75
+ # @return [Image] The loaded image
76
+ def open(file_or_url, ext = File.extname(file_or_url))
77
+ file_or_url = file_or_url.to_s # Force it to be a String... hell or highwater
78
+ if file_or_url.include?("://")
79
+ require 'open-uri'
80
+ self.read(Kernel::open(file_or_url), ext)
81
+ else
82
+ File.open(file_or_url, "rb") do |f|
83
+ self.read(f, ext)
84
+ end
26
85
  end
86
+ end
27
87
 
28
- return self.new(tempfile.path, tempfile)
88
+ # @deprecated Please use MiniMagick::Image.open(file_or_url) now
89
+ def from_file(file, ext = nil)
90
+ warn "Warning: MiniMagick::Image.from_file is now deprecated. Please use Image.open"
91
+ open(file, ext)
29
92
  end
30
93
 
31
- # Use this if you don't want to overwrite the image file
32
- def open(image_path)
33
- File.open(image_path, "rb") do |f|
34
- self.from_blob(f.read, File.extname(image_path))
94
+ # Used to create a new Image object data-copy. Not used to "paint" or that kind of thing.
95
+ #
96
+ # Takes an extension in a block and can be used to build a new Image object. Used
97
+ # by both #open and #read to create a new object! Ensures we have a good tempfile!
98
+ #
99
+ # @param ext [String] Specify the extension you want to read it as
100
+ # @yield [IOStream] You can #write bits to this object to create the new Image
101
+ # @return [Image] The created image
102
+ def create(ext = nil, &block)
103
+ begin
104
+ tempfile = Tempfile.new(['mini_magick', ext.to_s])
105
+ tempfile.binmode
106
+ block.call(tempfile)
107
+ tempfile.close
108
+
109
+ image = self.new(tempfile.path, tempfile)
110
+
111
+ if !image.valid?
112
+ raise MiniMagick::Invalid
113
+ end
114
+ return image
115
+ ensure
116
+ tempfile.close if tempfile
35
117
  end
36
118
  end
37
- alias_method :from_file, :open
38
119
  end
39
120
 
40
- # Instance Methods
41
- # ----------------
42
- def initialize(input_path, tempfile=nil)
121
+ # Create a new MiniMagick::Image object
122
+ #
123
+ # _DANGER_: The file location passed in here is the *working copy*. That is, it gets *modified*.
124
+ # you can either copy it yourself or use the MiniMagick::Image.open(path) method which creates a
125
+ # temporary file for you and protects your original!
126
+ #
127
+ # @param input_path [String] The location of an image file
128
+ # @todo Allow this to accept a block that can pass off to Image#combine_options
129
+ def initialize(input_path, tempfile = nil)
43
130
  @path = input_path
44
131
  @tempfile = tempfile # ensures that the tempfile will stick around until this image is garbage collected.
132
+ end
133
+
134
+ def escaped_path
135
+ Pathname.new(@path).to_s.gsub(" ", "\\ ")
136
+ end
45
137
 
46
- # Ensure that the file is an image
138
+ # Checks to make sure that MiniMagick can read the file and understand it.
139
+ #
140
+ # This uses the 'identify' command line utility to check the file. If you are having
141
+ # issues with this, then please work directly with the 'identify' command and see if you
142
+ # can figure out what the issue is.
143
+ #
144
+ # @return [Boolean]
145
+ def valid?
47
146
  run_command("identify", @path)
147
+ true
148
+ rescue MiniMagick::Invalid
149
+ false
48
150
  end
49
151
 
50
- # For reference see http://www.imagemagick.org/script/command-line-options.php#format
152
+ # A rather low-level way to interact with the "identify" command. No nice API here, just
153
+ # the crazy stuff you find in ImageMagick. See the examples listed!
154
+ #
155
+ # @example
156
+ # image["format"] #=> "TIFF"
157
+ # image["height"] #=> 41 (pixels)
158
+ # image["width"] #=> 50 (pixels)
159
+ # image["dimensions"] #=> [50, 41]
160
+ # image["size"] #=> 2050 (bits)
161
+ # image["original_at"] #=> 2005-02-23 23:17:24 +0000 (Read from Exif data)
162
+ # image["EXIF:ExifVersion"] #=> "0220" (Can read anything from Exif)
163
+ #
164
+ # @param format [String] A format for the "identify" command
165
+ # @see For reference see http://www.imagemagick.org/script/command-line-options.php#format
166
+ # @return [String, Numeric, Array, Time, Object] Depends on the method called! Defaults to String for unknown commands
51
167
  def [](value)
52
168
  # Why do I go to the trouble of putting in newlines? Because otherwise animated gifs screw everything up
53
169
  case value.to_s
54
170
  when "format"
55
- run_command("identify", "-format", format_option("%m"), @path).split("\n")[0]
171
+ run_command("identify", "-format", format_option("%m"), escaped_path).split("\n")[0]
56
172
  when "height"
57
- run_command("identify", "-format", format_option("%h"), @path).split("\n")[0].to_i
173
+ run_command("identify", "-format", format_option("%h"), escaped_path).split("\n")[0].to_i
58
174
  when "width"
59
- run_command("identify", "-format", format_option("%w"), @path).split("\n")[0].to_i
175
+ run_command("identify", "-format", format_option("%w"), escaped_path).split("\n")[0].to_i
60
176
  when "dimensions"
61
- run_command("identify", "-format", format_option("%w %h"), @path).split("\n")[0].split.map{|v|v.to_i}
177
+ run_command("identify", "-format", format_option("%w %h"), escaped_path).split("\n")[0].split.map{|v|v.to_i}
62
178
  when "size"
63
179
  File.size(@path) # Do this because calling identify -format "%b" on an animated gif fails!
64
180
  when "original_at"
65
181
  # Get the EXIF original capture as a Time object
66
182
  Time.local(*self["EXIF:DateTimeOriginal"].split(/:|\s+/)) rescue nil
67
183
  when /^EXIF\:/i
68
- run_command('identify', '-format', "\"%[#{value}]\"", @path).chop
184
+ result = run_command('identify', '-format', "\"%[#{value}]\"", escaped_path).chop
185
+ if result.include?(",")
186
+ read_character_data(result)
187
+ else
188
+ result
189
+ end
69
190
  else
70
- run_command('identify', '-format', "\"#{value}\"", @path).split("\n")[0]
191
+ run_command('identify', '-format', "\"#{value}\"", escaped_path).split("\n")[0]
71
192
  end
72
193
  end
73
194
 
74
- # Sends raw commands to imagemagick's mogrify command. The image path is automatically appended to the command
195
+ # Sends raw commands to imagemagick's `mogrify` command. The image path is automatically appended to the command.
196
+ #
197
+ # Remember, we are always acting on this instance of the Image when messing with this.
198
+ #
199
+ # @return [String] Whatever the result from the command line is. May not be terribly useful.
75
200
  def <<(*args)
76
- run_command("mogrify", *args << @path)
201
+ run_command("mogrify", *args << escaped_path)
77
202
  end
78
203
 
79
- # This is a 'special' command because it needs to change @path to reflect the new extension
204
+ # This is used to change the format of the image. That is, from "tiff to jpg" or something like that.
205
+ # Once you run it, the instance is pointing to a new file with a new extension!
206
+ #
207
+ # *DANGER*: This renames the file that the instance is pointing to. So, if you manually opened the
208
+ # file with Image.new(file_path)... then that file is DELETED! If you used Image.open(file) then
209
+ # you are ok. The original file will still be there. But, any changes to it might not be...
210
+ #
80
211
  # Formatting an animation into a non-animated type will result in ImageMagick creating multiple
81
212
  # pages (starting with 0). You can choose which page you want to manipulate. We default to the
82
213
  # first page.
83
- def format(format, page=0)
214
+ #
215
+ # @param format [String] The target format... like 'jpg', 'gif', 'tiff', etc.
216
+ # @param page [Integer] If this is an animated gif, say which 'page' you want with an integer. Leave as default if you don't care.
217
+ # @return [nil]
218
+ def format(format, page = 0)
84
219
  run_command("mogrify", "-format", format, @path)
85
220
 
86
221
  old_path = @path.dup
87
- @path.sub!(/(\.\w+)?$/, ".#{format}")
88
- File.delete(old_path) unless old_path == @path
222
+ @path.sub!(/(\.\w*)?$/, ".#{format}")
223
+ File.delete(old_path) if old_path != @path
89
224
 
90
225
  unless File.exists?(@path)
91
226
  begin
92
227
  FileUtils.copy_file(@path.sub(".#{format}", "-#{page}.#{format}"), @path)
93
- rescue e
94
- raise MiniMagickError, "Unable to format to #{format}; #{e}" unless File.exist?(@path)
228
+ rescue => ex
229
+ raise MiniMagick::Error, "Unable to format to #{format}; #{ex}" unless File.exist?(@path)
95
230
  end
96
231
  end
97
232
  ensure
@@ -100,13 +235,35 @@ module MiniMagick
100
235
  end
101
236
  end
102
237
 
238
+ # Collapse images with sequences to the first frame (ie. animated gifs) and
239
+ # preserve quality
240
+ def collapse!
241
+ run_command("mogrify", "-quality", "100", "#{path}[0]")
242
+ end
243
+
244
+ # Writes the temporary file out to either a file location (by passing in a String) or by
245
+ # passing in a Stream that you can #write(chunk) to repeatedly
246
+ #
247
+ # @param output_to [IOStream, String] Some kind of stream object that needs to be read or a file path as a String
248
+ # @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.
103
249
  # Writes the temporary image that we are using for processing to the output path
104
- def write(output_path)
105
- FileUtils.copy_file @path, output_path
106
- run_command "identify", output_path # Verify that we have a good image
250
+ def write(output_to)
251
+ if output_to.kind_of?(String) || !output_to.respond_to?(:write)
252
+ FileUtils.copy_file @path, output_to
253
+ run_command "identify", output_to # Verify that we have a good image
254
+ else # stream
255
+ File.open(@path, "rb") do |f|
256
+ f.binmode
257
+ while chunk = f.read(8192)
258
+ output_to.write(chunk)
259
+ end
260
+ end
261
+ output_to
262
+ end
107
263
  end
108
264
 
109
- # Give you raw data back
265
+ # Gives you raw image data back
266
+ # @return [String] binary string
110
267
  def to_blob
111
268
  f = File.new @path
112
269
  f.binmode
@@ -118,49 +275,105 @@ module MiniMagick
118
275
  # If an unknown method is called then it is sent through the morgrify program
119
276
  # Look here to find all the commands (http://www.imagemagick.org/script/mogrify.php)
120
277
  def method_missing(symbol, *args)
121
- args.push(@path) # push the path onto the end
122
- run_command("mogrify", "-#{symbol}", *args)
123
- self
278
+ combine_options do |c|
279
+ c.method_missing(symbol, *args)
280
+ end
124
281
  end
125
282
 
126
- # You can use multiple commands together using this method
283
+ # You can use multiple commands together using this method. Very easy to use!
284
+ #
285
+ # @example
286
+ # image.combine_options do |c|
287
+ # c.draw "image Over 0,0 10,10 '#{MINUS_IMAGE_PATH}'"
288
+ # c.thumbnail "300x500>"
289
+ # c.background background
290
+ # end
291
+ #
292
+ # @yieldparam command [CommandBuilder]
127
293
  def combine_options(&block)
128
- c = CommandBuilder.new
129
- block.call c
130
- run_command("mogrify", *c.args << @path)
294
+ c = CommandBuilder.new('mogrify')
295
+ block.call(c)
296
+ c << @path
297
+ run(c)
131
298
  end
132
299
 
133
300
  # Check to see if we are running on win32 -- we need to escape things differently
134
301
  def windows?
135
- !(RUBY_PLATFORM =~ /win32/).nil?
302
+ !(RUBY_PLATFORM =~ /win32|mswin|mingw/).nil?
303
+ end
304
+
305
+ def composite(other_image, output_extension = 'jpg', &block)
306
+ begin
307
+ second_tempfile = Tempfile.new(output_extension)
308
+ second_tempfile.binmode
309
+ ensure
310
+ second_tempfile.close
311
+ end
312
+
313
+ command = CommandBuilder.new("composite")
314
+ block.call(command) if block
315
+ command.push(other_image.path)
316
+ command.push(self.path)
317
+ command.push(second_tempfile.path)
318
+
319
+ run(command)
320
+ return Image.new(second_tempfile.path, second_tempfile)
136
321
  end
137
322
 
138
323
  # Outputs a carriage-return delimited format string for Unix and Windows
139
324
  def format_option(format)
140
- windows? ? "#{format}\\n" : "#{format}\\\\n"
325
+ windows? ? "\"#{format}\\n\"" : "\"#{format}\\\\n\""
141
326
  end
142
327
 
143
328
  def run_command(command, *args)
144
- args.collect! do |arg|
145
- # args can contain characters like '>' so we must escape them, but don't quote switches
146
- if arg !~ /^[\+\-]/
147
- "\"#{arg}\""
148
- else
149
- arg.to_s
150
- end
329
+ # -ping "efficiently determine image characteristics."
330
+ if command == 'identify'
331
+ args.unshift '-ping'
151
332
  end
152
333
 
153
- command = "#{command} #{args.join(' ')}"
154
- output = `#{command} 2>&1`
334
+ run(CommandBuilder.new(command, *args))
335
+ end
336
+
337
+ def run(command_builder)
338
+ command = command_builder.command
155
339
 
156
- if $?.exitstatus != 0
157
- raise MiniMagickError, "ImageMagick command (#{command.inspect}) failed: #{{:status_code => $?, :output => output}.inspect}"
340
+ sub = Subexec.run(command, :timeout => MiniMagick.timeout)
341
+
342
+ if sub.exitstatus != 0
343
+ # Clean up after ourselves in case of an error
344
+ destroy!
345
+
346
+ # Raise the appropriate error
347
+ if sub.output =~ /no decode delegate/i || sub.output =~ /did not return an image/i
348
+ raise Invalid, sub.output
349
+ else
350
+ # TODO: should we do something different if the command times out ...?
351
+ # its definitely better for logging.. otherwise we dont really know
352
+ raise Error, "Command (#{command.inspect.gsub("\\", "")}) failed: #{{:status_code => sub.exitstatus, :output => sub.output}.inspect}"
353
+ end
158
354
  else
159
- output
355
+ sub.output
160
356
  end
161
357
  end
162
- end
163
358
 
359
+ def destroy!
360
+ return if @tempfile.nil?
361
+ File.unlink(@tempfile.path)
362
+ @tempfile = nil
363
+ end
364
+
365
+ private
366
+ # Sometimes we get back a list of character values
367
+ def read_character_data(list_of_characters)
368
+ chars = list_of_characters.gsub(" ", "").split(",")
369
+ result = ""
370
+ chars.each do |val|
371
+ result << ("%c" % val.to_i)
372
+ end
373
+ result
374
+ end
375
+ end
376
+
164
377
  # Combines multiple images into a single montage via ImageMagick's montage script
165
378
  class Montage
166
379
  # Class Methods
@@ -181,7 +394,7 @@ module MiniMagick
181
394
  # The 'montage' script has several options, see here: http://www.imagemagick.org/script/montage.php
182
395
  def self.new(images, output_extension, options={})
183
396
  begin
184
- tempfile = ImageTempFile.new(output_extension)
397
+ tempfile = TempFile.new(output_extension)
185
398
  tempfile.binmode
186
399
  ensure
187
400
  tempfile.close
@@ -206,18 +419,45 @@ module MiniMagick
206
419
 
207
420
  class CommandBuilder
208
421
  attr :args
422
+ attr :command
209
423
 
210
- def initialize
424
+ def initialize(command, *options)
425
+ @command = command
211
426
  @args = []
427
+ options.each { |arg| push(arg) }
212
428
  end
213
429
 
214
- def method_missing(symbol, *args)
215
- @args << "-#{symbol}"
216
- @args += args
430
+ def command
431
+ "#{MiniMagick.processor} #{@command} #{@args.join(' ')}".strip
432
+ end
433
+
434
+ def method_missing(symbol, *options)
435
+ guessed_command_name = symbol.to_s.gsub('_','-')
436
+ if guessed_command_name == "format"
437
+ raise Error, "You must call 'format' on the image object directly!"
438
+ elsif MOGRIFY_COMMANDS.include?(guessed_command_name)
439
+ add(guessed_command_name, *options)
440
+ else
441
+ super(symbol, *args)
442
+ end
443
+ end
444
+
445
+ def add(command, *options)
446
+ push "-#{command}"
447
+ if options.any?
448
+ push "\"#{options.join(" ")}\""
449
+ end
450
+ end
451
+
452
+ def push(arg)
453
+ @args << arg.to_s.strip
217
454
  end
455
+ alias :<< :push
218
456
 
457
+ # @deprecated Please don't use the + method its has been deprecated
219
458
  def +(value)
220
- @args << "+#{value}"
459
+ warn "Warning: The MiniMagick::ComandBuilder#+ command has been deprecated. Please use c << '+#{value}' instead"
460
+ push "+#{value}"
221
461
  end
222
462
  end
223
463
  end