image_optim 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MmQ2Y2Q0NDBlYzJjYzQyODlhMjg1NjI1ZWJkODVhNzQwZGM4MzUwZA==
4
+ MTRmMzViYWYwZjQ2ZDQ3NzI4MzQ0MWZiNTA2MDgzNjc2OGU2MGM5ZA==
5
5
  data.tar.gz: !binary |-
6
- NzYxNjRjMjFiYWY2MzQwODdkY2NkZTkwMDM3ODY1NGQ2NDc0NGZiMA==
6
+ NjEzZTE5MmQ5NzcyM2M5NzQ0YTc4Zjk0YTcxODAzN2IyOTM2ODJjNg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZjEwZGVmNjE2YTBmYmFiM2M1ZTlkNWI0YzgyOWEzZTAzNzFkNDcxNDIwMGNh
10
- ODgyNGU5MjU5MTdlMjY4OGNmOGQ5NWUxYWU1MTZhZWEwNmE5ODI3NmZiNmE4
11
- MjM4NzNlNmI0MGU4YTM5NzFlMmVkOGQ0YTU1MTE5NTBkMzE2OGE=
9
+ NjZmMzM0YWIzMGI2ODg4NWVjZGMyY2U0NjBkMjdjM2U2ZTQ0NzM1YTk1NzU2
10
+ ZjI1OTk5Mzk3ODZmY2Q5MTM3NTI4ZmI4ZGJjMWViNzA2NDJlN2Q1NWE2ZjBj
11
+ YjAyMDBiYzFhZWM4ZTA2NzdlOGEzYjU1MzgwMDEzNTJhMThmZGY=
12
12
  data.tar.gz: !binary |-
13
- NWRmODhjMmE3Y2VhYTc0YTBjNjA2ZGIwNzY4ZGZiY2EzZTQxOWZhYjk0Mzhm
14
- YmRkYTUyMDUxZTZmZjA0NzQ2MzU0ODkyZGRlYjIyOWEzYTQxODUxZTliM2Ni
15
- MWVkMmJkNWVlMzZiOGM1ZTQzY2M5ZjEyNzZjYzkzOGU0YTZlMjY=
13
+ NGM2YmE2MGU3MDI0MjhjYWM5NzdjOWMwNWIyYzM2NzJmMTA3OWI4MzVhOGYy
14
+ YjUwZWFhMDQxNTg5MDk2YWY0YzUxODM2NTdkNjU3OGZhZjU0OTA5MTg5ZTY4
15
+ Y2Q2MGIzNjhjMmM4NDIyOTFiZTQzMzU5ZDhkYzAxODQ4MDJmZjQ=
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /.yardoc/
7
7
  /coverage/
8
8
 
9
+ Gemfile.lock
9
10
  Makefile
10
11
  *.o
11
12
  *.bundle
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
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
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
@@ -1,7 +1,6 @@
1
1
  preserve color (leave_color branch)
2
2
  preserve all extra stuff
3
3
  global level of optimization #10
4
- autorotate jpeg based on exif (jhead -auotrot)
5
4
  timeout workers?
6
5
  based on file size?
7
6
  fail worker instead of process on bin not present?
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.8.1'
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.3'
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'
@@ -22,7 +22,7 @@ class ImageOptim
22
22
  src.temp_path(src.dirname) do |temp|
23
23
  src.copy(temp)
24
24
  temp.write(read)
25
- temp.rename(src)
25
+ temp.rename(src.to_s)
26
26
  end
27
27
  end
28
28
 
@@ -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
@@ -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
- format_from_name ? [format_from_name.to_sym] : []
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
- resolve_bin!(bin)
72
+ command = build_command!(bin, *arguments)
72
73
 
73
- command = [bin, *arguments].map(&:to_s).shelljoin
74
74
  start = Time.now
75
75
 
76
- system "env PATH=#{@image_optim.env_path.shellescape} nice -n #{@image_optim.nice} #{command} > /dev/null 2>&1"
76
+ success = run_command(command)
77
+
78
+ $stderr << "#{success ? '✓' : '✗'} #{Time.now - start}s #{command}\n" if @image_optim.verbose?
77
79
 
78
- raise SignalException.new($?.termsig) if $?.signaled?
80
+ success
81
+ end
82
+
83
+ # Build command string
84
+ def build_command!(bin, *arguments)
85
+ resolve_bin!(bin)
79
86
 
80
- $stderr << "#{$?.success? ? '✓' : '✗'} #{Time.now - start}s #{command}\n" if @image_optim.verbose?
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
- $?.success?
100
+ success
83
101
  end
84
102
  end
85
103
  end
data/lib/image_optim.rb CHANGED
@@ -248,7 +248,7 @@ end
248
248
 
249
249
  %w[
250
250
  pngcrush pngout optipng advpng
251
- jpegoptim jpegtran
251
+ jhead jpegoptim jpegtran
252
252
  gifsicle
253
253
  ].each do |worker|
254
254
  require "image_optim/worker/#{worker}"
@@ -3,7 +3,7 @@ require 'rspec'
3
3
  require 'image_optim'
4
4
  require 'tempfile'
5
5
 
6
- TEST_IMAGES = (ImageOptim::ImagePath.new(__FILE__).dirname.relative_path_from(Dir.pwd) / 'images').glob('*')
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 = stub(:tmpdir)
212
- symlink = stub(: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 = stub(:tmpdir)
253
- symlink = stub(: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.8.1
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-05-26 00:00:00.000000000 Z
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.3
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.3
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