mini_magick 3.8.1 → 4.0.0.rc

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.

Potentially problematic release.


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

Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mini_gmagick.rb +2 -1
  3. data/lib/mini_magick.rb +43 -65
  4. data/lib/mini_magick/configuration.rb +136 -0
  5. data/lib/mini_magick/image.rb +356 -336
  6. data/lib/mini_magick/image/info.rb +104 -0
  7. data/lib/mini_magick/logger.rb +40 -0
  8. data/lib/mini_magick/shell.rb +46 -0
  9. data/lib/mini_magick/tool.rb +233 -0
  10. data/lib/mini_magick/tool/animate.rb +14 -0
  11. data/lib/mini_magick/tool/compare.rb +14 -0
  12. data/lib/mini_magick/tool/composite.rb +14 -0
  13. data/lib/mini_magick/tool/conjure.rb +14 -0
  14. data/lib/mini_magick/tool/convert.rb +14 -0
  15. data/lib/mini_magick/tool/display.rb +14 -0
  16. data/lib/mini_magick/tool/identify.rb +14 -0
  17. data/lib/mini_magick/tool/import.rb +14 -0
  18. data/lib/mini_magick/tool/mogrify.rb +14 -0
  19. data/lib/mini_magick/tool/montage.rb +14 -0
  20. data/lib/mini_magick/tool/stream.rb +14 -0
  21. data/lib/mini_magick/utilities.rb +23 -50
  22. data/lib/mini_magick/version.rb +6 -6
  23. data/spec/fixtures/animation.gif +0 -0
  24. data/spec/fixtures/default.jpg +0 -0
  25. data/spec/fixtures/exif.jpg +0 -0
  26. data/spec/fixtures/image.psd +0 -0
  27. data/spec/fixtures/not_an_image.rb +1 -0
  28. data/spec/lib/mini_magick/configuration_spec.rb +66 -0
  29. data/spec/lib/mini_magick/image_spec.rb +318 -410
  30. data/spec/lib/mini_magick/shell_spec.rb +66 -0
  31. data/spec/lib/mini_magick/tool_spec.rb +90 -0
  32. data/spec/lib/mini_magick/utilities_spec.rb +17 -0
  33. data/spec/lib/mini_magick_spec.rb +23 -47
  34. data/spec/spec_helper.rb +17 -25
  35. data/spec/support/helpers.rb +37 -0
  36. metadata +42 -76
  37. data/lib/mini_magick/command_builder.rb +0 -94
  38. data/lib/mini_magick/errors.rb +0 -4
  39. data/spec/files/actually_a_gif.jpg +0 -0
  40. data/spec/files/animation.gif +0 -0
  41. data/spec/files/composited.jpg +0 -0
  42. data/spec/files/erroneous.jpg +0 -0
  43. data/spec/files/layers.psd +0 -0
  44. data/spec/files/leaves (spaced).tiff +0 -0
  45. data/spec/files/not_an_image.php +0 -1
  46. data/spec/files/png.png +0 -0
  47. data/spec/files/simple-minus.gif +0 -0
  48. data/spec/files/simple.gif +0 -0
  49. data/spec/files/trogdor.jpg +0 -0
  50. data/spec/files/trogdor_capitalized.JPG +0 -0
  51. data/spec/lib/mini_magick/command_builder_spec.rb +0 -153
@@ -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
9
+ super("composite")
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
9
+ super("conjure")
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
9
+ super("convert")
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
9
+ super("display")
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
9
+ super("identify")
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
9
+ super("import")
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
9
+ super("mogrify")
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/montage.php
5
+ #
6
+ class Montage < MiniMagick::Tool
7
+
8
+ def initialize
9
+ super("montage")
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
9
+ super("stream")
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -1,62 +1,35 @@
1
- require 'rbconfig'
2
- require 'shellwords'
3
- require 'pathname'
1
+ require "tempfile"
4
2
 
5
3
  module MiniMagick
4
+ # @private
6
5
  module Utilities
7
- class << self
8
- # Cross-platform way of finding an executable in the $PATH.
9
- #
10
- # which('ruby') #=> /usr/bin/ruby
11
- def which(cmd)
12
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
13
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
14
- exts.each do |ext|
15
- exe = File.join(path, "#{cmd}#{ext}")
16
- return exe if File.executable? exe
17
- end
18
- end
19
- nil
20
- end
21
6
 
22
- # Finds out if the host OS is windows
23
- def windows?
24
- RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
25
- end
7
+ module_function
26
8
 
