image_squeeze 0.1.0 → 0.1.1
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.
- 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
|