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.
- checksums.yaml +4 -4
- data/lib/mini_gmagick.rb +2 -1
- data/lib/mini_magick.rb +43 -65
- data/lib/mini_magick/configuration.rb +136 -0
- data/lib/mini_magick/image.rb +356 -336
- data/lib/mini_magick/image/info.rb +104 -0
- data/lib/mini_magick/logger.rb +40 -0
- data/lib/mini_magick/shell.rb +46 -0
- data/lib/mini_magick/tool.rb +233 -0
- data/lib/mini_magick/tool/animate.rb +14 -0
- data/lib/mini_magick/tool/compare.rb +14 -0
- data/lib/mini_magick/tool/composite.rb +14 -0
- data/lib/mini_magick/tool/conjure.rb +14 -0
- data/lib/mini_magick/tool/convert.rb +14 -0
- data/lib/mini_magick/tool/display.rb +14 -0
- data/lib/mini_magick/tool/identify.rb +14 -0
- data/lib/mini_magick/tool/import.rb +14 -0
- data/lib/mini_magick/tool/mogrify.rb +14 -0
- data/lib/mini_magick/tool/montage.rb +14 -0
- data/lib/mini_magick/tool/stream.rb +14 -0
- data/lib/mini_magick/utilities.rb +23 -50
- data/lib/mini_magick/version.rb +6 -6
- data/spec/fixtures/animation.gif +0 -0
- data/spec/fixtures/default.jpg +0 -0
- data/spec/fixtures/exif.jpg +0 -0
- data/spec/fixtures/image.psd +0 -0
- data/spec/fixtures/not_an_image.rb +1 -0
- data/spec/lib/mini_magick/configuration_spec.rb +66 -0
- data/spec/lib/mini_magick/image_spec.rb +318 -410
- data/spec/lib/mini_magick/shell_spec.rb +66 -0
- data/spec/lib/mini_magick/tool_spec.rb +90 -0
- data/spec/lib/mini_magick/utilities_spec.rb +17 -0
- data/spec/lib/mini_magick_spec.rb +23 -47
- data/spec/spec_helper.rb +17 -25
- data/spec/support/helpers.rb +37 -0
- metadata +42 -76
- data/lib/mini_magick/command_builder.rb +0 -94
- data/lib/mini_magick/errors.rb +0 -4
- data/spec/files/actually_a_gif.jpg +0 -0
- data/spec/files/animation.gif +0 -0
- data/spec/files/composited.jpg +0 -0
- data/spec/files/erroneous.jpg +0 -0
- data/spec/files/layers.psd +0 -0
- data/spec/files/leaves (spaced).tiff +0 -0
- data/spec/files/not_an_image.php +0 -1
- data/spec/files/png.png +0 -0
- data/spec/files/simple-minus.gif +0 -0
- data/spec/files/simple.gif +0 -0
- data/spec/files/trogdor.jpg +0 -0
- data/spec/files/trogdor_capitalized.JPG +0 -0
- data/spec/lib/mini_magick/command_builder_spec.rb +0 -153
@@ -1,62 +1,35 @@
|
|
1
|
-
require
|
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
|
-
|
23
|
-
def windows?
|
24
|
-
RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
25
|
-
end
|
7
|
+
module_function
|
26
8
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
data/lib/mini_magick/version.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
module MiniMagick
|
2
2
|
##
|
3
|
-
#
|
4
|
-
#
|
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 =
|
11
|
-
MINOR =
|
12
|
-
TINY =
|
13
|
-
PRE =
|
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
|
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
|
2
|
-
require
|
3
|
-
require
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
context
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
39
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
52
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
105
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
114
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
127
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
-
|
181
|
-
|
182
|
-
File.
|
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
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
197
|
-
|
198
|
-
|
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
|
-
|
201
|
-
|
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
|
-
|
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
|
-
|
208
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
-
|
217
|
-
|
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
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
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
|
-
|
226
|
-
image.
|
227
|
-
|
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
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
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
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
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
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
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
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
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
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
-
|
271
|
-
|
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
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
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
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
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
|
-
|
294
|
-
|
295
|
-
|
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
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
304
|
-
|
305
|
-
|
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
|
-
|
309
|
-
|
310
|
-
|
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
|
-
|
314
|
-
|
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
|
-
|
321
|
-
|
322
|
-
|
323
|
-
image.destroy!
|
324
|
-
end
|
298
|
+
expect(jpg.layers.count).to eq 1
|
299
|
+
end
|
300
|
+
end
|
325
301
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
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
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
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
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
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
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
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
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
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
|
-
|
377
|
-
|
335
|
+
describe "#composite" do
|
336
|
+
let(:other_image) { described_class.open(image_path) }
|
337
|
+
let(:mask) { described_class.open(image_path) }
|
378
338
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
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
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
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
|
-
|
390
|
-
|
391
|
-
|
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
|
-
|
394
|
-
|
395
|
-
|
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
|
-
|
401
|
-
|
402
|
-
|
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
|
-
|
409
|
-
|
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
|
-
|
418
|
-
|
419
|
-
|
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
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
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
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
376
|
+
it "clears the info" do
|
377
|
+
expect { subject.collapse! }
|
378
|
+
.to change { subject.size }
|
379
|
+
end
|
448
380
|
|
449
|
-
|
450
|
-
|
381
|
+
it "returns self" do
|
382
|
+
expect(subject.collapse!).to eq subject
|
383
|
+
end
|
384
|
+
end
|
451
385
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
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
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
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
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
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
|