image_optim 0.8.1 → 0.9.0
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.
- checksums.yaml +8 -8
- data/.gitignore +1 -0
- data/.travis.yml +21 -0
- data/Gemfile +3 -0
- data/README.markdown +5 -4
- data/TODO +0 -1
- data/image_optim.gemspec +3 -2
- data/lib/image_optim/image_path.rb +1 -1
- data/lib/image_optim/worker/jhead.rb +27 -0
- data/lib/image_optim/worker.rb +25 -7
- data/lib/image_optim.rb +1 -1
- data/spec/image_optim_spec.rb +18 -5
- data/spec/images/orient/0.jpg +0 -0
- data/spec/images/orient/1.jpg +0 -0
- data/spec/images/orient/2.jpg +0 -0
- data/spec/images/orient/3.jpg +0 -0
- data/spec/images/orient/4.jpg +0 -0
- data/spec/images/orient/5.jpg +0 -0
- data/spec/images/orient/6.jpg +0 -0
- data/spec/images/orient/7.jpg +0 -0
- data/spec/images/orient/8.jpg +0 -0
- data/spec/images/orient/generate +21 -0
- data/spec/images/orient/original.jpg +0 -0
- metadata +43 -4
checksums.yaml
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
!binary "U0hBMQ==":
|
|
3
3
|
metadata.gz: !binary |-
|
|
4
|
-
|
|
4
|
+
MTRmMzViYWYwZjQ2ZDQ3NzI4MzQ0MWZiNTA2MDgzNjc2OGU2MGM5ZA==
|
|
5
5
|
data.tar.gz: !binary |-
|
|
6
|
-
|
|
6
|
+
NjEzZTE5MmQ5NzcyM2M5NzQ0YTc4Zjk0YTcxODAzN2IyOTM2ODJjNg==
|
|
7
7
|
!binary "U0hBNTEy":
|
|
8
8
|
metadata.gz: !binary |-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
NjZmMzM0YWIzMGI2ODg4NWVjZGMyY2U0NjBkMjdjM2U2ZTQ0NzM1YTk1NzU2
|
|
10
|
+
ZjI1OTk5Mzk3ODZmY2Q5MTM3NTI4ZmI4ZGJjMWViNzA2NDJlN2Q1NWE2ZjBj
|
|
11
|
+
YjAyMDBiYzFhZWM4ZTA2NzdlOGEzYjU1MzgwMDEzNTJhMThmZGY=
|
|
12
12
|
data.tar.gz: !binary |-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
NGM2YmE2MGU3MDI0MjhjYWM5NzdjOWMwNWIyYzM2NzJmMTA3OWI4MzVhOGYy
|
|
14
|
+
YjUwZWFhMDQxNTg5MDk2YWY0YzUxODM2NTdkNjU3OGZhZjU0OTA5MTg5ZTY4
|
|
15
|
+
Y2Q2MGIzNjhjMmM4NDIyOTFiZTQzMzU5ZDhkYzAxODQ4MDJmZjQ=
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
language:
|
|
2
|
+
- ruby
|
|
3
|
+
rvm:
|
|
4
|
+
- 1.8.7
|
|
5
|
+
- 1.9.2
|
|
6
|
+
- 1.9.3
|
|
7
|
+
- 2.0.0
|
|
8
|
+
- jruby-18mode
|
|
9
|
+
- jruby-19mode
|
|
10
|
+
- rbx-18mode
|
|
11
|
+
- rbx-19mode
|
|
12
|
+
- ree
|
|
13
|
+
script: bundle exec rspec
|
|
14
|
+
before_install:
|
|
15
|
+
- sudo apt-get update -qq
|
|
16
|
+
- sudo apt-get install -qq advancecomp gifsicle jhead jpegoptim libjpeg-progs optipng pngcrush
|
|
17
|
+
- wget http://static.jonof.id.au/dl/kenutils/pngout-20130221-linux.tar.gz
|
|
18
|
+
- tar -xzf pngout-*-linux.tar.gz
|
|
19
|
+
- mv pngout-*-linux pngout-linux
|
|
20
|
+
env:
|
|
21
|
+
- PATH=pngout-linux/x86_64:$PATH
|
data/Gemfile
ADDED
data/README.markdown
CHANGED
|
@@ -4,6 +4,7 @@ Optimize (lossless compress) images (jpeg, png, gif) 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
6
|
* [gifsicle](http://www.lcdf.org/gifsicle/)
|
|
7
|
+
* [jhead](http://www.sentex.net/~mwandel/jhead/)
|
|
7
8
|
* [jpegoptim](http://www.kokkonen.net/tjko/projects.html)
|
|
8
9
|
* jpegtran from [Independent JPEG Group's JPEG library](http://www.ijg.org/)
|
|
9
10
|
* [optipng](http://optipng.sourceforge.net/)
|
|
@@ -46,11 +47,11 @@ Besides permanently setting environment variables in `~/.profile`, `~/.bash_prof
|
|
|
46
47
|
|
|
47
48
|
### Linux - Debian/Ubuntu
|
|
48
49
|
|
|
49
|
-
sudo apt-get install -y advancecomp gifsicle jpegoptim libjpeg-progs optipng pngcrush
|
|
50
|
+
sudo apt-get install -y advancecomp gifsicle jhead jpegoptim libjpeg-progs optipng pngcrush
|
|
50
51
|
|
|
51
52
|
### Linux - RHEL/Fedora/Centos
|
|
52
53
|
|
|
53
|
-
sudo yum install -y advancecomp gifsicle libjpeg optipng
|
|
54
|
+
sudo yum install -y advancecomp gifsicle jhead libjpeg optipng
|
|
54
55
|
|
|
55
56
|
You will also need to install `jpegoptim` and `pngcrush` from source:
|
|
56
57
|
|
|
@@ -72,11 +73,11 @@ You will also need to install `jpegoptim` and `pngcrush` from source:
|
|
|
72
73
|
|
|
73
74
|
### OS X: Macports
|
|
74
75
|
|
|
75
|
-
sudo port install advancecomp gifsicle jpegoptim jpeg optipng pngcrush
|
|
76
|
+
sudo port install advancecomp gifsicle jhead jpegoptim jpeg optipng pngcrush
|
|
76
77
|
|
|
77
78
|
### OS X: Brew
|
|
78
79
|
|
|
79
|
-
brew install advancecomp gifsicle jpegoptim jpeg optipng pngcrush
|
|
80
|
+
brew install advancecomp gifsicle jhead jpegoptim jpeg optipng pngcrush
|
|
80
81
|
|
|
81
82
|
### pngout installation (optional)
|
|
82
83
|
|
data/TODO
CHANGED
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.9.0'
|
|
6
6
|
s.summary = %q{Optimize (lossless compress) images (jpeg, png, gif) using external utilities (advpng, gifsicle, jpegoptim, jpegtran, optipng, pngcrush, pngout)}
|
|
7
7
|
s.homepage = "http://github.com/toy/#{s.name}"
|
|
8
8
|
s.authors = ['Ivan Kuchin']
|
|
@@ -15,8 +15,9 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
16
16
|
s.require_paths = %w[lib]
|
|
17
17
|
|
|
18
|
-
s.add_dependency 'fspath', '~> 2.0.
|
|
18
|
+
s.add_dependency 'fspath', '~> 2.0.5'
|
|
19
19
|
s.add_dependency 'image_size', '~> 1.1.2'
|
|
20
|
+
s.add_dependency 'exifr', '~> 1.1.3'
|
|
20
21
|
s.add_dependency 'progress', '~> 2.4.0'
|
|
21
22
|
s.add_dependency 'in_threads', '~> 1.1.1'
|
|
22
23
|
s.add_development_dependency 'rspec'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'image_optim/worker'
|
|
2
|
+
require 'exifr'
|
|
3
|
+
|
|
4
|
+
class ImageOptim
|
|
5
|
+
class Worker
|
|
6
|
+
class Jhead < Worker
|
|
7
|
+
def image_formats
|
|
8
|
+
[:jpeg]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run_order
|
|
12
|
+
-10
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def optimize(src, dst)
|
|
16
|
+
if (2..8).include?(EXIFR::JPEG.new(src.to_s).orientation.to_i)
|
|
17
|
+
src.copy(dst)
|
|
18
|
+
args = %W[-autorot #{dst}]
|
|
19
|
+
resolve_bin!(:jpegtran)
|
|
20
|
+
execute(:jhead, *args) && dst.size?
|
|
21
|
+
else
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/image_optim/worker.rb
CHANGED
|
@@ -46,7 +46,8 @@ class ImageOptim
|
|
|
46
46
|
# List of formats which worker can optimize
|
|
47
47
|
def image_formats
|
|
48
48
|
format_from_name = self.class.name.downcase[/gif|jpeg|png/]
|
|
49
|
-
|
|
49
|
+
raise "#{self.class}: can't guess applicable format from worker name" unless format_from_name
|
|
50
|
+
[format_from_name.to_sym]
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
# Ordering in list of workers
|
|
@@ -68,18 +69,35 @@ class ImageOptim
|
|
|
68
69
|
|
|
69
70
|
# Run command setting priority and hiding output
|
|
70
71
|
def execute(bin, *arguments)
|
|
71
|
-
|
|
72
|
+
command = build_command!(bin, *arguments)
|
|
72
73
|
|
|
73
|
-
command = [bin, *arguments].map(&:to_s).shelljoin
|
|
74
74
|
start = Time.now
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
success = run_command(command)
|
|
77
|
+
|
|
78
|
+
$stderr << "#{success ? '✓' : '✗'} #{Time.now - start}s #{command}\n" if @image_optim.verbose?
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
success
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Build command string
|
|
84
|
+
def build_command!(bin, *arguments)
|
|
85
|
+
resolve_bin!(bin)
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
[bin, *arguments].map(&:to_s).shelljoin
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Run command defining environment, setting nice level, removing output and reraising signal exception
|
|
91
|
+
def run_command(command)
|
|
92
|
+
success = system "env PATH=#{@image_optim.env_path.shellescape} nice -n #{@image_optim.nice} #{command} > /dev/null 2>&1"
|
|
93
|
+
|
|
94
|
+
if $?.signaled?
|
|
95
|
+
unless defined?(JRUBY_VERSION) && $?.exitstatus == $?.termsig # jruby does not differ non zero exit status and signal number
|
|
96
|
+
raise SignalException.new($?.termsig)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
81
99
|
|
|
82
|
-
|
|
100
|
+
success
|
|
83
101
|
end
|
|
84
102
|
end
|
|
85
103
|
end
|
data/lib/image_optim.rb
CHANGED
data/spec/image_optim_spec.rb
CHANGED
|
@@ -3,7 +3,7 @@ require 'rspec'
|
|
|
3
3
|
require 'image_optim'
|
|
4
4
|
require 'tempfile'
|
|
5
5
|
|
|
6
|
-
TEST_IMAGES =
|
|
6
|
+
TEST_IMAGES = ImageOptim::ImagePath.new(__FILE__).dirname.glob('images/**/*.*')
|
|
7
7
|
|
|
8
8
|
Fixnum.class_eval do
|
|
9
9
|
def in_range?(range)
|
|
@@ -208,8 +208,8 @@ describe ImageOptim do
|
|
|
208
208
|
it "should resolve bin specified in ENV" do
|
|
209
209
|
path = (FSPath(__FILE__).dirname / '../bin/image_optim').relative_path_from(Dir.pwd).to_s
|
|
210
210
|
with_env 'IMAGE_OPTIM_BIN', path do
|
|
211
|
-
tmpdir =
|
|
212
|
-
symlink =
|
|
211
|
+
tmpdir = double(:tmpdir)
|
|
212
|
+
symlink = double(:symlink)
|
|
213
213
|
|
|
214
214
|
image_optim = ImageOptim.new
|
|
215
215
|
image_optim.should_receive(:bin_accessible?).with(symlink).once.and_return(true)
|
|
@@ -249,8 +249,8 @@ describe ImageOptim do
|
|
|
249
249
|
it "should raise on failure to resolve bin specified in ENV" do
|
|
250
250
|
path = (FSPath(__FILE__).dirname / '../bin/should_not_exist_bin').relative_path_from(Dir.pwd).to_s
|
|
251
251
|
with_env 'SHOULD_NOT_EXIST_BIN', path do
|
|
252
|
-
tmpdir =
|
|
253
|
-
symlink =
|
|
252
|
+
tmpdir = double(:tmpdir)
|
|
253
|
+
symlink = double(:symlink)
|
|
254
254
|
|
|
255
255
|
image_optim = ImageOptim.new
|
|
256
256
|
image_optim.should_receive(:bin_accessible?).with(symlink).once.and_return(false)
|
|
@@ -275,4 +275,17 @@ describe ImageOptim do
|
|
|
275
275
|
end
|
|
276
276
|
end
|
|
277
277
|
end
|
|
278
|
+
|
|
279
|
+
describe "auto orienting" do
|
|
280
|
+
original = ImageOptim::ImagePath.new(__FILE__).dirname / 'images/orient/original.jpg'
|
|
281
|
+
ImageOptim::ImagePath.new(__FILE__).dirname.glob('images/orient/?.jpg').each do |jpg|
|
|
282
|
+
it "should rotate #{jpg}" do
|
|
283
|
+
image_optim = ImageOptim.new
|
|
284
|
+
oriented = image_optim.optimize_image(jpg)
|
|
285
|
+
nrmse = `compare -metric RMSE #{original.to_s.shellescape} #{oriented.to_s.shellescape} /dev/null 2>&1`[/\((\d+(\.\d+)?)\)/, 1]
|
|
286
|
+
nrmse.should_not be_nil
|
|
287
|
+
nrmse.to_f.should be < 0.005
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
278
291
|
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
convert -size 2x2 xc:black -fill '#f00' -draw 'point 0,1' -fill '#0f0' -draw 'point 1,0' -fill '#00f' -draw 'point 1,1' -scale 64x64 original.png
|
|
4
|
+
|
|
5
|
+
convert original.png 0.jpg
|
|
6
|
+
convert original.png 1.jpg
|
|
7
|
+
convert original.png -flop 2.jpg
|
|
8
|
+
convert original.png -rotate 180 3.jpg
|
|
9
|
+
convert original.png -flip 4.jpg
|
|
10
|
+
convert original.png -transpose 5.jpg
|
|
11
|
+
convert original.png -rotate 270 6.jpg
|
|
12
|
+
convert original.png -transverse 7.jpg
|
|
13
|
+
convert original.png -rotate 90 8.jpg
|
|
14
|
+
|
|
15
|
+
convert original.png original.jpg
|
|
16
|
+
rm original.png
|
|
17
|
+
|
|
18
|
+
exiv2 -M"add Exif.Photo.UserComment image_optim-spec" *.jpg
|
|
19
|
+
for i in {1..8}; do
|
|
20
|
+
exiv2 -M"add Exif.Image.Orientation Short $i" $i.jpg
|
|
21
|
+
done
|
|
Binary file
|
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.9.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: 2013-
|
|
11
|
+
date: 2013-07-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: fspath
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ~>
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 2.0.
|
|
19
|
+
version: 2.0.5
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ~>
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 2.0.
|
|
26
|
+
version: 2.0.5
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: image_size
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -38,6 +38,20 @@ dependencies:
|
|
|
38
38
|
- - ~>
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: 1.1.2
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: exifr
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ~>
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 1.1.3
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 1.1.3
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: progress
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -88,6 +102,8 @@ extensions: []
|
|
|
88
102
|
extra_rdoc_files: []
|
|
89
103
|
files:
|
|
90
104
|
- .gitignore
|
|
105
|
+
- .travis.yml
|
|
106
|
+
- Gemfile
|
|
91
107
|
- LICENSE.txt
|
|
92
108
|
- README.markdown
|
|
93
109
|
- TODO
|
|
@@ -100,6 +116,7 @@ files:
|
|
|
100
116
|
- lib/image_optim/worker.rb
|
|
101
117
|
- lib/image_optim/worker/advpng.rb
|
|
102
118
|
- lib/image_optim/worker/gifsicle.rb
|
|
119
|
+
- lib/image_optim/worker/jhead.rb
|
|
103
120
|
- lib/image_optim/worker/jpegoptim.rb
|
|
104
121
|
- lib/image_optim/worker/jpegtran.rb
|
|
105
122
|
- lib/image_optim/worker/optipng.rb
|
|
@@ -112,6 +129,17 @@ files:
|
|
|
112
129
|
- spec/images/icecream.gif
|
|
113
130
|
- spec/images/image.jpg
|
|
114
131
|
- spec/images/lena.jpg
|
|
132
|
+
- spec/images/orient/0.jpg
|
|
133
|
+
- spec/images/orient/1.jpg
|
|
134
|
+
- spec/images/orient/2.jpg
|
|
135
|
+
- spec/images/orient/3.jpg
|
|
136
|
+
- spec/images/orient/4.jpg
|
|
137
|
+
- spec/images/orient/5.jpg
|
|
138
|
+
- spec/images/orient/6.jpg
|
|
139
|
+
- spec/images/orient/7.jpg
|
|
140
|
+
- spec/images/orient/8.jpg
|
|
141
|
+
- spec/images/orient/generate
|
|
142
|
+
- spec/images/orient/original.jpg
|
|
115
143
|
- spec/images/transparency1.png
|
|
116
144
|
- spec/images/transparency2.png
|
|
117
145
|
- spec/images/vergroessert.jpg
|
|
@@ -148,6 +176,17 @@ test_files:
|
|
|
148
176
|
- spec/images/icecream.gif
|
|
149
177
|
- spec/images/image.jpg
|
|
150
178
|
- spec/images/lena.jpg
|
|
179
|
+
- spec/images/orient/0.jpg
|
|
180
|
+
- spec/images/orient/1.jpg
|
|
181
|
+
- spec/images/orient/2.jpg
|
|
182
|
+
- spec/images/orient/3.jpg
|
|
183
|
+
- spec/images/orient/4.jpg
|
|
184
|
+
- spec/images/orient/5.jpg
|
|
185
|
+
- spec/images/orient/6.jpg
|
|
186
|
+
- spec/images/orient/7.jpg
|
|
187
|
+
- spec/images/orient/8.jpg
|
|
188
|
+
- spec/images/orient/generate
|
|
189
|
+
- spec/images/orient/original.jpg
|
|
151
190
|
- spec/images/transparency1.png
|
|
152
191
|
- spec/images/transparency2.png
|
|
153
192
|
- spec/images/vergroessert.jpg
|