image_optim 0.30.0 → 0.31.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 +4 -4
- data/.github/workflows/check.yml +59 -0
- data/CHANGELOG.markdown +5 -0
- data/CONTRIBUTING.markdown +1 -1
- data/Gemfile +1 -7
- data/README.markdown +17 -8
- data/image_optim.gemspec +2 -2
- data/lib/image_optim/bin_resolver/bin.rb +1 -1
- data/lib/image_optim/cmd.rb +1 -1
- data/lib/image_optim/config.rb +1 -1
- data/lib/image_optim/runner/option_parser.rb +18 -18
- data/lib/image_optim/runner.rb +1 -1
- data/lib/image_optim/worker/advpng.rb +5 -5
- data/lib/image_optim/worker/gifsicle.rb +8 -8
- data/lib/image_optim/worker/jpegoptim.rb +9 -9
- data/lib/image_optim/worker/jpegrecompress.rb +4 -4
- data/lib/image_optim/worker/jpegtran.rb +1 -1
- data/lib/image_optim/worker/optipng.rb +5 -5
- data/lib/image_optim/worker/oxipng.rb +53 -0
- data/lib/image_optim/worker/pngcrush.rb +4 -4
- data/lib/image_optim/worker/pngout.rb +5 -5
- data/lib/image_optim/worker/pngquant.rb +7 -7
- data/lib/image_optim/worker.rb +3 -3
- data/lib/image_optim.rb +1 -1
- data/script/update_worker_options_in_readme +1 -1
- data/spec/image_optim/cache_path_spec.rb +4 -3
- data/spec/image_optim/cmd_spec.rb +6 -0
- data/spec/image_optim/handler_spec.rb +1 -1
- data/spec/image_optim/path_spec.rb +4 -3
- data/spec/image_optim/worker/oxipng_spec.rb +89 -0
- data/spec/image_optim_spec.rb +1 -1
- data/spec/images/invisiblepixels/generate +1 -1
- data/spec/spec_helper.rb +2 -2
- metadata +10 -7
- data/.travis.yml +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e80e82d67e3434476f0bdf332b14b6369601010aaeef7ab7b901515d0c03565c
|
4
|
+
data.tar.gz: 16f304385c879cf1afc0073d7d037fb548d6c260f57e2b94cdcdd974f3cd4b4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0395980a804453e3273f779c1217d5dd5b9b7c522e74964070b43f2b3ac433f9f39d5f997a3e9c6594f22d244752910eea1697ac2e570de34e6b0641555b9e8
|
7
|
+
data.tar.gz: 9de0bebe89071d3aa7bd506b02dc684b53d998dcfccab1debad72bdc244e6fbae6eb0ac2d5871359fe2ffa5cb9e7c5ae8a7dc98829e00e8abef49c017ac3b00c
|
@@ -0,0 +1,59 @@
|
|
1
|
+
name: check
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
pull_request:
|
5
|
+
schedule:
|
6
|
+
- cron: 45 4 * * 2
|
7
|
+
jobs:
|
8
|
+
check:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
strategy:
|
11
|
+
matrix:
|
12
|
+
ruby:
|
13
|
+
- '2.0'
|
14
|
+
- '2.1'
|
15
|
+
- '2.2'
|
16
|
+
- '2.3'
|
17
|
+
- '2.4'
|
18
|
+
- '2.5'
|
19
|
+
- '2.6'
|
20
|
+
- '2.7'
|
21
|
+
- '3.0'
|
22
|
+
- jruby-9.2
|
23
|
+
fail-fast: false
|
24
|
+
steps:
|
25
|
+
- uses: actions/checkout@v2
|
26
|
+
- uses: ruby/setup-ruby@v1
|
27
|
+
with:
|
28
|
+
ruby-version: "${{ matrix.ruby }}"
|
29
|
+
bundler-cache: true
|
30
|
+
- run: sudo npm install -g svgo
|
31
|
+
- run: curl -L "https://www.jonof.id.au/files/kenutils/pngout-20200115-linux.tar.gz" | sudo tar -xz -C /usr/local/bin --strip-components 2 --wildcards '*/amd64/pngout'
|
32
|
+
- run: curl -L "https://github.com/shssoichiro/oxipng/releases/download/v4.0.3/oxipng-4.0.3-x86_64-unknown-linux-musl.tar.gz" | tar -xz -C /usr/local/bin --strip-components 1 --wildcards '*oxipng'
|
33
|
+
- run: bundle exec image_optim --info
|
34
|
+
- run: bundle exec rspec
|
35
|
+
coverage:
|
36
|
+
runs-on: ubuntu-latest
|
37
|
+
env:
|
38
|
+
CC_TEST_REPORTER_ID: b433c6540d220a2da0663670c9b260806bafdb3a43c6f22b2e81bfb1f87b12fe
|
39
|
+
steps:
|
40
|
+
- uses: actions/checkout@v2
|
41
|
+
- uses: ruby/setup-ruby@v1
|
42
|
+
with:
|
43
|
+
ruby-version: '3.0'
|
44
|
+
bundler-cache: true
|
45
|
+
- run: sudo npm install -g svgo
|
46
|
+
- run: curl -L "https://www.jonof.id.au/files/kenutils/pngout-20200115-linux.tar.gz" | sudo tar -xz -C /usr/local/bin --strip-components 2 --wildcards '*/amd64/pngout'
|
47
|
+
- run: curl -L "https://github.com/shssoichiro/oxipng/releases/download/v4.0.3/oxipng-4.0.3-x86_64-unknown-linux-musl.tar.gz" | tar -xz -C /usr/local/bin --strip-components 1 --wildcards '*oxipng'
|
48
|
+
- uses: paambaati/codeclimate-action@v2.7.5
|
49
|
+
with:
|
50
|
+
coverageCommand: bundle exec rspec
|
51
|
+
rubocop:
|
52
|
+
runs-on: ubuntu-latest
|
53
|
+
steps:
|
54
|
+
- uses: actions/checkout@v2
|
55
|
+
- uses: ruby/setup-ruby@v1
|
56
|
+
with:
|
57
|
+
ruby-version: '3.0'
|
58
|
+
bundler-cache: true
|
59
|
+
- run: bundle exec rubocop
|
data/CHANGELOG.markdown
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
## unreleased
|
4
4
|
|
5
|
+
## v0.31.0 (2021-10-03)
|
6
|
+
|
7
|
+
* Add support for Oxipng [#167](https://github.com/toy/image_optim/issues/167) [#190](https://github.com/toy/image_optim/pull/190) [@oblakeerickson](https://github.com/oblakeerickson)
|
8
|
+
* Fix `TypeError: can't convert ImageOptim::Timer into Float` with Ruby 3 [#194](https://github.com/toy/image_optim/pull/194) [@yahonda](https://github.com/yahonda)
|
9
|
+
|
5
10
|
## v0.30.0 (2021-05-11)
|
6
11
|
|
7
12
|
* Add `timeout` option to restrict maximum time spent on every image [#21](https://github.com/toy/image_optim/issues/21) [#148](https://github.com/toy/image_optim/pull/148) [#149](https://github.com/toy/image_optim/pull/149) [#162](https://github.com/toy/image_optim/pull/162) [#184](https://github.com/toy/image_optim/pull/184) [#189](https://github.com/toy/image_optim/pull/189) [@tgxworld](https://github.com/tgxworld) [@oblakeerickson](https://github.com/oblakeerickson) [@toy](https://github.com/toy)
|
data/CONTRIBUTING.markdown
CHANGED
@@ -11,4 +11,4 @@
|
|
11
11
|
* Rebase on master and squash commits to logical units
|
12
12
|
* Push your branch: `git push origin awesome-changes`
|
13
13
|
* Create pull request
|
14
|
-
* Check if [
|
14
|
+
* Check if [github actions workflow is happy](https://github.com/toy/image_optim_pack/actions/workflows/check.yml)
|
data/Gemfile
CHANGED
@@ -4,14 +4,8 @@ source 'https://rubygems.org'
|
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
-
if ENV['
|
7
|
+
if ENV['CC_TEST_REPORTER_ID']
|
8
8
|
group :test do
|
9
9
|
gem 'simplecov'
|
10
|
-
|
11
|
-
gem 'codeclimate-test-reporter'
|
12
10
|
end
|
13
11
|
end
|
14
|
-
|
15
|
-
if ENV['CHECK_RUBIES']
|
16
|
-
gem 'travis_check_rubies', '~> 0.2'
|
17
|
-
end
|
data/README.markdown
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
[](https://rubygems.org/gems/image_optim)
|
2
|
+
[](https://github.com/toy/image_optim/actions/workflows/check.yml)
|
3
|
+
[](https://ci.appveyor.com/project/toy/image-optim)
|
4
|
+
[](https://codeclimate.com/github/toy/image_optim)
|
5
|
+
[](https://codeclimate.com/github/toy/image_optim)
|
6
|
+
[](https://depfu.com/github/toy/image_optim)
|
7
|
+
[](https://inch-ci.org/github/toy/image_optim)
|
8
8
|
|
9
9
|
# image_optim
|
10
10
|
|
@@ -18,6 +18,7 @@ Command line tool and ruby interface to optimize (lossless compress, optionally
|
|
18
18
|
* [jpeg-recompress](https://github.com/danielgtaylor/jpeg-archive#jpeg-recompress)
|
19
19
|
* jpegtran from [Independent JPEG Group's JPEG library](http://www.ijg.org/)
|
20
20
|
* [optipng](http://optipng.sourceforge.net/)
|
21
|
+
* [oxipng](https://github.com/shssoichiro/oxipng)
|
21
22
|
* [pngcrush](http://pmt.sourceforge.net/pngcrush/)
|
22
23
|
* [pngout](http://www.advsys.net/ken/util/pngout.htm)
|
23
24
|
* [pngquant](http://pngquant.org/)
|
@@ -60,7 +61,7 @@ With version:
|
|
60
61
|
|
61
62
|
<!---<update-version>-->
|
62
63
|
```ruby
|
63
|
-
gem 'image_optim', '~> 0.
|
64
|
+
gem 'image_optim', '~> 0.31'
|
64
65
|
```
|
65
66
|
<!---</update-version>-->
|
66
67
|
|
@@ -166,6 +167,14 @@ sudo port install advancecomp gifsicle jhead jpegoptim jpeg optipng pngcrush png
|
|
166
167
|
brew install advancecomp gifsicle jhead jpegoptim jpeg optipng pngcrush pngquant jonof/kenutils/pngout
|
167
168
|
```
|
168
169
|
|
170
|
+
### oxipng installation (optional)
|
171
|
+
|
172
|
+
Unless it is available in your chosen package manager, can be installed using cargo:
|
173
|
+
|
174
|
+
```bash
|
175
|
+
cargo install oxipng
|
176
|
+
```
|
177
|
+
|
169
178
|
### pngout installation (optional)
|
170
179
|
|
171
180
|
If you installed the dependencies via brew, pngout should be installed already. Otherwise, you can install `pngout` by downloading and installing the [binary versions](http://www.jonof.id.au/kenutils).
|
data/image_optim.gemspec
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'image_optim'
|
5
|
-
s.version = '0.
|
6
|
-
s.summary = %q{Command line tool and ruby interface to optimize (lossless compress, optionally lossy) jpeg, png, gif and svg images using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, pngcrush, pngout, pngquant, svgo)}
|
5
|
+
s.version = '0.31.0'
|
6
|
+
s.summary = %q{Command line tool and ruby interface to optimize (lossless compress, optionally lossy) jpeg, png, gif and svg images using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, oxipng, pngcrush, pngout, pngquant, svgo)}
|
7
7
|
s.homepage = "https://github.com/toy/#{s.name}"
|
8
8
|
s.authors = ['Ivan Kuchin']
|
9
9
|
s.license = 'MIT'
|
@@ -109,7 +109,7 @@ class ImageOptim
|
|
109
109
|
case name
|
110
110
|
when :advpng
|
111
111
|
capture("#{escaped_path} --version 2> #{Path::NULL}")[/\bv(\d+(\.\d+)+|none)/, 1]
|
112
|
-
when :gifsicle, :jpegoptim, :optipng
|
112
|
+
when :gifsicle, :jpegoptim, :optipng, :oxipng
|
113
113
|
capture("#{escaped_path} --version 2> #{Path::NULL}")[/\d+(\.\d+)+/]
|
114
114
|
when :svgo, :pngquant
|
115
115
|
capture("#{escaped_path} --version 2>&1")[/\A\d+(\.\d+)+/]
|
data/lib/image_optim/cmd.rb
CHANGED
data/lib/image_optim/config.rb
CHANGED
@@ -38,7 +38,7 @@ class ImageOptim
|
|
38
38
|
columns = terminal_columns - 1
|
39
39
|
# 1 for distance between summary and description
|
40
40
|
# 2 for additional indent
|
41
|
-
wrapped_indent = summary_indent + ' ' * (summary_width + 1 + 2)
|
41
|
+
wrapped_indent = summary_indent + (' ' * (summary_width + 1 + 2))
|
42
42
|
wrapped_width = columns - wrapped_indent.length
|
43
43
|
# don't try to wrap if there is too little space for description
|
44
44
|
return text if wrapped_width < 20
|
@@ -84,7 +84,7 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
84
84
|
ImageOptim::TrueFalseNil.add_to_option_parser(op)
|
85
85
|
ImageOptim::NonNegativeIntegerRange.add_to_option_parser(op)
|
86
86
|
|
87
|
-
op.banner = <<-TEXT.gsub(/^\s*\|/, '')
|
87
|
+
op.banner = <<-TEXT.gsub(/^\s*\|/, '') # rubocop:disable Layout/DotPosition
|
88
88
|
|#{ImageOptim.full_version}
|
89
89
|
|
|
90
90
|
|Usage:
|
@@ -97,29 +97,29 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
97
97
|
TEXT
|
98
98
|
|
99
99
|
op.on('--config-paths PATH1,PATH2', Array, 'Config paths to use instead of '\
|
100
|
-
|
100
|
+
'default ones') do |paths|
|
101
101
|
options[:config_paths] = paths
|
102
102
|
end
|
103
103
|
|
104
104
|
op.separator nil
|
105
105
|
|
106
106
|
op.on('-r', '-R', '--recursive', 'Recursively scan directories '\
|
107
|
-
|
107
|
+
'for images') do |recursive|
|
108
108
|
options[:recursive] = recursive
|
109
109
|
end
|
110
110
|
|
111
111
|
op.on("--exclude-dir 'GLOB'", 'Glob for excluding directories '\
|
112
|
-
|
112
|
+
'(defaults to .*)') do |glob|
|
113
113
|
options[:exclude_dir_glob] = glob
|
114
114
|
end
|
115
115
|
|
116
116
|
op.on("--exclude-file 'GLOB'", 'Glob for excluding files '\
|
117
|
-
|
117
|
+
'(defaults to .*)') do |glob|
|
118
118
|
options[:exclude_file_glob] = glob
|
119
119
|
end
|
120
120
|
|
121
121
|
op.on("--exclude 'GLOB'", 'Set glob for excluding both directories and '\
|
122
|
-
|
122
|
+
'files') do |glob|
|
123
123
|
options[:exclude_file_glob] = options[:exclude_dir_glob] = glob
|
124
124
|
end
|
125
125
|
|
@@ -130,19 +130,19 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
130
130
|
end
|
131
131
|
|
132
132
|
op.on('--[no-]threads N', Integer, 'Number of threads or disable '\
|
133
|
-
|
133
|
+
'(defaults to number of processors)') do |threads|
|
134
134
|
options[:threads] = threads
|
135
135
|
end
|
136
136
|
|
137
137
|
op.on('--[no-]nice N', Integer, 'Nice level, priority of all used tools '\
|
138
|
-
|
139
|
-
|
138
|
+
'with higher value meaning lower priority, in range -20..19, negative '\
|
139
|
+
'values can be set only if run by root user (defaults to 10)') do |nice|
|
140
140
|
options[:nice] = nice
|
141
141
|
end
|
142
142
|
|
143
143
|
op.on('--[no-]pack', 'Require image_optim_pack or disable it, '\
|
144
|
-
|
145
|
-
|
144
|
+
'by default image_optim_pack will be used if available, '\
|
145
|
+
'will turn on skip-missing-workers unless explicitly disabled') do |pack|
|
146
146
|
options[:pack] = pack
|
147
147
|
end
|
148
148
|
|
@@ -150,12 +150,12 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
150
150
|
op.separator ' Caching:'
|
151
151
|
|
152
152
|
op.on('--cache-dir DIR', 'Cache optimized images '\
|
153
|
-
|
153
|
+
'into the specified directory') do |cache_dir|
|
154
154
|
options[:cache_dir] = cache_dir
|
155
155
|
end
|
156
156
|
|
157
157
|
op.on('--cache-worker-digests', 'Cache worker digests '\
|
158
|
-
|
158
|
+
'(updating workers invalidates cache)') do |cache_worker_digests|
|
159
159
|
options[:cache_worker_digests] = cache_worker_digests
|
160
160
|
end
|
161
161
|
|
@@ -163,7 +163,7 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
163
163
|
op.separator ' Disabling workers:'
|
164
164
|
|
165
165
|
op.on('--[no-]skip-missing-workers', 'Skip workers with missing or '\
|
166
|
-
|
166
|
+
'problematic binaries') do |skip|
|
167
167
|
options[:skip_missing_workers] = skip
|
168
168
|
end
|
169
169
|
|
@@ -178,7 +178,7 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
178
178
|
op.separator ' Worker options:'
|
179
179
|
|
180
180
|
op.on('--allow-lossy', 'Allow lossy workers and '\
|
181
|
-
|
181
|
+
'optimizations') do |allow_lossy|
|
182
182
|
options[:allow_lossy] = allow_lossy
|
183
183
|
end
|
184
184
|
|
@@ -230,8 +230,8 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
230
230
|
op.separator ' Common options:'
|
231
231
|
|
232
232
|
op.on_tail('-v', '--verbose', 'Verbose output (show global and worker '\
|
233
|
-
|
234
|
-
|
233
|
+
'config, binary resolution log, information about each tool invocation, '\
|
234
|
+
'backtrace of exception)') do
|
235
235
|
options[:verbose] = true
|
236
236
|
end
|
237
237
|
|
data/lib/image_optim/runner.rb
CHANGED
@@ -9,11 +9,11 @@ class ImageOptim
|
|
9
9
|
class Advpng < Worker
|
10
10
|
LEVEL_OPTION =
|
11
11
|
option(:level, 4, 'Compression level: '\
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
'`0` - don\'t compress, '\
|
13
|
+
'`1` - fast, '\
|
14
|
+
'`2` - normal, '\
|
15
|
+
'`3` - extra, '\
|
16
|
+
'`4` - extreme') do |v|
|
17
17
|
OptionHelpers.limit_with_range(v.to_i, 0..4)
|
18
18
|
end
|
19
19
|
|
@@ -18,19 +18,19 @@ class ImageOptim
|
|
18
18
|
|
19
19
|
INTERLACE_OPTION =
|
20
20
|
option(:interlace, false, TrueFalseNil, 'Interlace: '\
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
'`true` - interlace on, '\
|
22
|
+
'`false` - interlace off, '\
|
23
|
+
'`nil` - as is in original image '\
|
24
|
+
'(defaults to running two instances, one with interlace off and '\
|
25
|
+
'one with on)') do |v|
|
26
26
|
TrueFalseNil.convert(v)
|
27
27
|
end
|
28
28
|
|
29
29
|
LEVEL_OPTION =
|
30
30
|
option(:level, 3, 'Compression level: '\
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
'`1` - light and fast, '\
|
32
|
+
'`2` - normal, '\
|
33
|
+
'`3` - heavy (slower)') do |v|
|
34
34
|
OptionHelpers.limit_with_range(v.to_i, 1..3)
|
35
35
|
end
|
36
36
|
|
@@ -12,13 +12,13 @@ class ImageOptim
|
|
12
12
|
|
13
13
|
STRIP_OPTION =
|
14
14
|
option(:strip, :all, Array, 'List of markers to strip: '\
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
'`:com`, '\
|
16
|
+
'`:exif`, '\
|
17
|
+
'`:iptc`, '\
|
18
|
+
'`:icc`, '\
|
19
|
+
'`:xmp`, '\
|
20
|
+
'`:none` or '\
|
21
|
+
'`:all`') do |v|
|
22
22
|
values = Array(v).map(&:to_s)
|
23
23
|
known_values = %w[com exif iptc icc xmp none all]
|
24
24
|
unknown_values = values - known_values
|
@@ -30,13 +30,13 @@ class ImageOptim
|
|
30
30
|
|
31
31
|
MAX_QUALITY_OPTION =
|
32
32
|
option(:max_quality, 100, 'Maximum image quality factor '\
|
33
|
-
|
33
|
+
'`0`..`100`, ignored in default/lossless mode') do |v, opt_def|
|
34
34
|
if allow_lossy
|
35
35
|
OptionHelpers.limit_with_range(v.to_i, 0..100)
|
36
36
|
else
|
37
37
|
if v != opt_def.default
|
38
38
|
warn "#{self.class.bin_sym} #{opt_def.name} #{v} ignored " \
|
39
|
-
|
39
|
+
'in lossless mode'
|
40
40
|
end
|
41
41
|
opt_def.default
|
42
42
|
end
|
@@ -28,10 +28,10 @@ class ImageOptim
|
|
28
28
|
|
29
29
|
METHOD_OPTION =
|
30
30
|
option(:method, 'ssim', 'Comparison Metric: '\
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
'`mpe` - Mean pixel error, '\
|
32
|
+
'`ssim` - Structural similarity, '\
|
33
|
+
'`ms-ssim` - Multi-scale structural similarity (slow!), '\
|
34
|
+
'`smallfry` - Linear-weighted BBCQ-like (may be patented)') do |v, opt_def|
|
35
35
|
if %w[mpe ssim ms-ssim smallfry].include? v
|
36
36
|
v
|
37
37
|
else
|
@@ -17,7 +17,7 @@ class ImageOptim
|
|
17
17
|
|
18
18
|
JPEGRESCAN_OPTION =
|
19
19
|
option(:jpegrescan, true, 'Use jpegtran through jpegrescan, '\
|
20
|
-
|
20
|
+
'ignore progressive option'){ |v| !!v }
|
21
21
|
|
22
22
|
def used_bins
|
23
23
|
jpegrescan ? [:jpegtran, :jpegrescan] : [:jpegtran]
|
@@ -10,16 +10,16 @@ class ImageOptim
|
|
10
10
|
class Optipng < Worker
|
11
11
|
LEVEL_OPTION =
|
12
12
|
option(:level, 6, 'Optimization level preset: '\
|
13
|
-
|
14
|
-
|
13
|
+
'`0` is least, '\
|
14
|
+
'`7` is best') do |v|
|
15
15
|
OptionHelpers.limit_with_range(v.to_i, 0..7)
|
16
16
|
end
|
17
17
|
|
18
18
|
INTERLACE_OPTION =
|
19
19
|
option(:interlace, false, TrueFalseNil, 'Interlace: '\
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
'`true` - interlace on, '\
|
21
|
+
'`false` - interlace off, '\
|
22
|
+
'`nil` - as is in original image') do |v|
|
23
23
|
TrueFalseNil.convert(v)
|
24
24
|
end
|
25
25
|
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'image_optim/worker'
|
4
|
+
require 'image_optim/option_helpers'
|
5
|
+
require 'image_optim/true_false_nil'
|
6
|
+
|
7
|
+
class ImageOptim
|
8
|
+
class Worker
|
9
|
+
# https://github.com/shssoichiro/oxipng
|
10
|
+
class Oxipng < Worker
|
11
|
+
LEVEL_OPTION =
|
12
|
+
option(:level, 3, 'Optimization level preset: '\
|
13
|
+
'`0` is least, '\
|
14
|
+
'`6` is best') do |v|
|
15
|
+
OptionHelpers.limit_with_range(v.to_i, 0..6)
|
16
|
+
end
|
17
|
+
|
18
|
+
INTERLACE_OPTION =
|
19
|
+
option(:interlace, false, TrueFalseNil, 'Interlace: '\
|
20
|
+
'`true` - interlace on, '\
|
21
|
+
'`false` - interlace off, '\
|
22
|
+
'`nil` - as is in original image') do |v|
|
23
|
+
TrueFalseNil.convert(v)
|
24
|
+
end
|
25
|
+
|
26
|
+
STRIP_OPTION =
|
27
|
+
option(:strip, true, 'Remove all auxiliary chunks'){ |v| !!v }
|
28
|
+
|
29
|
+
def run_order
|
30
|
+
-4
|
31
|
+
end
|
32
|
+
|
33
|
+
def optimize(src, dst, options = {})
|
34
|
+
src.copy(dst)
|
35
|
+
args = %W[
|
36
|
+
-o #{level}
|
37
|
+
--quiet
|
38
|
+
--
|
39
|
+
#{dst}
|
40
|
+
]
|
41
|
+
args.unshift "-i#{interlace ? 1 : 0}" unless interlace.nil?
|
42
|
+
if strip
|
43
|
+
args.unshift '--strip', 'all'
|
44
|
+
end
|
45
|
+
execute(:oxipng, args, options) && optimized?(src, dst)
|
46
|
+
end
|
47
|
+
|
48
|
+
def optimized?(src, dst)
|
49
|
+
interlace ? dst.size? : super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -8,18 +8,18 @@ class ImageOptim
|
|
8
8
|
class Pngcrush < Worker
|
9
9
|
CHUNKS_OPTION =
|
10
10
|
option(:chunks, :alla, Array, 'List of chunks to remove or '\
|
11
|
-
|
12
|
-
|
11
|
+
'`:alla` - all except tRNS/transparency or '\
|
12
|
+
'`:allb` - all except tRNS and gAMA/gamma') do |v|
|
13
13
|
Array(v).map(&:to_s)
|
14
14
|
end
|
15
15
|
|
16
16
|
FIX_OPTION =
|
17
17
|
option(:fix, false, 'Fix otherwise fatal conditions '\
|
18
|
-
|
18
|
+
'such as bad CRCs'){ |v| !!v }
|
19
19
|
|
20
20
|
BRUTE_OPTION =
|
21
21
|
option(:brute, false, 'Brute force try all methods, '\
|
22
|
-
|
22
|
+
'very time-consuming and generally not worthwhile'){ |v| !!v }
|
23
23
|
|
24
24
|
BLACKEN_OPTION =
|
25
25
|
option(:blacken, true, 'Blacken fully transparent pixels'){ |v| !!v }
|
@@ -12,11 +12,11 @@ class ImageOptim
|
|
12
12
|
|
13
13
|
STRATEGY_OPTION =
|
14
14
|
option(:strategy, 0, 'Strategy: '\
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
'`0` - xtreme, '\
|
16
|
+
'`1` - intense, '\
|
17
|
+
'`2` - longest Match, '\
|
18
|
+
'`3` - huffman Only, '\
|
19
|
+
'`4` - uncompressed') do |v|
|
20
20
|
OptionHelpers.limit_with_range(v.to_i, 0..4)
|
21
21
|
end
|
22
22
|
|
@@ -19,9 +19,9 @@ class ImageOptim
|
|
19
19
|
QUALITY_OPTION =
|
20
20
|
option(:quality, '`100..100`, `0..100` in lossy mode',
|
21
21
|
NonNegativeIntegerRange, 'min..max - don\'t '\
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
'save below min, use less colors below max (both in range `0..100`; '\
|
23
|
+
'in yaml - `!ruby/range 0..100`), ignored in default/lossless '\
|
24
|
+
'mode') do |v, opt_def|
|
25
25
|
if allow_lossy
|
26
26
|
if v == opt_def.default
|
27
27
|
0..100
|
@@ -32,7 +32,7 @@ class ImageOptim
|
|
32
32
|
else
|
33
33
|
if v != opt_def.default
|
34
34
|
warn "#{self.class.bin_sym} #{opt_def.name} #{v} ignored " \
|
35
|
-
|
35
|
+
'in lossless mode'
|
36
36
|
end
|
37
37
|
100..100
|
38
38
|
end
|
@@ -40,9 +40,9 @@ class ImageOptim
|
|
40
40
|
|
41
41
|
SPEED_OPTION =
|
42
42
|
option(:speed, 3, 'speed/quality trade-off: '\
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
'`1` - slow, '\
|
44
|
+
'`3` - default, '\
|
45
|
+
'`11` - fast & rough') do |v|
|
46
46
|
OptionHelpers.limit_with_range(v.to_i, 1..11)
|
47
47
|
end
|
48
48
|
|
data/lib/image_optim/worker.rb
CHANGED
@@ -105,7 +105,7 @@ class ImageOptim
|
|
105
105
|
return if unknown_options.empty?
|
106
106
|
|
107
107
|
fail ConfigurationError, "unknown options #{unknown_options.inspect} "\
|
108
|
-
|
108
|
+
"for #{self}"
|
109
109
|
end
|
110
110
|
|
111
111
|
# Forward bin resolving to image_optim
|
@@ -118,8 +118,8 @@ class ImageOptim
|
|
118
118
|
def wrap_resolver_error_message(message)
|
119
119
|
name = self.class.bin_sym
|
120
120
|
"#{name} worker: #{message}; please provide proper binary or "\
|
121
|
-
|
122
|
-
|
121
|
+
"disable this worker (--no-#{name} argument or "\
|
122
|
+
"`:#{name} => false` through options)"
|
123
123
|
end
|
124
124
|
|
125
125
|
# Run command setting priority and hiding output
|
data/lib/image_optim.rb
CHANGED
@@ -29,7 +29,7 @@ end
|
|
29
29
|
def write_marked(io)
|
30
30
|
io.puts BEGIN_MARKER
|
31
31
|
io.puts '<!-- markdown for worker options is generated by '\
|
32
|
-
|
32
|
+
"`#{Pathname($PROGRAM_NAME).cleanpath}` -->"
|
33
33
|
io.puts
|
34
34
|
|
35
35
|
ImageOptim::Worker.klasses.sort_by(&:name).each do |klass|
|
@@ -42,13 +42,14 @@ describe ImageOptim::CachePath do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'does not preserve mtime of destination file' do
|
45
|
-
time = src.mtime
|
45
|
+
time = src.mtime - 1000
|
46
|
+
dst.utime(time, time)
|
46
47
|
|
47
|
-
|
48
|
+
time = dst.mtime
|
48
49
|
|
49
50
|
src.replace(dst)
|
50
51
|
|
51
|
-
expect(dst.mtime).
|
52
|
+
expect(dst.mtime).to_not eq(time)
|
52
53
|
end
|
53
54
|
|
54
55
|
it 'changes inode of destination', skip: SkipConditions[:inodes_support] do
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require 'image_optim/cmd'
|
5
|
+
require 'image_optim/timer'
|
5
6
|
|
6
7
|
describe ImageOptim::Cmd do
|
7
8
|
before do
|
@@ -48,6 +49,11 @@ describe ImageOptim::Cmd do
|
|
48
49
|
expect(Cmd.run('sh -c "exit 66"', timeout: 1)).to eq(false)
|
49
50
|
end
|
50
51
|
|
52
|
+
it 'returns process success status when timeout is instance of ImageOptim::Timer' do
|
53
|
+
timeout = ImageOptim::Timer.new(1.0)
|
54
|
+
expect(Cmd.run('sh -c "exit 0"', timeout: timeout)).to eq(true)
|
55
|
+
end
|
56
|
+
|
51
57
|
it 'raises SignalException if process terminates after signal', skip: SkipConditions[:signals_support] do
|
52
58
|
expect_int_exception do
|
53
59
|
Cmd.run('kill -s INT $$', timeout: 1)
|
@@ -9,7 +9,7 @@ describe ImageOptim::Handler do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'uses original as source for first conversion '\
|
12
|
-
|
12
|
+
'and two temp files for further conversions' do
|
13
13
|
original = double(:original)
|
14
14
|
allow(original).to receive(:respond_to?).with(:temp_path).and_return(true)
|
15
15
|
|
@@ -86,13 +86,14 @@ describe ImageOptim::Path do
|
|
86
86
|
end
|
87
87
|
|
88
88
|
it 'does not preserve mtime of destination file' do
|
89
|
-
time = src.mtime
|
89
|
+
time = src.mtime - 1000
|
90
|
+
dst.utime(time, time)
|
90
91
|
|
91
|
-
|
92
|
+
time = dst.mtime
|
92
93
|
|
93
94
|
src.replace(dst)
|
94
95
|
|
95
|
-
expect(dst.mtime).
|
96
|
+
expect(dst.mtime).to_not eq(time)
|
96
97
|
end
|
97
98
|
|
98
99
|
it 'changes inode of destination', skip: SkipConditions[:inodes_support] do
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'image_optim/worker/oxipng'
|
5
|
+
require 'image_optim/path'
|
6
|
+
|
7
|
+
describe ImageOptim::Worker::Oxipng do
|
8
|
+
describe 'strip option' do
|
9
|
+
subject{ described_class.new(ImageOptim.new, options) }
|
10
|
+
|
11
|
+
let(:options){ {} }
|
12
|
+
let(:src){ instance_double(ImageOptim::Path, copy: nil) }
|
13
|
+
let(:dst){ instance_double(ImageOptim::Path) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
oxipng_bin = instance_double(ImageOptim::BinResolver::Bin)
|
17
|
+
allow(subject).to receive(:resolve_bin!).
|
18
|
+
with(:oxipng).and_return(oxipng_bin)
|
19
|
+
|
20
|
+
allow(subject).to receive(:optimized?)
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'by default' do
|
24
|
+
it 'should add --strip all to arguments' do
|
25
|
+
expect(subject).to receive(:execute) do |_bin, *args|
|
26
|
+
expect(args.join(' ')).to match(/(^| )--strip all($| )/)
|
27
|
+
end
|
28
|
+
|
29
|
+
subject.optimize(src, dst)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when strip is disabled' do
|
34
|
+
let(:options){ {strip: false} }
|
35
|
+
|
36
|
+
it 'should not add --strip all to arguments' do
|
37
|
+
expect(subject).to receive(:execute) do |_bin, *args|
|
38
|
+
expect(args.join(' ')).not_to match(/(^| )--strip all($| )/)
|
39
|
+
end
|
40
|
+
|
41
|
+
subject.optimize(src, dst)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#optimized?' do
|
47
|
+
let(:src){ instance_double(ImageOptim::Path, src_options) }
|
48
|
+
let(:dst){ instance_double(ImageOptim::Path, dst_options) }
|
49
|
+
let(:src_options){ {size: 10} }
|
50
|
+
let(:dst_options){ {size?: 9} }
|
51
|
+
let(:instance){ described_class.new(ImageOptim.new, instance_options) }
|
52
|
+
let(:instance_options){ {} }
|
53
|
+
|
54
|
+
subject{ instance.optimized?(src, dst) }
|
55
|
+
|
56
|
+
context 'when interlace option is enabled' do
|
57
|
+
let(:instance_options){ {interlace: true} }
|
58
|
+
|
59
|
+
context 'when dst is empty' do
|
60
|
+
let(:dst_options){ {size?: nil} }
|
61
|
+
it{ is_expected.to be_falsy }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when dst is not empty' do
|
65
|
+
let(:dst_options){ {size?: 20} }
|
66
|
+
it{ is_expected.to be_truthy }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when interlace option is disabled' do
|
71
|
+
let(:instance_options){ {interlace: false} }
|
72
|
+
|
73
|
+
context 'when dst is empty' do
|
74
|
+
let(:dst_options){ {size?: nil} }
|
75
|
+
it{ is_expected.to be_falsy }
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when dst is greater than or equal to src' do
|
79
|
+
let(:dst_options){ {size?: 10} }
|
80
|
+
it{ is_expected.to be_falsy }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when dst is less than src' do
|
84
|
+
let(:dst_options){ {size?: 9} }
|
85
|
+
it{ is_expected.to be_truthy }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/spec/image_optim_spec.rb
CHANGED
@@ -253,7 +253,7 @@ describe ImageOptim do
|
|
253
253
|
|
254
254
|
describe 'given block' do
|
255
255
|
it 'optimizes images, yields path and result for each and '\
|
256
|
-
|
256
|
+
'returns array of yield results' do
|
257
257
|
image_optim = ImageOptim.new
|
258
258
|
results = test_images.map do |src|
|
259
259
|
dst = double
|
@@ -17,7 +17,7 @@ IO.popen(%W[
|
|
17
17
|
].shelljoin, 'w') do |f|
|
18
18
|
side.times do |a|
|
19
19
|
side.times do |b|
|
20
|
-
alpha = [0, 1, 0x7f, 0xff][(a / 8 + b / 8) % 4]
|
20
|
+
alpha = [0, 1, 0x7f, 0xff][((a / 8) + (b / 8)) % 4]
|
21
21
|
f << [rand(256), rand(256), rand(256), alpha].pack('C*')
|
22
22
|
end
|
23
23
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
if ENV['
|
3
|
+
if ENV['CC_TEST_REPORTER_ID']
|
4
4
|
require 'simplecov'
|
5
5
|
SimpleCov.start
|
6
6
|
end
|
@@ -71,7 +71,7 @@ RSpec::Matchers.define :be_similar_to do |expected, max_difference|
|
|
71
71
|
end
|
72
72
|
failure_message do |actual|
|
73
73
|
"expected #{actual} to have at most #{max_difference} difference from "\
|
74
|
-
|
74
|
+
"#{expected}, got normalized root-mean-square error of #{@diff}"
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
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.31.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: 2021-
|
11
|
+
date: 2021-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fspath
|
@@ -168,10 +168,10 @@ extensions: []
|
|
168
168
|
extra_rdoc_files: []
|
169
169
|
files:
|
170
170
|
- ".appveyor.yml"
|
171
|
+
- ".github/workflows/check.yml"
|
171
172
|
- ".gitignore"
|
172
173
|
- ".pre-commit-hooks.yaml"
|
173
174
|
- ".rubocop.yml"
|
174
|
-
- ".travis.yml"
|
175
175
|
- CHANGELOG.markdown
|
176
176
|
- CONTRIBUTING.markdown
|
177
177
|
- Gemfile
|
@@ -216,6 +216,7 @@ files:
|
|
216
216
|
- lib/image_optim/worker/jpegrecompress.rb
|
217
217
|
- lib/image_optim/worker/jpegtran.rb
|
218
218
|
- lib/image_optim/worker/optipng.rb
|
219
|
+
- lib/image_optim/worker/oxipng.rb
|
219
220
|
- lib/image_optim/worker/pngcrush.rb
|
220
221
|
- lib/image_optim/worker/pngout.rb
|
221
222
|
- lib/image_optim/worker/pngquant.rb
|
@@ -246,6 +247,7 @@ files:
|
|
246
247
|
- spec/image_optim/timer_spec.rb
|
247
248
|
- spec/image_optim/worker/jpegrecompress_spec.rb
|
248
249
|
- spec/image_optim/worker/optipng_spec.rb
|
250
|
+
- spec/image_optim/worker/oxipng_spec.rb
|
249
251
|
- spec/image_optim/worker/pngquant_spec.rb
|
250
252
|
- spec/image_optim/worker_spec.rb
|
251
253
|
- spec/image_optim_spec.rb
|
@@ -284,7 +286,7 @@ licenses:
|
|
284
286
|
metadata:
|
285
287
|
bug_tracker_uri: https://github.com/toy/image_optim/issues
|
286
288
|
changelog_uri: https://github.com/toy/image_optim/blob/master/CHANGELOG.markdown
|
287
|
-
documentation_uri: https://www.rubydoc.info/gems/image_optim/0.
|
289
|
+
documentation_uri: https://www.rubydoc.info/gems/image_optim/0.31.0
|
288
290
|
source_code_uri: https://github.com/toy/image_optim
|
289
291
|
post_install_message: |
|
290
292
|
Rails image assets optimization is extracted into image_optim_rails gem
|
@@ -303,13 +305,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
303
305
|
- !ruby/object:Gem::Version
|
304
306
|
version: '0'
|
305
307
|
requirements: []
|
306
|
-
rubygems_version: 3.2.
|
308
|
+
rubygems_version: 3.2.28
|
307
309
|
signing_key:
|
308
310
|
specification_version: 4
|
309
311
|
summary: Command line tool and ruby interface to optimize (lossless compress, optionally
|
310
312
|
lossy) jpeg, png, gif and svg images using external utilities (advpng, gifsicle,
|
311
|
-
jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng,
|
312
|
-
pngquant, svgo)
|
313
|
+
jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, oxipng, pngcrush,
|
314
|
+
pngout, pngquant, svgo)
|
313
315
|
test_files:
|
314
316
|
- spec/image_optim/bin_resolver/comparable_condition_spec.rb
|
315
317
|
- spec/image_optim/bin_resolver/simple_version_spec.rb
|
@@ -332,6 +334,7 @@ test_files:
|
|
332
334
|
- spec/image_optim/timer_spec.rb
|
333
335
|
- spec/image_optim/worker/jpegrecompress_spec.rb
|
334
336
|
- spec/image_optim/worker/optipng_spec.rb
|
337
|
+
- spec/image_optim/worker/oxipng_spec.rb
|
335
338
|
- spec/image_optim/worker/pngquant_spec.rb
|
336
339
|
- spec/image_optim/worker_spec.rb
|
337
340
|
- spec/image_optim_spec.rb
|
data/.travis.yml
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
dist: xenial
|
2
|
-
language: ruby
|
3
|
-
cache:
|
4
|
-
bundler: true
|
5
|
-
directories:
|
6
|
-
- $(npm root)
|
7
|
-
- ~/bin
|
8
|
-
rvm:
|
9
|
-
- '1.9.3-p551'
|
10
|
-
- '2.0.0-p648'
|
11
|
-
- '2.1.10'
|
12
|
-
- '2.2.10'
|
13
|
-
- '2.3.8'
|
14
|
-
- '2.4.10'
|
15
|
-
- '2.5.9'
|
16
|
-
- '2.6.7'
|
17
|
-
- '2.7.3'
|
18
|
-
- '3.0.1'
|
19
|
-
- 'jruby-9.2.14.0'
|
20
|
-
script:
|
21
|
-
- bundle exec image_optim --info
|
22
|
-
- bundle exec rspec
|
23
|
-
before_install:
|
24
|
-
- 'echo "gem: --no-ri --no-rdoc --no-document" > ~/.gemrc'
|
25
|
-
- gem install rubygems-update || gem install rubygems-update --version '< 3'
|
26
|
-
- update_rubygems
|
27
|
-
- gem install bundler || gem install bundler --version '< 2'
|
28
|
-
- nvm install stable
|
29
|
-
- command -v svgo || npm install -g svgo
|
30
|
-
- mkdir -p ~/bin
|
31
|
-
- command -v pngout || curl -L "https://www.jonof.id.au/files/kenutils/pngout-20200115-linux.tar.gz" | tar -xz -C ~/bin --strip-components 2 --wildcards '*/amd64/pngout'
|
32
|
-
matrix:
|
33
|
-
include:
|
34
|
-
- env: CODECLIMATE=1
|
35
|
-
rvm: '2.4.10'
|
36
|
-
after_success: bundle exec codeclimate-test-reporter
|
37
|
-
- env: RUBOCOP=1
|
38
|
-
rvm: '2.4.10'
|
39
|
-
script: bundle exec rubocop
|
40
|
-
before_install: gem update --system && gem install bundler
|
41
|
-
- env: CHECK_RUBIES=1
|
42
|
-
rvm: '2.4.10'
|
43
|
-
script: bundle exec travis_check_rubies
|
44
|
-
before_install: gem update --system && gem install bundler
|
45
|
-
addons:
|
46
|
-
code_climate:
|
47
|
-
repo_token:
|
48
|
-
secure: FDikT3JnOJHOAFSaKwVPB1VOphU0sSzAnzQ+YjSt9XFE+9uFQHth/j4rFoVoqALhCj+47trv6spwkbcSjGCVzm+87OI80GkLCCzNjDOz2W4sP9JWItVgS1VoWW+ioPxpLgpguTb3wutwKOUMEAf+40EX657ZiHumM7nNHrF+RCU=
|