image_optim 0.12.1 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.markdown +2 -0
- data/bin/image_optim +2 -1
- data/image_optim.gemspec +1 -1
- data/lib/image_optim.rb +9 -2
- data/lib/image_optim/bin_resolver.rb +12 -3
- data/lib/image_optim/bin_resolver/comparable_condition.rb +44 -0
- data/lib/image_optim/bin_resolver/simple_version.rb +22 -0
- data/lib/image_optim/image_meta.rb +24 -0
- data/lib/image_optim/image_path.rb +4 -2
- data/script/update_worker_options_in_readme +23 -15
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +38 -0
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +34 -0
- data/spec/image_optim_spec.rb +67 -9
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YjI2ZTQxODlhMzRlMzllZjk0YjhiNmU3YWZiNWYzMDA5NTM2Y2VhNw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MWM1N2Q3ZDlmYzQ1MzczMWQ3NjJhMjBlN2E5NGU0ZGIwODhhMDkwNQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTM3MDE3NThhYmEzZjI3NTM2NWMxZDZhOWI2YTg0NDk0ZDk1NjM1NzMyMzhj
|
10
|
+
NTJjY2UzODhhZmY5MTRhZjRlYTBlYzYxZTY1NzgyMGFmMjY5ZjBhNWU3NDAx
|
11
|
+
MmJlZTEwYTY5MDMxMGI1MTI3YWU0N2NkMjdmOTNjNzYzNjE0NTE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2IzNzVhNWFjNzFkOWUwYjg1MjUyYmM4YWJhNjgzYTlhMjgwZDQ0NTYwNWE0
|
14
|
+
MGMzYTA3YzMzOWZkMmZlYzk0YzliMWJhMDZkODZiN2JkMzc3MzIwZDQ4ZGE3
|
15
|
+
MDE1MGY4NWI5YTA5YWU5YmNhN2Y4MTc4YmExMjkzNDg0NTFmNDk=
|
data/README.markdown
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
Optimize (lossless compress) images (jpeg, png, gif, svg) using external utilities:
|
4
4
|
|
5
5
|
* [advpng](http://advancemame.sourceforge.net/doc-advpng.html) from [AdvanceCOMP](http://advancemame.sourceforge.net/comp-readme.html)
|
6
|
+
(will use [zopfli](https://code.google.com/p/zopfli/) on default/maximum level 4)
|
6
7
|
* [gifsicle](http://www.lcdf.org/gifsicle/)
|
7
8
|
* [jhead](http://www.sentex.net/~mwandel/jhead/)
|
8
9
|
* [jpegoptim](http://www.kokkonen.net/tjko/projects.html)
|
@@ -218,6 +219,7 @@ optipng:
|
|
218
219
|
Worker can be disabled by passing `false` instead of options hash.
|
219
220
|
|
220
221
|
<!---<worker-options>-->
|
222
|
+
<!-- worker options markdown is generated by `script/update_worker_options_in_readme` -->
|
221
223
|
|
222
224
|
### :pngcrush =>
|
223
225
|
* `:chunks` — List of chunks to remove or `:alla` - all except tRNS/transparency or `:allb` - all except tRNS and gAMA/gamma *(defaults to `:alla`)*
|
data/bin/image_optim
CHANGED
@@ -9,7 +9,7 @@ option_parser = OptionParser.new do |op|
|
|
9
9
|
op.accept(ImageOptim::TrueFalseNil, OptionParser.top.atype[TrueClass][0].merge('nil' => nil)){ |arg, val| val }
|
10
10
|
|
11
11
|
op.banner = <<-TEXT.gsub(/^\s*\|/, '')
|
12
|
-
|#{
|
12
|
+
|#{ImageOptim.full_version}
|
13
13
|
|
|
14
14
|
|Usege:
|
15
15
|
| #{op.program_name} [options] image_path …
|
@@ -103,6 +103,7 @@ begin
|
|
103
103
|
end
|
104
104
|
|
105
105
|
option_parser.parse!(args)
|
106
|
+
$stderr.puts ImageOptim.full_version if options[:verbose]
|
106
107
|
ImageOptim::Runner.run!(args, options) or exit 1
|
107
108
|
rescue OptionParser::ParseError => e
|
108
109
|
abort "#{e.to_s}\n\n#{option_parser.help}"
|
data/image_optim.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'image_optim'
|
5
|
-
s.version = '0.
|
5
|
+
s.version = '0.13.0'
|
6
6
|
s.summary = %q{Optimize (lossless compress) images (jpeg, png, gif, svg) using external utilities (advpng, gifsicle, jpegoptim, jpegtran, optipng, pngcrush, pngout, svgo)}
|
7
7
|
s.homepage = "http://github.com/toy/#{s.name}"
|
8
8
|
s.authors = ['Ivan Kuchin']
|
data/lib/image_optim.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'image_optim/bin_resolver'
|
2
2
|
require 'image_optim/config'
|
3
3
|
require 'image_optim/handler'
|
4
|
+
require 'image_optim/image_meta'
|
4
5
|
require 'image_optim/image_path'
|
5
6
|
require 'image_optim/worker'
|
6
7
|
require 'in_threads'
|
@@ -97,8 +98,9 @@ class ImageOptim
|
|
97
98
|
|
98
99
|
# Optimize image data, return new data or nil if optimization failed
|
99
100
|
def optimize_image_data(original_data)
|
100
|
-
|
101
|
-
|
101
|
+
image_meta = ImageMeta.for_data(original_data)
|
102
|
+
return unless image_meta && image_meta.format
|
103
|
+
ImagePath.temp_file %W[image_optim .#{image_meta.format}] do |temp|
|
102
104
|
temp.binmode
|
103
105
|
temp.write(original_data)
|
104
106
|
temp.close
|
@@ -144,6 +146,11 @@ class ImageOptim
|
|
144
146
|
Gem.loaded_specs['image_optim'].version.to_s rescue 'DEV'
|
145
147
|
end
|
146
148
|
|
149
|
+
# Full version of image_optim
|
150
|
+
def self.full_version
|
151
|
+
"image_optim v#{version}"
|
152
|
+
end
|
153
|
+
|
147
154
|
# Are there workers for file at path?
|
148
155
|
def optimizable?(path)
|
149
156
|
!!workers_for_image(path)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'fspath'
|
3
|
+
require 'image_optim/bin_resolver/simple_version'
|
4
|
+
require 'image_optim/bin_resolver/comparable_condition'
|
3
5
|
|
4
6
|
class ImageOptim
|
5
7
|
class BinNotFoundError < StandardError; end
|
@@ -9,7 +11,8 @@ class ImageOptim
|
|
9
11
|
class Bin
|
10
12
|
attr_reader :name, :version
|
11
13
|
def initialize(name, version)
|
12
|
-
@name
|
14
|
+
@name = name
|
15
|
+
@version = version && SimpleVersion.new(version)
|
13
16
|
end
|
14
17
|
|
15
18
|
def to_s
|
@@ -94,11 +97,17 @@ class ImageOptim
|
|
94
97
|
end
|
95
98
|
|
96
99
|
def check!(bin)
|
100
|
+
is = ComparableCondition.is
|
97
101
|
case bin.name
|
98
102
|
when :pngcrush
|
99
103
|
case bin.version
|
100
|
-
when '1.7.60'
|
101
|
-
raise BadBinVersion, "`#{bin}` is known to produce broken pngs"
|
104
|
+
when c = is.between?('1.7.60', '1.7.65')
|
105
|
+
raise BadBinVersion, "`#{bin}` (#{c}) is known to produce broken pngs"
|
106
|
+
end
|
107
|
+
when :advpng
|
108
|
+
case bin.version
|
109
|
+
when c = is < '1.17'
|
110
|
+
warn "Note that `#{bin}` (#{c}) does not use zopfli"
|
102
111
|
end
|
103
112
|
end
|
104
113
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class ImageOptim
|
2
|
+
class BinResolver
|
3
|
+
class ComparableCondition
|
4
|
+
class Builder
|
5
|
+
Comparable.instance_methods.each do |method|
|
6
|
+
define_method method do |*args|
|
7
|
+
ComparableCondition.new(method, *args)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.is
|
13
|
+
Builder.new
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :method, :args
|
17
|
+
def initialize(method, *args)
|
18
|
+
@method = method.to_sym
|
19
|
+
@args = args
|
20
|
+
|
21
|
+
case @method
|
22
|
+
when :between?
|
23
|
+
raise ArgumentError, "`between?' expects 2 arguments" unless args.length == 2
|
24
|
+
when :<, :<=, :==, :>, :>=
|
25
|
+
raise ArgumentError, "`#{method}' expects 1 argument" unless args.length == 1
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Unknown method `#{method}'"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def ===(to_compare)
|
32
|
+
to_compare.send(@method, *@args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
if @method == :between?
|
37
|
+
@args.join('..')
|
38
|
+
else
|
39
|
+
"#{@method} #{@args.first}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ImageOptim
|
2
|
+
class BinResolver
|
3
|
+
class SimpleVersion
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_reader :parts
|
7
|
+
def initialize(str)
|
8
|
+
@str = String(str)
|
9
|
+
@parts = @str.split('.').map(&:to_i).reverse.drop_while(&:zero?).reverse
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
@str
|
14
|
+
end
|
15
|
+
|
16
|
+
def <=>(other)
|
17
|
+
other = self.class.new(other) unless other.is_a?(self.class)
|
18
|
+
parts <=> other.parts
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'image_size'
|
2
|
+
|
3
|
+
class ImageOptim
|
4
|
+
class ImageMeta
|
5
|
+
def self.for_path(path)
|
6
|
+
is = ImageSize.path(path)
|
7
|
+
new(is.format)
|
8
|
+
rescue => e
|
9
|
+
warn "#{e} (detecting format of image at #{path})"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.for_data(data)
|
13
|
+
is = ImageSize.new(data)
|
14
|
+
new(is.format)
|
15
|
+
rescue => e
|
16
|
+
warn "#{e} (detecting format of image data)"
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :format
|
20
|
+
def initialize(format)
|
21
|
+
@format = format
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'fspath'
|
2
|
-
require '
|
2
|
+
require 'image_optim/image_meta'
|
3
3
|
|
4
4
|
class ImageOptim
|
5
5
|
class ImagePath < FSPath
|
@@ -49,7 +49,9 @@ class ImageOptim
|
|
49
49
|
|
50
50
|
# Get format using ImageSize
|
51
51
|
def format
|
52
|
-
|
52
|
+
if image_meta = ImageMeta.for_path(self)
|
53
|
+
image_meta.format
|
54
|
+
end
|
53
55
|
end
|
54
56
|
|
55
57
|
# Returns path if it is already an instance of this class otherwise new instance
|
@@ -8,28 +8,36 @@ require 'image_optim'
|
|
8
8
|
README_FILE = File.expand_path('../../README.markdown', __FILE__)
|
9
9
|
BEGIN_MARKER = '<!---<worker-options>-->'
|
10
10
|
END_MARKER = '<!---</worker-options>-->'
|
11
|
+
GENERATED_NOTE = "<!-- markdown for worker options is generated by `#{$0}` -->"
|
12
|
+
|
13
|
+
def update_worker_options(text)
|
14
|
+
text.clone.sub!(/#{Regexp.escape(BEGIN_MARKER)}.*#{Regexp.escape(END_MARKER)}/m) do
|
15
|
+
StringIO.open do |md|
|
16
|
+
md.puts BEGIN_MARKER
|
17
|
+
md.puts GENERATED_NOTE
|
18
|
+
md.puts
|
19
|
+
|
20
|
+
ImageOptim::Worker.klasses.each_with_index do |klass, i|
|
21
|
+
md.puts "### :#{klass.bin_sym} =>"
|
22
|
+
if klass.option_definitions.empty?
|
23
|
+
md.puts 'Worker has no options'
|
24
|
+
else
|
25
|
+
klass.option_definitions.each do |option_definition|
|
26
|
+
md.puts "* `:#{option_definition.name}` — #{option_definition.description} *(defaults to `#{option_definition.default.inspect}`)*"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
md.puts
|
30
|
+
end
|
11
31
|
|
12
|
-
|
13
|
-
io = StringIO.new
|
32
|
+
md.puts END_MARKER
|
14
33
|
|
15
|
-
|
16
|
-
io.puts "### :#{klass.bin_sym} =>"
|
17
|
-
if klass.option_definitions.empty?
|
18
|
-
io.puts 'Worker has no options'
|
19
|
-
else
|
20
|
-
klass.option_definitions.each do |option_definition|
|
21
|
-
io.puts "* `:#{option_definition.name}` — #{option_definition.description} *(defaults to `#{option_definition.default.inspect}`)*"
|
22
|
-
end
|
34
|
+
md.string.strip
|
23
35
|
end
|
24
|
-
io.puts
|
25
36
|
end
|
26
|
-
|
27
|
-
io.string
|
28
37
|
end
|
29
38
|
|
30
39
|
readme = File.read(README_FILE)
|
31
|
-
|
32
|
-
if readme.sub!(/#{Regexp.escape(BEGIN_MARKER)}.*#{Regexp.escape(END_MARKER)}/m, "#{BEGIN_MARKER}\n\n#{worker_options.strip}\n\n#{END_MARKER}")
|
40
|
+
if readme = update_worker_options(readme)
|
33
41
|
File.open(README_FILE, 'w') do |f|
|
34
42
|
f.write readme
|
35
43
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
$:.unshift File.expand_path('../../../../lib', __FILE__)
|
2
|
+
require 'rspec'
|
3
|
+
require 'image_optim/bin_resolver/comparable_condition'
|
4
|
+
|
5
|
+
describe ImageOptim::BinResolver::ComparableCondition do
|
6
|
+
is = ImageOptim::BinResolver::ComparableCondition.is
|
7
|
+
|
8
|
+
it "should build conditions" do
|
9
|
+
expect(is.between?(10, 20).method).to eq(:between?)
|
10
|
+
expect(is.between?(10, 20).args).to eq([10, 20])
|
11
|
+
|
12
|
+
expect((is >= 15).method).to eq(:>=)
|
13
|
+
expect((is >= 15).args).to eq([15])
|
14
|
+
|
15
|
+
expect((is < 30).method).to eq(:<)
|
16
|
+
expect((is < 30).args).to eq([30])
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should stringify conditions" do
|
20
|
+
expect(is.between?(10, 20).to_s).to eq('10..20')
|
21
|
+
expect((is >= 15).to_s).to eq('>= 15')
|
22
|
+
expect((is < 30).to_s).to eq('< 30')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should match conditions" do
|
26
|
+
expect(is.between?(10, 20)).not_to be === 9
|
27
|
+
expect(is.between?(10, 20)).to be === 15
|
28
|
+
expect(is.between?(10, 20)).not_to be === 21
|
29
|
+
|
30
|
+
expect(is >= 15).not_to be === 14
|
31
|
+
expect(is >= 15).to be === 15
|
32
|
+
expect(is >= 15).to be === 16
|
33
|
+
|
34
|
+
expect(is < 30).to be === 29
|
35
|
+
expect(is < 30).not_to be === 30
|
36
|
+
expect(is < 30).not_to be === 31
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
$:.unshift File.expand_path('../../../../lib', __FILE__)
|
2
|
+
require 'rspec'
|
3
|
+
require 'image_optim/bin_resolver/simple_version'
|
4
|
+
|
5
|
+
describe ImageOptim::BinResolver::SimpleVersion do
|
6
|
+
def v(str)
|
7
|
+
ImageOptim::BinResolver::SimpleVersion.new(str)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should compare versions" do
|
11
|
+
expect(v '1.17').to be > '0'
|
12
|
+
expect(v '1.17').to be > '0.1'
|
13
|
+
expect(v '1.17').to be > '0.9'
|
14
|
+
expect(v '1.17').to be > '1.9'
|
15
|
+
expect(v '1.17').to be < '1.17.1'
|
16
|
+
expect(v '1.17').to be < '1.99'
|
17
|
+
expect(v '1.17').to be < '2.1'
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should normalize versions" do
|
21
|
+
variations = %w[1 01 1.0 1.00 1.0.0 1.0.0.0]
|
22
|
+
variations.each do |a|
|
23
|
+
variations.each do |b|
|
24
|
+
expect(v a).to eq(b)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should convert objects" do
|
30
|
+
expect(v 1.17).to eq('1.17')
|
31
|
+
expect(v '1.17').to eq('1.17')
|
32
|
+
expect(v(v 1.17)).to eq('1.17')
|
33
|
+
end
|
34
|
+
end
|
data/spec/image_optim_spec.rb
CHANGED
@@ -44,6 +44,18 @@ describe ImageOptim do
|
|
44
44
|
ImageOptim::Config.stub(:global => {}, :local => {})
|
45
45
|
end
|
46
46
|
|
47
|
+
describe "worker" do
|
48
|
+
options = Hash[ImageOptim::Worker.klasses.map{ |klass| [klass.bin_sym, false] }]
|
49
|
+
ImageOptim::Worker.klasses.reject{ |k| k.new({}).image_formats.empty? }.each do |worker_klass|
|
50
|
+
describe worker_klass.bin_sym do
|
51
|
+
it "should optimize at least one test image" do
|
52
|
+
image_optim = ImageOptim.new(options.merge(worker_klass.bin_sym => true))
|
53
|
+
expect(TEST_IMAGES.any?{ |original| image_optim.optimize_image(original.temp_copy) }).to be_true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
47
59
|
describe "isolated" do
|
48
60
|
describe "optimize" do
|
49
61
|
TEST_IMAGES.each do |original|
|
@@ -92,6 +104,7 @@ describe ImageOptim do
|
|
92
104
|
it "should optimize #{original}" do
|
93
105
|
image_optim = ImageOptim.new
|
94
106
|
optimized_data = image_optim.optimize_image_data(original.read)
|
107
|
+
optimized_data.should_not be_nil
|
95
108
|
optimized_data.should == image_optim.optimize_image(original.temp_copy).open('rb', &:read)
|
96
109
|
|
97
110
|
image_optim.optimize_image_data(optimized_data).should be_nil
|
@@ -139,6 +152,7 @@ describe ImageOptim do
|
|
139
152
|
it "should optimize datas" do
|
140
153
|
optimized_images_datas = ImageOptim.optimize_images_data(TEST_IMAGES.map(&:read))
|
141
154
|
TEST_IMAGES.zip(optimized_images_datas).each do |original, optimized_image_data|
|
155
|
+
optimized_image_data.should_not be_nil
|
142
156
|
optimized_image_data.should == ImageOptim.optimize_image(original.temp_copy).open('rb', &:read)
|
143
157
|
end
|
144
158
|
end
|
@@ -165,6 +179,27 @@ describe ImageOptim do
|
|
165
179
|
Tempfile.init_count.should == 0
|
166
180
|
copy.read.should == original.read
|
167
181
|
end
|
182
|
+
|
183
|
+
{
|
184
|
+
:png => "\211PNG\r\n\032\n",
|
185
|
+
:jpeg => "\377\330",
|
186
|
+
}.each do |type, data|
|
187
|
+
describe "broken #{type}" do
|
188
|
+
before do
|
189
|
+
ImageOptim::ImageMeta.should_receive(:warn)
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should ignore path" do
|
193
|
+
path = FSPath.temp_file_path
|
194
|
+
path.write(data)
|
195
|
+
ImageOptim.optimize_image(path).should be_nil
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should ignore data" do
|
199
|
+
ImageOptim.optimize_image_data(data).should be_nil
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
168
203
|
end
|
169
204
|
|
170
205
|
describe "optimize multiple" do
|
@@ -202,15 +237,38 @@ describe ImageOptim do
|
|
202
237
|
end
|
203
238
|
end
|
204
239
|
|
205
|
-
describe "
|
206
|
-
|
207
|
-
ImageOptim::ImagePath.new(__FILE__).dirname.glob('images/orient/?.jpg')
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
240
|
+
describe "losslessness" do
|
241
|
+
rotated = ImageOptim::ImagePath.new(__FILE__).dirname / 'images/orient/original.jpg'
|
242
|
+
rotate_images = ImageOptim::ImagePath.new(__FILE__).dirname.glob('images/orient/?.jpg')
|
243
|
+
|
244
|
+
def flatten_animation(image)
|
245
|
+
if image.format == :gif
|
246
|
+
flattened = image.temp_path
|
247
|
+
system("convert #{image.to_s.shellescape} -coalesce -append #{flattened.to_s.shellescape}").should be_true
|
248
|
+
flattened
|
249
|
+
else
|
250
|
+
image
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def check_lossless_optimization(original, optimized)
|
255
|
+
optimized.should_not be_nil
|
256
|
+
original = flatten_animation(original)
|
257
|
+
optimized = flatten_animation(optimized)
|
258
|
+
nrmse = `compare -metric RMSE #{original.to_s.shellescape} #{optimized.to_s.shellescape} /dev/null 2>&1`[/\((\d+(\.\d+)?)\)/, 1]
|
259
|
+
nrmse.should_not be_nil
|
260
|
+
nrmse.to_f.should == 0
|
261
|
+
end
|
262
|
+
|
263
|
+
rotate_images.each do |image|
|
264
|
+
it "should rotate and optimize #{image} losslessly" do
|
265
|
+
check_lossless_optimization(rotated, ImageOptim.optimize_image(image))
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
(TEST_IMAGES - rotate_images).each do |image|
|
270
|
+
it "should optimize #{image} losslessly" do
|
271
|
+
check_lossless_optimization(image, ImageOptim.optimize_image(image))
|
214
272
|
end
|
215
273
|
end
|
216
274
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_optim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Kuchin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fspath
|
@@ -110,10 +110,13 @@ files:
|
|
110
110
|
- image_optim.gemspec
|
111
111
|
- lib/image_optim.rb
|
112
112
|
- lib/image_optim/bin_resolver.rb
|
113
|
+
- lib/image_optim/bin_resolver/comparable_condition.rb
|
114
|
+
- lib/image_optim/bin_resolver/simple_version.rb
|
113
115
|
- lib/image_optim/config.rb
|
114
116
|
- lib/image_optim/configuration_error.rb
|
115
117
|
- lib/image_optim/handler.rb
|
116
118
|
- lib/image_optim/hash_helpers.rb
|
119
|
+
- lib/image_optim/image_meta.rb
|
117
120
|
- lib/image_optim/image_path.rb
|
118
121
|
- lib/image_optim/option_definition.rb
|
119
122
|
- lib/image_optim/option_helpers.rb
|
@@ -131,6 +134,8 @@ files:
|
|
131
134
|
- lib/image_optim/worker/pngout.rb
|
132
135
|
- lib/image_optim/worker/svgo.rb
|
133
136
|
- script/update_worker_options_in_readme
|
137
|
+
- spec/image_optim/bin_resolver/comparable_condition_spec.rb
|
138
|
+
- spec/image_optim/bin_resolver/simple_version_spec.rb
|
134
139
|
- spec/image_optim/bin_resolver_spec.rb
|
135
140
|
- spec/image_optim/config_spec.rb
|
136
141
|
- spec/image_optim/handler_spec.rb
|
@@ -184,6 +189,8 @@ specification_version: 4
|
|
184
189
|
summary: Optimize (lossless compress) images (jpeg, png, gif, svg) using external
|
185
190
|
utilities (advpng, gifsicle, jpegoptim, jpegtran, optipng, pngcrush, pngout, svgo)
|
186
191
|
test_files:
|
192
|
+
- spec/image_optim/bin_resolver/comparable_condition_spec.rb
|
193
|
+
- spec/image_optim/bin_resolver/simple_version_spec.rb
|
187
194
|
- spec/image_optim/bin_resolver_spec.rb
|
188
195
|
- spec/image_optim/config_spec.rb
|
189
196
|
- spec/image_optim/handler_spec.rb
|