27
- def escape(value)
28
- if windows?
29
- windows_escape(value)
30
- else
31
- shell_escape(value)
32
- end
33
- end
34
-
35
- def shell_escape(value)
36
- Shellwords.escape(value)
37
- end
38
-
39
- def windows_escape(value)
40
- # For Windows, ^ is the escape char, equivalent to \ in Unix.
41
- escaped = value.gsub(/\^/, '^^').gsub(/>/, '^>')
42
- if escaped !~ /^".+"$/ && escaped.include?("'")
43
- escaped.inspect
44
- else
45
- escaped
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['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
46
21
  end
47
22
  end
23
+ nil
24
+ end
48
25
 
49
- def path(path)
50
- if windows?
51
- # For Windows, if a path contains space char, you need to quote it,
52
- # otherwise you SHOULD NOT quote it. If you quote a path that does
53
- # not contains space, it will not work.
54
- pathname = Pathname.new(path).to_s
55
- path.include?(' ') ? pathname.inspect : pathname
56
- else
57
- path
58
- end
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
59
31
  end
60
32
  end
33
+
61
34
  end
62
35
  end
@@ -1,16 +1,16 @@
1
1
  module MiniMagick
2
2
  ##
3
- # Returns the version of the currently loaded MiniMagick as
4
- # a <tt>Gem::Version</tt>.
3
+ # @return [Gem::Version]
4
+ #
5
5
  def self.version
6
6
  Gem::Version.new VERSION::STRING
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 3
11
- MINOR = 8
12
- TINY = 1
13
- PRE = nil
10
+ MAJOR = 4
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "rc"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
16
16
  end
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ expect(__FILE__).not_to be_an_image
@@ -0,0 +1,66 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe MiniMagick::Configuration do
4
+ subject { Object.new.extend(MiniMagick::Configuration) }
5
+
6
+ describe "#configure" do
7
+ it "yields self" do
8
+ expect { |b| subject.configure(&b) }
9
+ .to yield_with_args(subject)
10
+ end
11
+ end
12
+
13
+ describe "#cli" do
14
+ it "can be assigned" do
15
+ subject.cli = :imagemagick
16
+ expect(subject.cli).to eq :imagemagick
17
+ end
18
+
19
+ it "returns :imagemagick if #processor is mogrify" do
20
+ allow(subject).to receive(:processor).and_return("mogrify")
21
+ expect(subject.cli).to eq :imagemagick
22
+ end
23
+
24
+ it "returns :graphicsmagick if #processor is gm" do
25
+ allow(subject).to receive(:processor).and_return("gm")
26
+ expect(subject.cli).to eq :graphicsmagick
27
+ end
28
+
29
+ it "returns nil of #processor is nil" do
30
+ allow(subject).to receive(:processor).and_return(nil)
31
+ expect(subject.cli).to eq nil
32
+ end
33
+ end
34
+
35
+ describe "#cli=" do
36
+ it "raises an error when set to an invalid value" do
37
+ expect { subject.cli = :grapicsmagick }
38
+ .to raise_error(ArgumentError)
39
+ end
40
+ end
41
+
42
+ describe "#processor" do
43
+ it "assigns :mogrify by default" do
44
+ expect(subject.processor).to eq "mogrify"
45
+ end
46
+
47
+ it "assigns :gm if ImageMagick is not available" do
48
+ allow(MiniMagick::Utilities).to receive(:which).with("mogrify").and_return(nil)
49
+ allow(MiniMagick::Utilities).to receive(:which).with("gm").and_return(true)
50
+ expect(subject.processor).to eq "gm"
51
+ end
52
+
53
+ it "returns nil if neither ImageMagick nor GraphicsMagick are available" do
54
+ allow(MiniMagick::Utilities).to receive(:which).with("mogrify").and_return(nil)
55
+ allow(MiniMagick::Utilities).to receive(:which).with("gm").and_return(nil)
56
+ expect(subject.processor).to eq nil
57
+ end
58
+ end
59
+
60
+ describe "#processor=" do
61
+ it "raises an error when set to an invalid value" do
62
+ expect { subject.processor = "mogrfy" }
63
+ .to raise_error(ArgumentError)
64
+ end
65
+ end
66
+ end
@@ -1,499 +1,407 @@
1
- require 'spec_helper'
2
- require 'pathname'
3
- require 'tempfile'
4
-
5
- MiniMagick.processor = :mogrify
6
-
7
- describe MiniMagick::Image do
8
- context 'when ImageMagick and GraphicsMagick are both unavailable' do
9
- before do
10
- MiniMagick::Utilities.expects(:which).at_least_once.returns(nil)
11
- MiniMagick.instance_variable_set(:@processor, nil)
12
- @old_path = ENV['PATH']
13
- ENV['PATH'] = ''
14
- end
15
-
16
- after do
17
- ENV['PATH'] = @old_path
18
- end
1
+ require "spec_helper"
2
+ require "pathname"
3
+ require "tempfile"
4
+ require "fileutils"
5
+ require "stringio"
6
+
7
+ ["ImageMagick", "GraphicsMagick"].each do |cli|
8
+ RSpec.context "With #{cli}", cli: cli.downcase.to_sym do
9
+ describe MiniMagick::Image do
10
+ subject { described_class.open(image_path) }
11
+
12
+ describe ".read" do
13
+ it "reads image from String" do
14
+ string = File.binread(image_path)
15
+ image = described_class.read(string)
16
+ expect(image).to be_valid
17
+ end
19
18
 
20
- it "raises an exception with 'No such file' in the message" do
21
- begin
22
- described_class.open(SIMPLE_IMAGE_PATH)
23
- rescue => e
24
- expect(e.message).to match(/(No such file|not found)/)
25
- end
26
- end
27
- end
19
+ it "reads image from StringIO" do
20
+ stringio = StringIO.new(File.binread(image_path))
21
+ image = described_class.read(stringio)
22
+ expect(image).to be_valid
23
+ end
28
24
 
29
- describe 'ported from testunit', :ported => true do
30
- it 'reads image from blob' do
31
- File.open(SIMPLE_IMAGE_PATH, 'rb') do |f|
32
- image = described_class.read(f.read)
33
- expect(image).to be_valid
34
- image.destroy!
25
+ it "reads image from tempfile" do
26
+ tempfile = Tempfile.open('magick')
27
+ FileUtils.cp image_path, tempfile.path
28
+ image = described_class.read(tempfile)
29
+ expect(image).to be_valid
30
+ end
35
31
  end
36
- end
37
32
 
38
- it 'reads image from tempfile', :if => !MiniMagick::Utilities.windows? do
39
- tempfile = Tempfile.new('magick')
33
+ describe ".import_pixels" do
34
+ let(:dimensions) { [325, 200] }
35
+ let(:depth) { 16 } # 16 bits (2 bytes) per pixel
36
+ let(:map) { 'gray' }
37
+ let(:pixels) { Array.new(dimensions.inject(:*)) { |i| i } }
38
+ let(:blob) { pixels.pack('S*') } # unsigned short, native byte order
40
39
 
41
- File.open(SIMPLE_IMAGE_PATH, 'rb') do |f|
42
- tempfile.write(f.read)
43
- tempfile.rewind
44
- end
40
+ it "can import pixels with default format" do
41
+ image = described_class.import_pixels(blob, *dimensions, depth, map)
45
42
 
46
- image = described_class.read(tempfile)
47
- expect(image).to be_valid
48
- image.destroy!
49
- end
43
+ expect(image).to be_valid
44
+ expect(image.type).to eq 'PNG'
45
+ expect(image.dimensions).to eq dimensions
46
+ end
50
47
 
51
- # from https://github.com/minimagick/minimagick/issues/163
52
- it 'annotates image with whitespace' do
53
- image = described_class.open(SIMPLE_IMAGE_PATH)
48
+ it "can import pixels with custom format" do
49
+ image = described_class.import_pixels(blob, *dimensions, depth, map, 'jpeg')
54
50
 
55
- expect {
56
- message = 'a b'
51
+ expect(image).to be_valid
52
+ expect(image.type).to eq 'JPEG'
53
+ expect(image.dimensions).to eq dimensions
54
+ end
55
+ end
57
56
 
58
- image.combine_options do |c|
59
- c.gravity 'SouthWest'
60
- c.fill 'white'
61
- c.stroke 'black'
62
- c.strokewidth '2'
63
- c.pointsize '48'
64
- c.interline_spacing '-9'
65
- c.annotate '0', message
57
+ describe ".open" do
58
+ it "makes a copy of the image" do
59
+ image = described_class.open(image_path)
60
+ expect(image.path).not_to eq image_path
61
+ expect(image).to be_valid
66
62
  end
67
- }.to_not raise_error
68
- end
69
63
 
70
- it 'opens image' do
71
- image = described_class.open(SIMPLE_IMAGE_PATH)
72
- expect(image).to be_valid
73
- image.destroy!
74
- end
64
+ it "accepts a Pathname" do
65
+ image = described_class.open(Pathname(image_path))
66
+ expect(image).to be_valid
67
+ end
75
68
 
76
- it 'reads image from buffer' do
77
- buffer = StringIO.new File.open(SIMPLE_IMAGE_PATH, 'rb') { |f| f.read }
78
- image = described_class.read(buffer)
79
- expect(image).to be_valid
80
- image.destroy!
81
- end
69
+ it "loads a remote image" do
70
+ begin
71
+ image = described_class.open(image_url)
72
+ expect(image).to be_valid
73
+ rescue SocketError
74
+ end
75
+ end
82
76
 
83
- describe '.create' do
84
- subject(:create) do
85
- described_class.create do |f|
86
- # Had to replace the old File.read with the following to work across all platforms
87
- f.write(File.open(SIMPLE_IMAGE_PATH, 'rb') { |fi| fi.read })
77
+ it "validates the image" do
78
+ expect { described_class.open(image_path(:not)) }
79
+ .to raise_error(MiniMagick::Invalid)
88
80
  end
89
81
  end
90
82
 
91
- it 'creates an image' do
92
- expect {
93
- image = create
94
- image.destroy!
95
- }.to_not raise_error
96
- end
83
+ describe ".create" do
84
+ def create(path = image_path)
85
+ described_class.create do |f|
86
+ f.write(File.binread(path))
87
+ end
88
+ end
97
89
 
98
- describe 'validation' do
99
- before do
100
- @old_validate = MiniMagick.validate_on_create
101
- MiniMagick.validate_on_create = validate
90
+ it "creates an image" do
91
+ image = create
92
+ expect(File.exists?(image.path)).to eq true
102
93
  end
103
94
 
104
- context 'MiniMagick.validate_on_create = true' do
105
- let(:validate) { true }
95
+ it "validates the image if validation is set" do
96
+ allow(MiniMagick).to receive(:validate_on_create).and_return(true)
97
+ expect { create(image_path(:not)) }
98
+ .to raise_error(MiniMagick::Invalid)
99
+ end
106
100
 
107
- it 'validates image' do
108
- described_class.any_instance.expects(:valid?).returns(true)
109
- create
110
- end
101
+ it "doesn't validate image if validation is disabled" do
102
+ allow(MiniMagick).to receive(:validate_on_create).and_return(false)
103
+ expect { create(image_path(:not)) }
104
+ .not_to raise_error
111
105
  end
106
+ end
112
107
 
113
- context 'MiniMagick.validate_on_create = false' do
114
- let(:validate) { false }
108
+ describe "#initialize" do
109
+ it "initializes a new image" do
110
+ image = described_class.new(image_path)
111
+ expect(image).to be_valid
112
+ end
115
113
 
116
- it 'skips validation' do
117
- described_class.any_instance.expects(:valid?).never
118
- create
114
+ it "accepts a block which it passes on to #combine_options" do
115
+ image = described_class.new(subject.path) do |b|
116
+ b.resize "100x100!"
119
117
  end
118
+ expect(image.dimensions).to eq [100, 100]
120
119
  end
121
-
122
- after { MiniMagick.validate_on_create = @old_validate }
123
120
  end
124
- end
125
121
 
126
- it 'loads a new image' do
127
- expect {
128
- image = described_class.new(SIMPLE_IMAGE_PATH)
129
- image.destroy!
130
- }.to_not raise_error
131
- end
122
+ describe "#format" do
123
+ subject { described_class.open(image_path(:jpg)) }
132
124
 
133
- it 'loads remote image' do
134
- image = described_class.open('http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png')
135
- expect(image).to be_valid
136
- image.destroy!
137
- end
125
+ it "changes the format of the photo" do
126
+ expect { subject.format("png") }
127
+ .to change { subject.type }
128
+ end
138
129
 
139
- it 'loads remote image with complex url' do
140
- image = described_class.open(
141
- 'http://a0.twimg.com/a/1296609216/images/fronts/logo_withbird_home.png?extra=foo&plus=bar'
142
- )
143
- expect(image).to be_valid
144
- image.destroy!
145
- end
130
+ it "reformats an image with a given extension" do
131
+ expect { subject.format('png') }
132
+ .to change { File.extname(subject.path) }.to ".png"
133
+ end
146
134
 
147
- it 'reformats an image with a given extension' do
148
- expect {
149
- image = described_class.open(CAP_EXT_PATH)
150
- image.format 'jpg'
151
- }.to_not raise_error
152
- end
135
+ it "creates the file with new extension" do
136
+ subject.format('png')
137
+ expect(File.exist?(subject.path)).to eq true
138
+ end
153
139
 
154
- describe '#write' do
155
- it 'reformats a PSD with a given a extension and all layers' do
156
- expect {
157
- image = described_class.open(PSD_IMAGE_PATH)
158
- image.format('jpg', nil)
159
- }.to_not raise_error
160
- end
140
+ it "accepts a block of additional commands" do
141
+ expect {
142
+ subject.format("png") do |b|
143
+ b.resize("100x100!")
144
+ end
145
+ }.to change { subject.dimensions }.to [100, 100]
146
+ end
161
147
 
162
- it 'opens and writes an image' do
163
- output_path = 'output.gif'
164
- begin
165
- image = described_class.new(SIMPLE_IMAGE_PATH)
166
- image.write output_path
167
- expect(File.exist?(output_path)).to be(true)
168
- ensure
169
- File.delete output_path
148
+ it "works without an extension" do
149
+ subject = described_class.open(image_path(:without_extension))
150
+ expect { subject.format("png") }
151
+ .to change { File.extname(subject.path) }.from("").to(".png")
170
152
  end
171
- image.destroy!
172
- end
173
153
 
174
- it 'opens and writes an image with space in its filename' do
175
- output_path = 'test output.gif'
176
- begin
177
- image = described_class.new(SIMPLE_IMAGE_PATH)
178
- image.write output_path
154
+ it "deletes the previous tempfile" do
155
+ old_path = subject.path.dup
156
+ subject.format('png')
157
+ expect(File.exist?(old_path)).to eq false
158
+ end
179
159
 
180
- expect(File.exist?(output_path)).to be(true)
181
- ensure
182
- File.delete output_path
160
+ it "doesn't delete itself when formatted to the same format" do
161
+ subject.format(subject.type.downcase)
162
+ expect(File.exists?(subject.path)).to eq true
183
163
  end
184
- image.destroy!
185
- end
186
164
 
187
- it 'writes an image with stream' do
188
- stream = StringIO.new
189
- image = described_class.open(SIMPLE_IMAGE_PATH)
190
- image.write("#{Dir.tmpdir}/foo.gif")
191
- image.write(stream)
192
- expect(described_class.read(stream.string)).to be_valid
193
- image.destroy!
194
- end
165
+ it "reformats multi-image formats to multiple images" do
166
+ subject = described_class.open(image_path(:animation))
167
+ subject.format('jpg', nil)
168
+ expect(Dir[subject.path.sub('.', '*.')]).not_to be_empty
169
+ end
195
170
 
196
- describe 'validation' do
197
- let(:image) { described_class.new(SIMPLE_IMAGE_PATH) }
198
- let(:output_path) { 'output.gif' }
171
+ it "reformats multi-image formats to a single image" do
172
+ subject = described_class.open(image_path(:animation))
173
+ subject.format('jpg')
174
+ expect(subject).to be_valid
175
+ end
199
176
 
200
- before do
201
- @old_validate = MiniMagick.validate_on_write
202
- MiniMagick.validate_on_write = validate
177
+ it "returns self" do
178
+ expect(subject.format('png')).to eq subject
203
179
  end
180
+ end
204
181
 
205
- subject(:write) { image.write output_path }
182
+ describe "#write" do
183
+ it "writes the image" do
184
+ output_path = random_path("test output")
185
+ subject.write(output_path)
186
+ expect(described_class.new(output_path)).to be_valid
187
+ end
206
188
 
207
- context 'MiniMagick.validate_on_write = true' do
208
- let(:validate) { true }
189
+ it "writes an image with stream" do
190
+ output_stream = StringIO.new
191
+ subject.write(output_stream)
192
+ expect(described_class.read(output_stream.string)).to be_valid
193
+ end
209
194
 
210
- it 'runs post-validation' do
211
- image.expects(:run_command).with('identify', output_path)
212
- write
213
- end
195
+ it "writes layers" do
196
+ output_path = random_path(["", ".#{subject.type.downcase}"])
197
+ subject = described_class.new(image_path(:gif))
198
+ subject.frames.first.write(output_path)
199
+ expect(described_class.new(output_path)).to be_valid
214
200
  end
215
201
 
216
- context 'MiniMagick.validate_on_write = false' do
217
- let(:validate) { false }
202
+ it "accepts a Pathname" do
203
+ output_path = Pathname(random_path)
204
+ subject.write(output_path)
205
+ expect(described_class.new(output_path.to_s)).to be_valid
206
+ end
207
+ end
218
208
 
219
- it 'runs post-validation' do
220
- image.expects(:run_command).never
221
- write
222
- end
209
+ describe "#valid?" do
210
+ it "returns true when image is valid" do
211
+ image = described_class.new(image_path)
212
+ expect(image).to be_valid
223
213
  end
224
214
 
225
- after do
226
- image.destroy!
227
- File.delete output_path
228
- MiniMagick.validate_on_write = @old_validate
215
+ it "returns false when image is not valid" do
216
+ image = described_class.new(image_path(:not))
217
+ expect(image).not_to be_valid
229
218
  end
230
219
  end
231
- end
232
220
 
233
- it 'tells when an image is invalid' do
234
- image = described_class.new(NOT_AN_IMAGE_PATH)
235
- expect(image).not_to be_valid
236
- image.destroy!
237
- end
221
+ describe "#[]" do
222
+ it "inspects image meta info" do
223
+ expect(subject[:width]).to be_a(Fixnum)
224
+ expect(subject[:height]).to be_a(Fixnum)
225
+ expect(subject[:dimensions]).to all(be_a(Fixnum))
226
+ expect(subject[:colorspace]).to be_a(String)
227
+ expect(subject[:format]).to match(/[A-Z]/)
228
+ end
238
229
 
239
- it "raises error when opening a file that isn't an image" do
240
- expect {
241
- image = described_class.open(NOT_AN_IMAGE_PATH)
242
- image.destroy
243
- }.to raise_error(MiniMagick::Invalid)
244
- end
230
+ it "supports string keys" do
231
+ expect(subject["width"]).to be_a(Fixnum)
232
+ expect(subject["height"]).to be_a(Fixnum)
233
+ expect(subject["dimensions"]).to all(be_a(Fixnum))
234
+ expect(subject["colorspace"]).to be_a(String)
235
+ expect(subject["format"]).to match(/[A-Z]/)
236
+ end
245
237
 
246
- it "raises error when imagemagick raised an error during processing" do
247
- image = described_class.open(SIMPLE_IMAGE_PATH)
248
- image.rotate "invalid_value"
249
- expect { image.run_queue }.to raise_error(MiniMagick::Error)
250
- end
238
+ it "reads exif" do
239
+ subject = described_class.new(image_path(:exif))
240
+ expect(subject["EXIF:ColorSpace"]).to eq "1"
241
+ end
251
242
 
252
- it 'inspects image meta info' do
253
- image = described_class.new(SIMPLE_IMAGE_PATH)
254
- expect(image[:width]).to be(150)
255
- expect(image[:height]).to be(55)
256
- expect(image[:dimensions]).to match_array [150, 55]
257
- expect(image[:colorspace]).to be_an_instance_of(String)
258
- expect(image[:format]).to match(/^gif$/i)
259
- image.destroy!
260
- end
243
+ it "passes unknown values directly to -format" do
244
+ expect(subject["%w %h"].split.map(&:to_i)).to eq [subject[:width], subject[:height]]
245
+ end
246
+ end
261
247
 
262
- it 'supports string keys for dimension attributes' do
263
- image = described_class.new(SIMPLE_IMAGE_PATH)
264
- expect(image["width"]).to be(150)
265
- expect(image["height"]).to be(55)
266
- expect(image["dimensions"]).to match_array [150, 55]
267
- image.destroy!
268
- end
248
+ it "has attributes" do
249
+ expect(subject.type).to match(/^[A-Z]+$/)
250
+ expect(subject.mime_type).to match(/^image\/[a-z]+$/)
251
+ expect(subject.width).to be_a(Fixnum).and be_nonzero
252
+ expect(subject.height).to be_a(Fixnum).and be_nonzero
253
+ expect(subject.dimensions).to all(be_a(Fixnum))
254
+ expect(subject.size).to be_a(Fixnum).and be_nonzero
255
+ expect(subject.colorspace).to be_a(String)
256
+ expect(subject.resolution).to all(be_a(Fixnum))
257
+ end
269
258
 
270
- it 'inspects an erroneus image meta info' do
271
- image = described_class.new(ERRONEOUS_IMAGE_PATH)
272
- expect(image[:width]).to be(10)
273
- expect(image[:height]).to be(10)
274
- expect(image[:dimensions]).to match_array [10, 10]
275
- expect(image[:format]).to eq 'JPEG'
276
- image.destroy!
277
- end
259
+ describe "#exif" do
260
+ subject { described_class.new(image_path(:exif)) }
278
261
 
279
- it 'inspects meta info from tiff images' do
280
- image = described_class.new(TIFF_IMAGE_PATH)
281
- expect(image[:format].to_s.downcase).to eq 'tiff'
282
- expect(image[:width]).to be(50)
283
- expect(image[:height]).to be(41)
284
- image.destroy!
285
- end
262
+ it "returns a hash of EXIF data" do
263
+ expect(subject.exif["DateTimeOriginal"]).to be_a(String)
264
+ end
265
+ end
286
266
 
287
- it 'inspects a gif with jpg format correctly' do
288
- image = described_class.new(GIF_WITH_JPG_EXT)
289
- expect(image[:format].to_s.downcase).to eq 'gif'
290
- image.destroy!
291
- end
267
+ describe "#resolution" do
268
+ it "accepts units", skip_cli: :graphicsmagick do
269
+ expect(subject.resolution("PixelsPerCentimeter"))
270
+ .not_to eq subject.resolution("PixelsPerInch")
271
+ end
272
+ end
292
273
 
293
- it 'resizes an image correctly' do
294
- image = described_class.open(SIMPLE_IMAGE_PATH)
295
- image.resize '20x30!'
274
+ describe "#mime_type" do
275
+ it "returns the correct mime type" do
276
+ jpg = described_class.new(image_path(:jpg))
277
+ expect(jpg.mime_type).to eq 'image/jpeg'
278
+ end
279
+ end
296
280
 
297
- expect(image[:width]).to be(20)
298
- expect(image[:height]).to be(30)
299
- expect(image[:format]).to match(/^gif$/i)
300
- image.destroy!
301
- end
281
+ describe "#layers" do
282
+ it "returns a list of images" do
283
+ expect(subject.layers).to all(be_a(MiniMagick::Image))
284
+ expect(subject.layers.first).to be_valid
285
+ end
302
286
 
303
- it 'resizes an image with minimum dimensions' do
304
- image = described_class.open(SIMPLE_IMAGE_PATH)
305
- original_width, original_height = image[:width], image[:height]
306
- image.resize "#{original_width + 10}x#{original_height + 10}>"
287
+ it "returns multiple images for GIFs, PDFs and PSDs" do
288
+ gif = described_class.new(image_path(:gif))
289
+ psd = described_class.new(image_path(:psd))
307
290
 
308
- expect(image[:width]).to be original_width
309
- expect(image[:height]).to be original_height
310
- image.destroy!
311
- end
291
+ expect(gif.frames.count).to be > 1
292
+ expect(psd.layers.count).to be > 1 unless MiniMagick.graphicsmagick?
293
+ end
312
294
 
313
- it 'combines options to create an image with resize and blur' do
314
- image = described_class.open(SIMPLE_IMAGE_PATH)
315
- image.combine_options do |c|
316
- c.resize '20x30!'
317
- c.blur '50'
318
- end
295
+ it "returns one image for other formats" do
296
+ jpg = described_class.new(image_path(:jpg))
319
297
 
320
- expect(image[:width]).to be(20)
321
- expect(image[:height]).to be(30)
322
- expect(image[:format]).to match(/\Agif\z/i)
323
- image.destroy!
324
- end
298
+ expect(jpg.layers.count).to eq 1
299
+ end
300
+ end
325
301
 
326
- it "combines options to create an image even with minuses symbols on it's name it" do
327
- image = described_class.open(SIMPLE_IMAGE_PATH)
328
- background = '#000000'
329
- expect {
330
- image.combine_options do |c|
331
- c.draw "image Over 0,0 10,10 '#{MINUS_IMAGE_PATH}'"
332
- c.thumbnail '300x500>'
333
- c.background background
334
- end
335
- }.to_not raise_error
336
- image.destroy!
337
- end
302
+ describe "#method_missing" do
303
+ it "executes the command correctly" do
304
+ expect { subject.resize '20x30!' }
305
+ .to change { subject.dimensions }.to [20, 30]
306
+ end
338
307
 
339
- it 'inspects the EXIF of an image' do
340
- image = described_class.open(EXIF_IMAGE_PATH)
341
- expect(image['exif:ExifVersion']).to eq '0220'
342
- image = described_class.open(SIMPLE_IMAGE_PATH)
343
- expect(image['EXIF:ExifVersion']).to be_empty
344
- image.destroy!
345
- end
308
+ it "fails with a correct NoMethodError" do
309
+ expect { subject.foo }
310
+ .to raise_error(NoMethodError, /MiniMagick::Image/)
311
+ end
346
312
 
347
- it 'inspects the original at of an image' do
348
- image = described_class.open(EXIF_IMAGE_PATH)
349
- expect(image[:original_at]).to eq Time.local('2005', '2', '23', '23', '17', '24')
350
- image = described_class.open(SIMPLE_IMAGE_PATH)
351
- expect(image[:original_at]).to be_nil
352
- image.destroy!
353
- end
313
+ it "returns self" do
314
+ expect(subject.resize('20x30!')).to eq subject
315
+ end
316
+ end
354
317
 
355
- it 'has the same path for tempfile and image' do
356
- image = described_class.open(TIFF_IMAGE_PATH)
357
- expect(image.instance_eval('@tempfile.path')).to eq image.path
358
- image.destroy!
359
- end
318
+ describe "#combine_options" do
319
+ it "chains multiple options and executes them in one command" do
320
+ expect {
321
+ subject.combine_options { |c| c.resize '20x30!' }
322
+ }.to change { subject.dimensions }.to [20, 30]
323
+ end
360
324
 
361
- it 'has the tempfile at path after format' do
362
- image = described_class.open(TIFF_IMAGE_PATH)
363
- image.format('png')
364
- expect(File.exist?(image.path)).to be(true)
365
- image.destroy!
366
- end
325
+ it "doesn't allow calling of #format" do
326
+ expect { subject.combine_options { |c| c.format("png") } }
327
+ .to raise_error(NoMethodError)
328
+ end
367
329
 
368
- it "hasn't previous tempfile at path after format" do
369
- image = described_class.open(TIFF_IMAGE_PATH)
370
- before = image.path.dup
371
- image.format('png')
372
- expect(File.exist?(before)).to be(false)
373
- image.destroy!
374
- end
330
+ it "returns self" do
331
+ expect(subject.combine_options {}).to eq subject
332
+ end
333
+ end
375
334
 
376
- it 'changes the format of image with special characters', :if => !MiniMagick::Utilities.windows? do
377
- tempfile = Tempfile.new('magick with special! "chars\'')
335
+ describe "#composite" do
336
+ let(:other_image) { described_class.open(image_path) }
337
+ let(:mask) { described_class.open(image_path) }
378
338
 
379
- File.open(SIMPLE_IMAGE_PATH, 'rb') do |file|
380
- tempfile.write(file.read)
381
- tempfile.rewind
382
- end
339
+ it "creates a composite of two images" do
340
+ image = subject.composite(other_image)
341
+ expect(image).to be_valid
342
+ end
383
343
 
384
- image = described_class.new(tempfile.path)
385
- image.format('png')
386
- expect(File.exist?(image.path)).to be(true)
387
- image.destroy!
344
+ it "creates a composite of two images with mask" do
345
+ image = subject.composite(other_image, 'jpg', mask)
346
+ expect(image).to be_valid
347
+ end
388
348
 
389
- File.delete(image.path)
390
- tempfile.unlink
391
- end
349
+ it "yields an optional block" do
350
+ expect { |b| subject.composite(other_image, &b) }
351
+ .to yield_with_args(an_instance_of(MiniMagick::Tool::Composite))
352
+ end
392
353
 
393
- it 'raises exception when calling wrong method' do
394
- image = described_class.open(TIFF_IMAGE_PATH)
395
- expect { image.to_blog }.to raise_error(NoMethodError)
396
- image.to_blob
397
- image.destroy!
398
- end
354
+ it "makes the composited image with the provided extension" do
355
+ result = subject.composite(other_image, 'png')
356
+ expect(result.path).to end_with ".png"
399
357
 
400
- it 'can create a composite of two images' do
401
- image = described_class.open(EXIF_IMAGE_PATH)
402
- result = image.composite(described_class.open(TIFF_IMAGE_PATH)) do |c|
403
- c.gravity 'center'
358
+ result = subject.composite(other_image)
359
+ expect(result.path).to end_with ".jpg"
360
+ end
404
361
  end
405
- expect(File.exist?(result.path)).to be(true)
406
- end
407
362
 
408
- # https://github.com/minimagick/minimagick/issues/212
409
- it 'can create a composite of two images with mask' do
410
- image = described_class.open(EXIF_IMAGE_PATH)
411
- result = image.composite(described_class.open(TIFF_IMAGE_PATH), 'jpg', described_class.open(PNG_PATH)) do |c|
412
- c.gravity 'center'
413
- end
414
- expect(File.exist?(result.path)).to be(true)
415
- end
363
+ describe "#collapse!" do
364
+ subject { described_class.open(image_path(:animation)) }
416
365
 
417
- # https://github.com/minimagick/minimagick/issues/8
418
- it 'has issue 8 fixed' do
419
- image = described_class.open(SIMPLE_IMAGE_PATH)
420
- expect {
421
- image.combine_options do |c|
422
- c.sample '50%'
423
- c.rotate '-90>'
366
+ it "collapses the image to one frame" do
367
+ subject.collapse!
368
+ expect(subject.identify.lines.count).to eq 1
424
369
  end
425
- }.to_not raise_error
426
- image.destroy!
427
- end
428
370
 
429
- # https://github.com/minimagick/minimagick/issues/8
430
- it 'has issue 15 fixed' do
431
- expect {
432
- image = described_class.open(Pathname.new(SIMPLE_IMAGE_PATH))
433
- output = Pathname.new('test.gif')
434
- image.write(output)
435
- }.to_not raise_error
436
- FileUtils.rm('test.gif')
437
- end
438
-
439
- # https://github.com/minimagick/minimagick/issues/37
440
- it 'respects the language set' do
441
- original_lang = ENV['LANG']
442
- ENV['LANG'] = 'fr_FR.UTF-8'
371
+ it "keeps the extension" do
372
+ expect { subject.collapse! }
373
+ .not_to change { subject.type }
374
+ end
443
375
 
444
- expect {
445
- image = described_class.open(NOT_AN_IMAGE_PATH)
446
- image.destroy
447
- }.to raise_error(MiniMagick::Invalid)
376
+ it "clears the info" do
377
+ expect { subject.collapse! }
378
+ .to change { subject.size }
379
+ end
448
380
 
449
- ENV['LANG'] = original_lang
450
- end
381
+ it "returns self" do
382
+ expect(subject.collapse!).to eq subject
383
+ end
384
+ end
451
385
 
452
- it 'can import pixels with default format' do
453
- columns = 325
454
- rows = 200
455
- depth = 16 # 16 bits (2 bytes) per pixel
456
- map = 'gray'
457
- pixels = Array.new(columns * rows) { |i| i }
458
- blob = pixels.pack('S*') # unsigned short, native byte order
459
- image = described_class.import_pixels(blob, columns, rows, depth, map)
460
-
461
- expect(image).to be_valid
462
- expect(image[:format].to_s.downcase).to eq 'png'
463
- expect(image[:width]).to eq columns
464
- expect(image[:height]).to eq rows
465
- image.write("#{Dir.tmpdir}/imported_pixels_image.png")
466
- end
386
+ describe "#identify" do
387
+ it "returns the output of identify" do
388
+ expect(subject.identify).to match(subject.type)
389
+ end
467
390
 
468
- it 'can import pixels with custom format' do
469
- columns = 325
470
- rows = 200
471
- depth = 16 # 16 bits (2 bytes) per pixel
472
- map = 'gray'
473
- format = 'jpeg'
474
- pixels = Array.new(columns * rows) { |i| i }
475
- blob = pixels.pack('S*') # unsigned short, native byte order
476
- image = described_class.import_pixels(blob, columns, rows, depth, map, format)
477
-
478
- expect(image).to be_valid
479
- expect(image[:format].to_s.downcase).to eq format
480
- expect(image[:width]).to eq columns
481
- expect(image[:height]).to eq rows
482
- image.write("#{Dir.tmpdir}/imported_pixels_image." + format)
483
- end
391
+ it "yields an optional block" do
392
+ output = subject.identify do |b|
393
+ b.verbose
394
+ end
395
+ expect(output).to match("Format:")
396
+ end
397
+ end
484
398
 
485
- it 'loads mimetype correctly' do
486
- gif = described_class.open(SIMPLE_IMAGE_PATH)
487
- jpeg = described_class.open(EXIF_IMAGE_PATH)
488
- png = described_class.open(PNG_PATH)
489
- tiff = described_class.open(TIFF_IMAGE_PATH)
490
- hidden_gif = described_class.open(GIF_WITH_JPG_EXT)
491
-
492
- expect(gif.mime_type).to eq 'image/gif'
493
- expect(jpeg.mime_type).to eq 'image/jpeg'
494
- expect(png.mime_type).to eq 'image/png'
495
- expect(tiff.mime_type).to eq 'image/tiff'
496
- expect(hidden_gif.mime_type).to eq 'image/gif'
399
+ describe "#run_command" do
400
+ it "runs the given command" do
401
+ output = subject.run_command("identify", "-format", "%w", subject.path)
402
+ expect(output).to eq subject.width.to_s
403
+ end
404
+ end
497
405
  end
498
406
  end
499
407
  end