image_squeeze 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -0
- data/lib/image_squeeze.rb +27 -8
- data/lib/image_squeeze/processors/gif_to_png_processor.rb +5 -2
- data/lib/image_squeeze/processors/processor.rb +0 -15
- data/lib/image_squeeze/utils.rb +4 -3
- data/test/functional/default_processors_test.rb +2 -1
- data/test/functional/squeeze_test.rb +14 -2
- data/test/functional/utils_test.rb +29 -0
- data/test/test_helper.rb +1 -0
- data/test/tmp/already_optimized_gif.gif20100520-30783-189g816 +1 -0
- metadata +5 -3
data/README.md
CHANGED
@@ -6,6 +6,8 @@ A library for automated lossless image optimization
|
|
6
6
|
|
7
7
|
The default processors depend on ImageMagick, pngcrush, gifsicle, and jpegtran. ImageMagick is required for all processors.
|
8
8
|
|
9
|
+
gem install image_squeeze
|
10
|
+
|
9
11
|
## Usage
|
10
12
|
|
11
13
|
# set up an ImageSqueeze with the default processors
|
data/lib/image_squeeze.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)
|
2
2
|
|
3
|
+
require 'tempfile'
|
3
4
|
require 'logger'
|
5
|
+
|
4
6
|
require 'image_squeeze/log_factory'
|
5
7
|
require 'image_squeeze/utils'
|
6
8
|
require 'image_squeeze/image_identifier'
|
@@ -16,8 +18,9 @@ require 'image_squeeze/processors/gif_to_png_processor'
|
|
16
18
|
|
17
19
|
class ImageSqueeze
|
18
20
|
attr_reader :processors
|
21
|
+
attr_reader :tmpdir
|
19
22
|
|
20
|
-
VERSION = '0.1.
|
23
|
+
VERSION = '0.1.1'
|
21
24
|
|
22
25
|
# Image Types
|
23
26
|
GIF = 'gif'
|
@@ -40,6 +43,8 @@ class ImageSqueeze
|
|
40
43
|
|
41
44
|
@processors = options[:processors] || []
|
42
45
|
@processors += self.class.default_processors if options[:default_processors]
|
46
|
+
|
47
|
+
@tmpdir = options[:tmpdir] || Dir::tmpdir
|
43
48
|
end
|
44
49
|
|
45
50
|
def self.default
|
@@ -55,7 +60,8 @@ class ImageSqueeze
|
|
55
60
|
|
56
61
|
original_file_size = File.size(filename)
|
57
62
|
sorted_results = processors.map do |processor_class|
|
58
|
-
output_filename =
|
63
|
+
output_filename = tmp_filename(filename)
|
64
|
+
processor_class.squeeze(filename, output_filename)
|
59
65
|
output_file_size = File.size(output_filename)
|
60
66
|
result_options = { :filename => filename, :output_filename => output_filename, :bytes_saved => original_file_size - output_file_size, :output_extension => processor_class.output_extension }
|
61
67
|
Result.new(result_options)
|
@@ -87,22 +93,35 @@ class ImageSqueeze
|
|
87
93
|
output_filename
|
88
94
|
end
|
89
95
|
|
90
|
-
def logger
|
96
|
+
def self.logger
|
91
97
|
LogFactory.logger
|
92
98
|
end
|
93
99
|
|
100
|
+
def logger
|
101
|
+
self.class.logger
|
102
|
+
end
|
103
|
+
|
94
104
|
def self.default_processors
|
95
105
|
processors = []
|
96
|
-
ImageSqueeze::Utils.image_utility_available?('identify', 'all image',
|
97
|
-
if ImageSqueeze::Utils.image_utility_available?('pngcrush', 'pngs and gif'
|
106
|
+
ImageSqueeze::Utils.image_utility_available?('identify', 'all image', true)
|
107
|
+
if ImageSqueeze::Utils.image_utility_available?('pngcrush', 'pngs and gif')
|
98
108
|
processors << PNGCrushProcessor
|
99
|
-
processors << GIFToPNGProcessor if ImageSqueeze::Utils.image_utility_available?('convert', 'gif'
|
109
|
+
processors << GIFToPNGProcessor if ImageSqueeze::Utils.image_utility_available?('convert', 'gif')
|
100
110
|
end
|
101
|
-
processors << GifsicleProcessor if ImageSqueeze::Utils.image_utility_available?('gifsicle', 'animated gif'
|
102
|
-
if ImageSqueeze::Utils.image_utility_available?('jpegtran', 'jpeg'
|
111
|
+
processors << GifsicleProcessor if ImageSqueeze::Utils.image_utility_available?('gifsicle', 'animated gif')
|
112
|
+
if ImageSqueeze::Utils.image_utility_available?('jpegtran', 'jpeg')
|
103
113
|
processors << JPEGTranProgressiveProcessor
|
104
114
|
processors << JPEGTranNonProgressiveProcessor
|
105
115
|
end
|
106
116
|
processors
|
107
117
|
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def tmp_filename(filename)
|
121
|
+
t = Time.now.strftime("%Y%m%d")
|
122
|
+
path = "#{File.basename(filename)}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
|
123
|
+
|
124
|
+
File.join(tmpdir, path)
|
125
|
+
end
|
126
|
+
|
108
127
|
end
|
@@ -9,11 +9,14 @@ class ImageSqueeze
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.squeeze(filename, output_filename)
|
12
|
-
intermediate_tmp_filename =
|
12
|
+
intermediate_tmp_filename = "%s-%s" % [output_filename, '.tmp']
|
13
13
|
|
14
14
|
system("convert #{filename} PNG:#{intermediate_tmp_filename} 2> /dev/null")
|
15
|
+
response = PNGCrushProcessor.squeeze(intermediate_tmp_filename, output_filename) # run it through PNGCrush afterwards
|
15
16
|
|
16
|
-
|
17
|
+
FileUtils.rm(intermediate_tmp_filename) # clean up after ourselves
|
18
|
+
|
19
|
+
response
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
@@ -1,22 +1,7 @@
|
|
1
|
-
require 'tempfile'
|
2
|
-
|
3
1
|
class ImageSqueeze
|
4
2
|
class Processor
|
5
3
|
attr_reader :filename
|
6
4
|
|
7
|
-
def self.tmp_filename(filename)
|
8
|
-
t = Time.now.strftime("%Y%m%d")
|
9
|
-
path = "#{File.basename(filename)}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
|
10
|
-
|
11
|
-
File.join(Dir::tmpdir, path)
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.squeeze_to_tmp(filename)
|
15
|
-
tmp = tmp_filename(filename)
|
16
|
-
squeeze(filename, tmp)
|
17
|
-
tmp
|
18
|
-
end
|
19
|
-
|
20
5
|
def self.squeeze(filename, output_filename)
|
21
6
|
raise "#{to_s}#squeeze should be defined in subclass and should convert filename to something at output_filename"
|
22
7
|
end
|
data/lib/image_squeeze/utils.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
class ImageSqueeze
|
2
2
|
module Utils
|
3
|
-
def self.image_utility_available?(bin, extension,
|
3
|
+
def self.image_utility_available?(bin, extension, raise_when_missing = false)
|
4
4
|
return true if system("which #{bin} > /dev/null")
|
5
|
-
if
|
5
|
+
if raise_when_missing
|
6
6
|
ImageSqueeze.logger.error("#{bin} utility is required for running ImageSqueeze, get it installed already")
|
7
7
|
raise StandardError, "#{bin} utility is required for running ImageSqueeze, get it installed already"
|
8
8
|
else
|
9
|
-
ImageSqueeze.logger.
|
9
|
+
ImageSqueeze.logger.warn("#{bin} utility could not be found, your #{extension} files won't be squeezed")
|
10
10
|
end
|
11
|
+
false
|
11
12
|
end
|
12
13
|
end
|
13
14
|
end
|
@@ -55,6 +55,7 @@ class DefaultProcessorsTest < Test::Unit::TestCase
|
|
55
55
|
filename = fixtures(file)
|
56
56
|
old_size = File.size(filename)
|
57
57
|
new_filename = squeezer.squeeze!(filename)
|
58
|
-
|
58
|
+
new_file_size = File.size(new_filename) if new_filename
|
59
|
+
assert new_filename.nil?, "New file size of #{new_file_size} should be at least as big as original of #{old_size}"
|
59
60
|
end
|
60
61
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
-
require 'mocha'
|
3
2
|
|
4
3
|
class SqueezeTest < Test::Unit::TestCase
|
5
4
|
def setup
|
@@ -77,6 +76,17 @@ class SqueezeTest < Test::Unit::TestCase
|
|
77
76
|
assert_equal old_size, File.size(filename)
|
78
77
|
end
|
79
78
|
|
79
|
+
def test_override_tmp_dir_stores_output_files_in_correct_location
|
80
|
+
tmpdir = File.join(File.dirname(__FILE__), '..', 'tmp')
|
81
|
+
FileUtils.mkdir_p(tmpdir)
|
82
|
+
image_squeezer = custom_image_squeezer(AlwaysOptimize, :tmpdir => tmpdir)
|
83
|
+
|
84
|
+
result = image_squeezer.squeeze(fixtures('already_optimized_gif.gif'))
|
85
|
+
assert_equal 0, result.output_filename.index(tmpdir), "Output file should be located in tmpdir: #{tmpdir}"
|
86
|
+
ensure
|
87
|
+
FileUtils.rm(result.output_filename) if result && result.output_filename && File.exists?(result.output_filename)
|
88
|
+
end
|
89
|
+
|
80
90
|
private
|
81
91
|
class AlwaysOptimize < ImageSqueeze::Processor
|
82
92
|
def self.squeeze(filename, output_filename)
|
@@ -111,6 +121,8 @@ class SqueezeTest < Test::Unit::TestCase
|
|
111
121
|
end
|
112
122
|
|
113
123
|
def custom_image_squeezer(*processors)
|
114
|
-
|
124
|
+
options = processors.last.is_a?(Hash) ? processors.pop : {}
|
125
|
+
|
126
|
+
ImageSqueeze.new(options.merge(:processors => processors))
|
115
127
|
end
|
116
128
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
class UtilsTest < Test::Unit::TestCase
|
4
|
+
def test_utility_available_returns_true_when_found
|
5
|
+
assert ImageSqueeze::Utils.image_utility_available?('ls', 'files'), "You don't have ls, wtf?"
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_utility_available_returns_false_when_not_found
|
9
|
+
ImageSqueeze.logger.stubs(:warn)
|
10
|
+
assert !ImageSqueeze::Utils.image_utility_available?('andrewMagicNonexistantFileDestroyer', 'magic'), "Uhhh...where'd you get that?"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_utility_logs_warning_when_not_found
|
14
|
+
ImageSqueeze.logger.expects(:warn).with(regexp_matches(/marbles/))
|
15
|
+
ImageSqueeze::Utils.image_utility_available?('andrewMagicNonexistantFileDestroyer', 'marbles')
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_utility_available_raises_when_not_found_and_raise_when_missing_true
|
19
|
+
ImageSqueeze.logger.stubs(:error)
|
20
|
+
assert_raises StandardError do
|
21
|
+
!ImageSqueeze::Utils.image_utility_available?('andrewMagicNonexistantFileDestroyer', 'magic', true)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_utility_logs_error_when_missing_true
|
26
|
+
ImageSqueeze.logger.expects(:error)
|
27
|
+
ImageSqueeze::Utils.image_utility_available?('andrewMagicNonexistantFileDestroyer', 'dolphins', true) rescue StandardError
|
28
|
+
end
|
29
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
real small
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Andrew Grim
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-20 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -54,7 +54,9 @@ files:
|
|
54
54
|
- test/functional/image_identifier_test.rb
|
55
55
|
- test/functional/result_test.rb
|
56
56
|
- test/functional/squeeze_test.rb
|
57
|
+
- test/functional/utils_test.rb
|
57
58
|
- test/test_helper.rb
|
59
|
+
- test/tmp/already_optimized_gif.gif20100520-30783-189g816
|
58
60
|
- CONTRIBUTORS
|
59
61
|
- LICENSE
|
60
62
|
- Rakefile
|