discourse_image_optim 0.24.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.appveyor.yml +46 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +110 -0
- data/.travis.yml +42 -0
- data/CHANGELOG.markdown +316 -0
- data/CONTRIBUTING.markdown +11 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +358 -0
- data/Vagrantfile +38 -0
- data/bin/image_optim +28 -0
- data/image_optim.gemspec +34 -0
- data/lib/image_optim.rb +267 -0
- data/lib/image_optim/bin_resolver.rb +142 -0
- data/lib/image_optim/bin_resolver/bin.rb +115 -0
- data/lib/image_optim/bin_resolver/comparable_condition.rb +60 -0
- data/lib/image_optim/bin_resolver/error.rb +6 -0
- data/lib/image_optim/bin_resolver/simple_version.rb +31 -0
- data/lib/image_optim/cache.rb +72 -0
- data/lib/image_optim/cache_path.rb +16 -0
- data/lib/image_optim/cmd.rb +122 -0
- data/lib/image_optim/config.rb +219 -0
- data/lib/image_optim/configuration_error.rb +3 -0
- data/lib/image_optim/handler.rb +57 -0
- data/lib/image_optim/hash_helpers.rb +45 -0
- data/lib/image_optim/image_meta.rb +20 -0
- data/lib/image_optim/non_negative_integer_range.rb +11 -0
- data/lib/image_optim/optimized_path.rb +25 -0
- data/lib/image_optim/option_definition.rb +38 -0
- data/lib/image_optim/option_helpers.rb +17 -0
- data/lib/image_optim/path.rb +70 -0
- data/lib/image_optim/runner.rb +139 -0
- data/lib/image_optim/runner/glob_helpers.rb +45 -0
- data/lib/image_optim/runner/option_parser.rb +246 -0
- data/lib/image_optim/space.rb +29 -0
- data/lib/image_optim/true_false_nil.rb +16 -0
- data/lib/image_optim/worker.rb +170 -0
- data/lib/image_optim/worker/advpng.rb +37 -0
- data/lib/image_optim/worker/class_methods.rb +107 -0
- data/lib/image_optim/worker/gifsicle.rb +65 -0
- data/lib/image_optim/worker/jhead.rb +47 -0
- data/lib/image_optim/worker/jpegoptim.rb +63 -0
- data/lib/image_optim/worker/jpegrecompress.rb +49 -0
- data/lib/image_optim/worker/jpegtran.rb +48 -0
- data/lib/image_optim/worker/optipng.rb +53 -0
- data/lib/image_optim/worker/pngcrush.rb +56 -0
- data/lib/image_optim/worker/pngout.rb +40 -0
- data/lib/image_optim/worker/pngquant.rb +61 -0
- data/lib/image_optim/worker/svgo.rb +34 -0
- data/script/template/jquery-2.1.3.min.js +4 -0
- data/script/template/sortable-0.6.0.min.js +2 -0
- data/script/template/worker_analysis.erb +254 -0
- data/script/update_worker_options_in_readme +59 -0
- data/script/worker_analysis +589 -0
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +37 -0
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +65 -0
- data/spec/image_optim/bin_resolver_spec.rb +290 -0
- data/spec/image_optim/cache_path_spec.rb +57 -0
- data/spec/image_optim/cache_spec.rb +162 -0
- data/spec/image_optim/cmd_spec.rb +93 -0
- data/spec/image_optim/config_spec.rb +254 -0
- data/spec/image_optim/handler_spec.rb +90 -0
- data/spec/image_optim/hash_helpers_spec.rb +74 -0
- data/spec/image_optim/image_meta_spec.rb +61 -0
- data/spec/image_optim/optimized_path_spec.rb +58 -0
- data/spec/image_optim/option_definition_spec.rb +138 -0
- data/spec/image_optim/option_helpers_spec.rb +25 -0
- data/spec/image_optim/path_spec.rb +103 -0
- data/spec/image_optim/runner/glob_helpers_spec.rb +21 -0
- data/spec/image_optim/runner/option_parser_spec.rb +105 -0
- data/spec/image_optim/space_spec.rb +23 -0
- data/spec/image_optim/worker/optipng_spec.rb +102 -0
- data/spec/image_optim/worker/pngquant_spec.rb +67 -0
- data/spec/image_optim/worker_spec.rb +303 -0
- data/spec/image_optim_spec.rb +259 -0
- data/spec/images/broken_jpeg +1 -0
- data/spec/images/comparison.png +0 -0
- data/spec/images/decompressed.jpeg +0 -0
- data/spec/images/icecream.gif +0 -0
- data/spec/images/image.jpg +0 -0
- data/spec/images/invisiblepixels/generate +24 -0
- data/spec/images/invisiblepixels/image.png +0 -0
- data/spec/images/lena.jpg +0 -0
- 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 +23 -0
- data/spec/images/orient/original.jpg +0 -0
- data/spec/images/quant/64.png +0 -0
- data/spec/images/quant/generate +25 -0
- data/spec/images/rails.png +0 -0
- data/spec/images/test.svg +3 -0
- data/spec/images/transparency1.png +0 -0
- data/spec/images/transparency2.png +0 -0
- data/spec/images/vergroessert.jpg +0 -0
- data/spec/spec_helper.rb +93 -0
- metadata +281 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'image_optim/worker'
|
2
|
+
require 'image_optim/option_helpers'
|
3
|
+
|
4
|
+
class ImageOptim
|
5
|
+
class Worker
|
6
|
+
# http://advancemame.sourceforge.net/doc-advpng.html
|
7
|
+
class Advpng < Worker
|
8
|
+
LEVEL_OPTION =
|
9
|
+
option(:level, 4, 'Compression level: '\
|
10
|
+
'`0` - don\'t compress, '\
|
11
|
+
'`1` - fast, '\
|
12
|
+
'`2` - normal, '\
|
13
|
+
'`3` - extra, '\
|
14
|
+
'`4` - extreme') do |v|
|
15
|
+
OptionHelpers.limit_with_range(v.to_i, 0..4)
|
16
|
+
end
|
17
|
+
|
18
|
+
TIMEOUT_OPTION = timeout_option
|
19
|
+
|
20
|
+
def run_order
|
21
|
+
4
|
22
|
+
end
|
23
|
+
|
24
|
+
def optimize(src, dst)
|
25
|
+
src.copy(dst)
|
26
|
+
args = %W[
|
27
|
+
--recompress
|
28
|
+
-#{level}
|
29
|
+
--quiet
|
30
|
+
--
|
31
|
+
#{dst}
|
32
|
+
]
|
33
|
+
execute(:advpng, *args) && optimized?(src, dst)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'image_optim/bin_resolver'
|
2
|
+
require 'image_optim/option_definition'
|
3
|
+
|
4
|
+
class ImageOptim
|
5
|
+
class Worker
|
6
|
+
# Class methods of ImageOptim::Worker
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(klass)
|
9
|
+
klass.instance_variable_set(:@klasses, [])
|
10
|
+
end
|
11
|
+
|
12
|
+
# List of available workers
|
13
|
+
def klasses
|
14
|
+
@klasses.to_enum
|
15
|
+
end
|
16
|
+
|
17
|
+
# Remember all classes inheriting from this one
|
18
|
+
def inherited(base)
|
19
|
+
@klasses << base
|
20
|
+
end
|
21
|
+
|
22
|
+
# Underscored class name symbol
|
23
|
+
def bin_sym
|
24
|
+
@underscored_name ||=
|
25
|
+
name.
|
26
|
+
split('::').last. # get last part
|
27
|
+
gsub(/([a-z])([A-Z])/, '\1_\2').downcase. # convert AbcDef to abc_def
|
28
|
+
to_sym
|
29
|
+
end
|
30
|
+
|
31
|
+
def option_definitions
|
32
|
+
@option_definitions ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
def timeout_option
|
36
|
+
option(
|
37
|
+
:timeout,
|
38
|
+
0,
|
39
|
+
'Number of seconds before worker is timed out. Must be greater than' \
|
40
|
+
'0 to enable timeout.'
|
41
|
+
){ |v| v }
|
42
|
+
end
|
43
|
+
|
44
|
+
def option(name, default, type, description = nil, &proc)
|
45
|
+
attr_reader name
|
46
|
+
OptionDefinition.new(name, default, type, description, &proc).
|
47
|
+
tap{ |option_definition| option_definitions << option_definition }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Create hash with format mapped to list of workers sorted by run order
|
51
|
+
def create_all_by_format(image_optim, &options_proc)
|
52
|
+
by_format = {}
|
53
|
+
create_all(image_optim, &options_proc).each do |worker|
|
54
|
+
worker.image_formats.each do |format|
|
55
|
+
by_format[format] ||= []
|
56
|
+
by_format[format] << worker
|
57
|
+
end
|
58
|
+
end
|
59
|
+
by_format
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create list of workers sorted by run order
|
63
|
+
# Workers are initialized with options provided through options_proc
|
64
|
+
# Resolve all bins of all workers, if there are errors and
|
65
|
+
# skip_missing_workers of image_optim is true - show warnings, otherwise
|
66
|
+
# fail with one joint exception
|
67
|
+
def create_all(image_optim, &options_proc)
|
68
|
+
workers = init_all(image_optim, &options_proc)
|
69
|
+
|
70
|
+
resolved = []
|
71
|
+
errors = BinResolver.collect_errors(workers) do |worker|
|
72
|
+
worker.resolve_used_bins!
|
73
|
+
resolved << worker
|
74
|
+
end
|
75
|
+
|
76
|
+
unless errors.empty?
|
77
|
+
messages = errors.map(&:to_s).uniq
|
78
|
+
if image_optim.skip_missing_workers
|
79
|
+
messages.each{ |message| warn message }
|
80
|
+
else
|
81
|
+
joint_message = ['Bin resolving errors:', *messages].join("\n")
|
82
|
+
fail BinResolver::Error, joint_message
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
resolved.sort_by.with_index{ |worker, i| [worker.run_order, i] }
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def init_all(image_optim, &options_proc)
|
92
|
+
klasses.map do |klass|
|
93
|
+
options = options_proc[klass]
|
94
|
+
next if options[:disable]
|
95
|
+
|
96
|
+
[:allow_lossy, :timeout].each do |option_key|
|
97
|
+
if !options.key?(option_key) && klass.method_defined?(option_key)
|
98
|
+
options[option_key] = image_optim.send(option_key)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
klass.init(image_optim, options)
|
103
|
+
end.compact.flatten
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'image_optim/worker'
|
2
|
+
|
3
|
+
class ImageOptim
|
4
|
+
class Worker
|
5
|
+
# http://www.lcdf.org/gifsicle/
|
6
|
+
class Gifsicle < Worker
|
7
|
+
# If interlace specified initialize one instance
|
8
|
+
# Otherwise initialize two, one with interlace off and one with on
|
9
|
+
def self.init(image_optim, options = {})
|
10
|
+
return super if options.key?(:interlace)
|
11
|
+
|
12
|
+
[false, true].map do |interlace|
|
13
|
+
new(image_optim, options.merge(:interlace => interlace))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
INTERLACE_OPTION =
|
18
|
+
option(:interlace, false, TrueFalseNil, 'Interlace: '\
|
19
|
+
'`true` - interlace on, '\
|
20
|
+
'`false` - interlace off, '\
|
21
|
+
'`nil` - as is in original image '\
|
22
|
+
'(defaults to running two instances, one with interlace off and '\
|
23
|
+
'one with on)') do |v|
|
24
|
+
TrueFalseNil.convert(v)
|
25
|
+
end
|
26
|
+
|
27
|
+
LEVEL_OPTION =
|
28
|
+
option(:level, 3, 'Compression level: '\
|
29
|
+
'`1` - light and fast, '\
|
30
|
+
'`2` - normal, '\
|
31
|
+
'`3` - heavy (slower)') do |v|
|
32
|
+
OptionHelpers.limit_with_range(v.to_i, 1..3)
|
33
|
+
end
|
34
|
+
|
35
|
+
CAREFUL_OPTION =
|
36
|
+
option(:careful, false, 'Avoid bugs with some software'){ |v| !!v }
|
37
|
+
|
38
|
+
TIMEOUT_OPTION = timeout_option
|
39
|
+
|
40
|
+
def optimize(src, dst)
|
41
|
+
args = %W[
|
42
|
+
--output=#{dst}
|
43
|
+
--no-comments
|
44
|
+
--no-names
|
45
|
+
--same-delay
|
46
|
+
--same-loopcount
|
47
|
+
--no-warnings
|
48
|
+
--
|
49
|
+
#{src}
|
50
|
+
]
|
51
|
+
|
52
|
+
if resolve_bin!(:gifsicle).version >= '1.85'
|
53
|
+
args.unshift '--no-extensions', '--no-app-extensions'
|
54
|
+
end
|
55
|
+
|
56
|
+
unless interlace.nil?
|
57
|
+
args.unshift interlace ? '--interlace' : '--no-interlace'
|
58
|
+
end
|
59
|
+
args.unshift '--careful' if careful
|
60
|
+
args.unshift "--optimize=#{level}" if level
|
61
|
+
execute(:gifsicle, *args) && optimized?(src, dst)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'image_optim/worker'
|
2
|
+
require 'exifr'
|
3
|
+
|
4
|
+
class ImageOptim
|
5
|
+
class Worker
|
6
|
+
# http://www.sentex.net/~mwandel/jhead/
|
7
|
+
#
|
8
|
+
# Jhead internally uses jpegtran which should be on path
|
9
|
+
class Jhead < Worker
|
10
|
+
TIMEOUT_OPTION = timeout_option
|
11
|
+
|
12
|
+
ORIENTED = 2..8 # not top-left
|
13
|
+
|
14
|
+
# Works on jpegs
|
15
|
+
def image_formats
|
16
|
+
[:jpeg]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Run first, while exif is still present
|
20
|
+
def run_order
|
21
|
+
-10
|
22
|
+
end
|
23
|
+
|
24
|
+
def used_bins
|
25
|
+
[:jhead, :jpegtran]
|
26
|
+
end
|
27
|
+
|
28
|
+
def optimize(src, dst)
|
29
|
+
return false unless oriented?(src)
|
30
|
+
src.copy(dst)
|
31
|
+
args = %W[
|
32
|
+
-autorot
|
33
|
+
#{dst}
|
34
|
+
]
|
35
|
+
resolve_bin!(:jpegtran)
|
36
|
+
execute(:jhead, *args) && dst.size?
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def oriented?(image)
|
42
|
+
exif = EXIFR::JPEG.new(image.to_s)
|
43
|
+
ORIENTED.include?(exif.orientation.to_i)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'image_optim/worker'
|
2
|
+
require 'image_optim/option_helpers'
|
3
|
+
|
4
|
+
class ImageOptim
|
5
|
+
class Worker
|
6
|
+
# http://www.kokkonen.net/tjko/projects.html
|
7
|
+
class Jpegoptim < Worker
|
8
|
+
ALLOW_LOSSY_OPTION =
|
9
|
+
option(:allow_lossy, false, 'Allow limiting maximum quality'){ |v| !!v }
|
10
|
+
|
11
|
+
STRIP_OPTION =
|
12
|
+
option(:strip, :all, Array, 'List of extra markers to strip: '\
|
13
|
+
'`:comments`, '\
|
14
|
+
'`:exif`, '\
|
15
|
+
'`:iptc`, '\
|
16
|
+
'`:icc` or '\
|
17
|
+
'`:all`') do |v|
|
18
|
+
values = Array(v).map(&:to_s)
|
19
|
+
known_values = %w[all comments exif iptc icc]
|
20
|
+
unknown_values = values - known_values
|
21
|
+
unless unknown_values.empty?
|
22
|
+
warn "Unknown markers for jpegoptim: #{unknown_values.join(', ')}"
|
23
|
+
end
|
24
|
+
values & known_values
|
25
|
+
end
|
26
|
+
|
27
|
+
MAX_QUALITY_OPTION =
|
28
|
+
option(:max_quality, 100, 'Maximum image quality factor '\
|
29
|
+
'`0`..`100`, ignored in default/lossless mode') do |v, opt_def|
|
30
|
+
if allow_lossy
|
31
|
+
OptionHelpers.limit_with_range(v.to_i, 0..100)
|
32
|
+
else
|
33
|
+
if v != opt_def.default
|
34
|
+
warn "#{self.class.bin_sym} #{opt_def.name} #{v} ignored " \
|
35
|
+
'in lossless mode'
|
36
|
+
end
|
37
|
+
opt_def.default
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
TIMEOUT_OPTION = timeout_option
|
42
|
+
|
43
|
+
# Run earlier if max_quality is less than 100
|
44
|
+
def run_order
|
45
|
+
max_quality < 100 ? -1 : 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def optimize(src, dst)
|
49
|
+
src.copy(dst)
|
50
|
+
args = %W[
|
51
|
+
--quiet
|
52
|
+
--
|
53
|
+
#{dst}
|
54
|
+
]
|
55
|
+
strip.each do |strip_marker|
|
56
|
+
args.unshift "--strip-#{strip_marker}"
|
57
|
+
end
|
58
|
+
args.unshift "--max=#{max_quality}" if max_quality < 100
|
59
|
+
execute(:jpegoptim, *args) && optimized?(src, dst)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'image_optim/worker'
|
2
|
+
require 'image_optim/option_helpers'
|
3
|
+
|
4
|
+
class ImageOptim
|
5
|
+
class Worker
|
6
|
+
# https://github.com/danielgtaylor/jpeg-archive#jpeg-recompress
|
7
|
+
class Jpegrecompress < Worker
|
8
|
+
ALLOW_LOSSY_OPTION =
|
9
|
+
option(:allow_lossy, false, 'Allow worker, it is always lossy'){ |v| !!v }
|
10
|
+
|
11
|
+
# Initialize only if allow_lossy
|
12
|
+
def self.init(image_optim, options = {})
|
13
|
+
super if options[:allow_lossy]
|
14
|
+
end
|
15
|
+
|
16
|
+
QUALITY_NAMES = [:low, :medium, :high, :veryhigh].freeze
|
17
|
+
|
18
|
+
quality_names_desc = QUALITY_NAMES.each_with_index.map do |name, i|
|
19
|
+
"`#{i}` - #{name}"
|
20
|
+
end.join(', ')
|
21
|
+
|
22
|
+
QUALITY_OPTION =
|
23
|
+
option(:quality, 3, "JPEG quality preset: #{quality_names_desc}") do |v|
|
24
|
+
OptionHelpers.limit_with_range(v.to_i, 0...QUALITY_NAMES.length)
|
25
|
+
end
|
26
|
+
|
27
|
+
TIMEOUT_OPTION = timeout_option
|
28
|
+
|
29
|
+
def used_bins
|
30
|
+
[:'jpeg-recompress']
|
31
|
+
end
|
32
|
+
|
33
|
+
# Run early as lossy worker
|
34
|
+
def run_order
|
35
|
+
-5
|
36
|
+
end
|
37
|
+
|
38
|
+
def optimize(src, dst)
|
39
|
+
args = %W[
|
40
|
+
--quality #{QUALITY_NAMES[quality]}
|
41
|
+
--no-copy
|
42
|
+
#{src}
|
43
|
+
#{dst}
|
44
|
+
]
|
45
|
+
execute(:'jpeg-recompress', *args) && optimized?(src, dst)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'image_optim/worker'
|
2
|
+
|
3
|
+
class ImageOptim
|
4
|
+
class Worker
|
5
|
+
# http://www.ijg.org/
|
6
|
+
#
|
7
|
+
# Uses jpegtran through jpegrescan if enabled, jpegrescan is vendored with
|
8
|
+
# this gem
|
9
|
+
class Jpegtran < Worker
|
10
|
+
COPY_CHUNKS_OPTION =
|
11
|
+
option(:copy_chunks, false, 'Copy all chunks'){ |v| !!v }
|
12
|
+
|
13
|
+
PROGRESSIVE_OPTION =
|
14
|
+
option(:progressive, true, 'Create progressive JPEG file'){ |v| !!v }
|
15
|
+
|
16
|
+
JPEGRESCAN_OPTION =
|
17
|
+
option(:jpegrescan, false, 'Use jpegtran through jpegrescan, '\
|
18
|
+
'ignore progressive option'){ |v| !!v }
|
19
|
+
|
20
|
+
TIMEOUT_OPTION = timeout_option
|
21
|
+
|
22
|
+
def used_bins
|
23
|
+
jpegrescan ? [:jpegtran, :jpegrescan] : [:jpegtran]
|
24
|
+
end
|
25
|
+
|
26
|
+
def optimize(src, dst)
|
27
|
+
if jpegrescan
|
28
|
+
args = %W[
|
29
|
+
#{src}
|
30
|
+
#{dst}
|
31
|
+
]
|
32
|
+
args.unshift '-s' unless copy_chunks
|
33
|
+
resolve_bin!(:jpegtran)
|
34
|
+
execute(:jpegrescan, *args) && optimized?(src, dst)
|
35
|
+
else
|
36
|
+
args = %W[
|
37
|
+
-optimize
|
38
|
+
-outfile #{dst}
|
39
|
+
#{src}
|
40
|
+
]
|
41
|
+
args.unshift '-copy', (copy_chunks ? 'all' : 'none')
|
42
|
+
args.unshift '-progressive' if progressive
|
43
|
+
execute(:jpegtran, *args) && optimized?(src, dst)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'image_optim/worker'
|
2
|
+
require 'image_optim/option_helpers'
|
3
|
+
require 'image_optim/true_false_nil'
|
4
|
+
|
5
|
+
class ImageOptim
|
6
|
+
class Worker
|
7
|
+
# http://optipng.sourceforge.net/
|
8
|
+
class Optipng < Worker
|
9
|
+
LEVEL_OPTION =
|
10
|
+
option(:level, 6, 'Optimization level preset: '\
|
11
|
+
'`0` is least, '\
|
12
|
+
'`7` is best') do |v|
|
13
|
+
OptionHelpers.limit_with_range(v.to_i, 0..7)
|
14
|
+
end
|
15
|
+
|
16
|
+
INTERLACE_OPTION =
|
17
|
+
option(:interlace, false, TrueFalseNil, 'Interlace: '\
|
18
|
+
'`true` - interlace on, '\
|
19
|
+
'`false` - interlace off, '\
|
20
|
+
'`nil` - as is in original image') do |v|
|
21
|
+
TrueFalseNil.convert(v)
|
22
|
+
end
|
23
|
+
|
24
|
+
STRIP_OPTION =
|
25
|
+
option(:strip, true, 'Remove all auxiliary chunks'){ |v| !!v }
|
26
|
+
|
27
|
+
TIMEOUT_OPTION = timeout_option
|
28
|
+
|
29
|
+
def run_order
|
30
|
+
-4
|
31
|
+
end
|
32
|
+
|
33
|
+
def optimize(src, dst)
|
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 resolve_bin!(:optipng).version >= '0.7'
|
43
|
+
args.unshift '-strip', 'all' if strip
|
44
|
+
end
|
45
|
+
execute(:optipng, *args) && optimized?(src, dst)
|
46
|
+
end
|
47
|
+
|
48
|
+
def optimized?(src, dst)
|
49
|
+
interlace ? dst.size? : super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|