openstreetmap-image_optim 0.21.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rubocop.yml +65 -0
  4. data/.travis.yml +42 -0
  5. data/CHANGELOG.markdown +272 -0
  6. data/CONTRIBUTING.markdown +10 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.markdown +344 -0
  10. data/Vagrantfile +33 -0
  11. data/bin/image_optim +28 -0
  12. data/image_optim.gemspec +29 -0
  13. data/lib/image_optim.rb +228 -0
  14. data/lib/image_optim/bin_resolver.rb +144 -0
  15. data/lib/image_optim/bin_resolver/bin.rb +105 -0
  16. data/lib/image_optim/bin_resolver/comparable_condition.rb +60 -0
  17. data/lib/image_optim/bin_resolver/error.rb +6 -0
  18. data/lib/image_optim/bin_resolver/simple_version.rb +31 -0
  19. data/lib/image_optim/cmd.rb +49 -0
  20. data/lib/image_optim/config.rb +205 -0
  21. data/lib/image_optim/configuration_error.rb +3 -0
  22. data/lib/image_optim/handler.rb +57 -0
  23. data/lib/image_optim/hash_helpers.rb +45 -0
  24. data/lib/image_optim/image_meta.rb +25 -0
  25. data/lib/image_optim/image_path.rb +68 -0
  26. data/lib/image_optim/non_negative_integer_range.rb +11 -0
  27. data/lib/image_optim/option_definition.rb +32 -0
  28. data/lib/image_optim/option_helpers.rb +17 -0
  29. data/lib/image_optim/railtie.rb +38 -0
  30. data/lib/image_optim/runner.rb +139 -0
  31. data/lib/image_optim/runner/glob_helpers.rb +45 -0
  32. data/lib/image_optim/runner/option_parser.rb +227 -0
  33. data/lib/image_optim/space.rb +29 -0
  34. data/lib/image_optim/true_false_nil.rb +16 -0
  35. data/lib/image_optim/worker.rb +159 -0
  36. data/lib/image_optim/worker/advpng.rb +35 -0
  37. data/lib/image_optim/worker/class_methods.rb +91 -0
  38. data/lib/image_optim/worker/gifsicle.rb +63 -0
  39. data/lib/image_optim/worker/jhead.rb +43 -0
  40. data/lib/image_optim/worker/jpegoptim.rb +58 -0
  41. data/lib/image_optim/worker/jpegrecompress.rb +44 -0
  42. data/lib/image_optim/worker/jpegtran.rb +46 -0
  43. data/lib/image_optim/worker/optipng.rb +45 -0
  44. data/lib/image_optim/worker/pngcrush.rb +54 -0
  45. data/lib/image_optim/worker/pngout.rb +38 -0
  46. data/lib/image_optim/worker/pngquant.rb +51 -0
  47. data/lib/image_optim/worker/svgo.rb +32 -0
  48. data/script/template/jquery-2.1.3.min.js +4 -0
  49. data/script/template/sortable-0.6.0.min.js +2 -0
  50. data/script/template/worker_analysis.erb +254 -0
  51. data/script/update_worker_options_in_readme +60 -0
  52. data/script/worker_analysis +599 -0
  53. data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +37 -0
  54. data/spec/image_optim/bin_resolver/simple_version_spec.rb +57 -0
  55. data/spec/image_optim/bin_resolver_spec.rb +272 -0
  56. data/spec/image_optim/cmd_spec.rb +66 -0
  57. data/spec/image_optim/config_spec.rb +217 -0
  58. data/spec/image_optim/handler_spec.rb +95 -0
  59. data/spec/image_optim/hash_helpers_spec.rb +76 -0
  60. data/spec/image_optim/image_path_spec.rb +54 -0
  61. data/spec/image_optim/railtie_spec.rb +121 -0
  62. data/spec/image_optim/runner/glob_helpers_spec.rb +25 -0
  63. data/spec/image_optim/runner/option_parser_spec.rb +99 -0
  64. data/spec/image_optim/space_spec.rb +25 -0
  65. data/spec/image_optim/worker_spec.rb +192 -0
  66. data/spec/image_optim_spec.rb +242 -0
  67. data/spec/images/comparison.png +0 -0
  68. data/spec/images/decompressed.jpeg +0 -0
  69. data/spec/images/icecream.gif +0 -0
  70. data/spec/images/image.jpg +0 -0
  71. data/spec/images/invisiblepixels/generate +24 -0
  72. data/spec/images/invisiblepixels/image.png +0 -0
  73. data/spec/images/lena.jpg +0 -0
  74. data/spec/images/orient/0.jpg +0 -0
  75. data/spec/images/orient/1.jpg +0 -0
  76. data/spec/images/orient/2.jpg +0 -0
  77. data/spec/images/orient/3.jpg +0 -0
  78. data/spec/images/orient/4.jpg +0 -0
  79. data/spec/images/orient/5.jpg +0 -0
  80. data/spec/images/orient/6.jpg +0 -0
  81. data/spec/images/orient/7.jpg +0 -0
  82. data/spec/images/orient/8.jpg +0 -0
  83. data/spec/images/orient/generate +23 -0
  84. data/spec/images/orient/original.jpg +0 -0
  85. data/spec/images/quant/64.png +0 -0
  86. data/spec/images/quant/generate +25 -0
  87. data/spec/images/rails.png +0 -0
  88. data/spec/images/test.svg +3 -0
  89. data/spec/images/transparency1.png +0 -0
  90. data/spec/images/transparency2.png +0 -0
  91. data/spec/images/vergroessert.jpg +0 -0
  92. data/spec/spec_helper.rb +64 -0
  93. data/vendor/jpegrescan +143 -0
  94. metadata +308 -0
@@ -0,0 +1,344 @@
1
+ [![Gem Version](https://img.shields.io/gem/v/image_optim.svg?style=flat)](https://rubygems.org/gems/image_optim)
2
+ [![Build Status](https://img.shields.io/travis/toy/image_optim/master.svg?style=flat)](https://travis-ci.org/toy/image_optim)
3
+ [![Code Climate](https://img.shields.io/codeclimate/github/toy/image_optim.svg?style=flat)](https://codeclimate.com/github/toy/image_optim)
4
+ [![Code Climate Coverage](https://img.shields.io/codeclimate/coverage/github/toy/image_optim.svg?style=flat)](https://codeclimate.com/github/toy/image_optim)
5
+ [![Dependency Status](https://img.shields.io/gemnasium/toy/image_optim.svg?style=flat)](https://gemnasium.com/toy/image_optim)
6
+ [![Inch CI](http://inch-ci.org/github/toy/image_optim.svg?branch=master&style=flat)](http://inch-ci.org/github/toy/image_optim)
7
+
8
+ # image_optim
9
+
10
+ Optimize (lossless compress, optionally lossy) images (jpeg, png, gif, svg) using external utilities:
11
+
12
+ * [advpng](http://advancemame.sourceforge.net/doc-advpng.html) from [AdvanceCOMP](http://advancemame.sourceforge.net/comp-readme.html)
13
+ (will use [zopfli](https://code.google.com/p/zopfli/) on default/maximum level 4)
14
+ * [gifsicle](http://www.lcdf.org/gifsicle/)
15
+ * [jhead](http://www.sentex.net/~mwandel/jhead/)
16
+ * [jpegoptim](http://www.kokkonen.net/tjko/projects.html)
17
+ * [jpeg-recompress](https://github.com/danielgtaylor/jpeg-archive#jpeg-recompress)
18
+ * jpegtran from [Independent JPEG Group's JPEG library](http://www.ijg.org/)
19
+ * [optipng](http://optipng.sourceforge.net/)
20
+ * [pngcrush](http://pmt.sourceforge.net/pngcrush/)
21
+ * [pngout](http://www.advsys.net/ken/util/pngout.htm)
22
+ * [pngquant](http://pngquant.org/)
23
+ * [svgo](https://github.com/svg/svgo)
24
+
25
+ Based on [ImageOptim.app](http://imageoptim.com/).
26
+
27
+ Documentation for [latest version](http://rubydoc.info/gems/image_optim/frames) and [master](http://rubydoc.info/github/toy/image_optim/master/frames).
28
+
29
+ ## Gem installation
30
+
31
+ ```sh
32
+ gem install image_optim
33
+ ```
34
+
35
+ You may also want to install [`image_optim_pack`](https://github.com/toy/image_optim_pack) (see [Binaries pack](#binaries-pack)).
36
+
37
+ ```sh
38
+ gem install image_optim_pack
39
+ ```
40
+
41
+ ### Bundler
42
+
43
+ Add to your `Gemfile`:
44
+
45
+ ```ruby
46
+ gem 'image_optim'
47
+ ```
48
+
49
+ With `image_optim_pack`:
50
+
51
+ ```ruby
52
+ gem 'image_optim'
53
+ gem 'image_optim_pack'
54
+ ```
55
+
56
+ With version:
57
+
58
+ ```ruby
59
+ gem 'image_optim', '~> 0.11'
60
+ ```
61
+
62
+ If you want to check latest changes:
63
+
64
+ ```ruby
65
+ gem 'image_optim', :git => 'git://github.com/toy/image_optim.git'
66
+ ```
67
+
68
+ ## Binaries location
69
+
70
+ Simplest way for `image_optim` to locate binaries is to install them in common location present in `PATH` (see [Binaries installation](#binaries-installation)).
71
+
72
+ If you cannot install to common location, then install to custom one and add it to `PATH`.
73
+
74
+ Specify custom bin location using `XXX_BIN` environment variable (`JPEGOPTIM_BIN`, `OPTIPNG_BIN`, …).
75
+
76
+ Besides permanently setting environment variables in `~/.profile`, `~/.bash_profile`, `~/.bashrc`, `~/.zshrc`, … they can be set:
77
+
78
+ * before command:
79
+
80
+ `PATH="/custom/location:$PATH" image_optim *.jpg`
81
+
82
+ for example:
83
+
84
+ `PATH="/Applications/ImageOptim.app/Contents/MacOS:$PATH" image_optim *.jpg`
85
+
86
+ * inside script:
87
+
88
+ `ENV['PATH'] = "/custom/location:#{ENV['PATH']}"; ImageOptim.optimize_images([…])`
89
+
90
+ for example:
91
+
92
+ `ENV['PATH'] = "/Applications/ImageOptim.app/Contents/MacOS:#{ENV['PATH']}"; ImageOptim.optimize_images([…])`
93
+
94
+ ## Binaries installation
95
+
96
+ ### Binaries pack
97
+
98
+ Easiest way to get latest versions of most binaries for `image_optim` for Linux and Mac OS X is by installing [`image_optim_pack`](https://github.com/toy/image_optim_pack) gem.
99
+
100
+ Check installation instructions in [Gem installation](#gem-installation) section.
101
+
102
+ Pack doesn't include `pngout` and `svgo` binaries, their installation instructions are provided below.
103
+
104
+ ### Linux - Debian/Ubuntu
105
+
106
+ ```bash
107
+ sudo apt-get install -y advancecomp gifsicle jhead jpegoptim libjpeg-progs optipng pngcrush pngquant
108
+ ```
109
+
110
+ If you get an old version of `pngquant`, please check how to install up-to-date version or compile from source at [http://pngquant.org/](http://pngquant.org/).
111
+
112
+ ### Linux - RHEL/Fedora/Centos
113
+
114
+ ```bash
115
+ sudo yum install -y advancecomp gifsicle jhead libjpeg optipng pngquant
116
+ ```
117
+
118
+ You may also need to install `libjpeg-turbo-utils` instead of `libjpeg`:
119
+
120
+ ```bash
121
+ sudo yum install -y libjpeg-turbo-utils
122
+ ```
123
+
124
+ You will also need to install `jpegoptim` and `pngcrush` from source:
125
+
126
+ #### jpegoptim
127
+
128
+ Replace `X.Y.Z` with latest version number from http://www.kokkonen.net/tjko/projects.html#jpegoptim.
129
+
130
+ ```bash
131
+ JPEGOPTIM_VERSION=X.Y.Z
132
+ cd /tmp
133
+ curl -O http://www.kokkonen.net/tjko/src/jpegoptim-$JPEGOPTIM_VERSION.tar.gz
134
+ tar zxf jpegoptim-$JPEGOPTIM_VERSION.tar.gz
135
+ cd jpegoptim-$JPEGOPTIM_VERSION
136
+ ./configure && make && make install
137
+ ```
138
+
139
+ #### pngcrush
140
+
141
+ Replace `X.Y.Z` with latest version number from http://sourceforge.net/projects/pmt/files/pngcrush/.
142
+
143
+ ```bash
144
+ PNGCRUSH_VERSION=X.Y.Z
145
+ cd /tmp
146
+ curl -O http://iweb.dl.sourceforge.net/project/pmt/pngcrush/$PNGCRUSH_VERSION/pngcrush-$PNGCRUSH_VERSION.tar.gz
147
+ tar zxf pngcrush-$PNGCRUSH_VERSION.tar.gz
148
+ cd pngcrush-$PNGCRUSH_VERSION
149
+ make && cp -f pngcrush /usr/local/bin
150
+ ```
151
+
152
+ ### OS X: Macports
153
+
154
+ ```bash
155
+ sudo port install advancecomp gifsicle jhead jpegoptim jpeg optipng pngcrush pngquant
156
+ ```
157
+
158
+ ### OS X: Brew
159
+
160
+ ```bash
161
+ brew install advancecomp gifsicle jhead jpegoptim jpeg optipng pngcrush pngquant
162
+ ```
163
+
164
+ ### pngout installation (optional)
165
+
166
+ You can install `pngout` by downloading and installing the [binary versions](http://www.jonof.id.au/kenutils).
167
+
168
+ _Note: pngout is free to use even in commercial soft, but you can not redistribute, repackage or reuse it without consent and agreement of creator. [license](http://advsys.net/ken/utils.htm#pngoutkziplicense)_
169
+
170
+ ### svgo installation (optional)
171
+
172
+ `svgo` is available from NPM.
173
+
174
+ ```bash
175
+ npm install -g svgo
176
+ ```
177
+
178
+ ### jpeg-recompress installation (optional)
179
+
180
+ Download and install the `jpeg-recompress` binary from the [JPEG-Archive Releases](https://github.com/danielgtaylor/jpeg-archive/releases) page,
181
+ or follow the instructions to [build from source](https://github.com/danielgtaylor/jpeg-archive#building).
182
+
183
+ ## Usage
184
+
185
+ ### From shell
186
+
187
+ ```sh
188
+ image_optim *.{jpg,png,gif,svg}
189
+
190
+ image_optim -r .
191
+
192
+ image_optim -h
193
+ ```
194
+
195
+ ### From ruby
196
+
197
+ Initialize optimizer (or you can call optimization methods directly on `ImageOptim`):
198
+
199
+ ```ruby
200
+ image_optim = ImageOptim.new
201
+
202
+ image_optim = ImageOptim.new(:pngout => false)
203
+
204
+ image_optim = ImageOptim.new(:nice => 20)
205
+ ```
206
+
207
+ Optimize image getting temp path:
208
+
209
+ ```ruby
210
+ image_optim.optimize_image('a.png')
211
+ ```
212
+
213
+ Optimize image in place:
214
+
215
+ ```ruby
216
+ image_optim.optimize_image!('b.jpg')
217
+ ```
218
+
219
+ Optimize image data:
220
+
221
+ ```ruby
222
+ image_optim.optimize_image_data(data)
223
+ ```
224
+
225
+ Multiple images:
226
+
227
+ ```ruby
228
+ image_optim.optimize_images(Dir['*.png']) do |unoptimized, optimized|
229
+ if optimized
230
+ puts "#{unoptimized} => #{optimized}"
231
+ end
232
+ end
233
+
234
+ image_optim.optimize_images!(Dir['*.*'])
235
+
236
+ image_optim.optimize_images_data(datas)
237
+ ```
238
+
239
+ ### From rails
240
+
241
+ `ImageOptim::Railtie` will automatically register sprockets preprocessor unless you set `config.assets.image_optim = false` or `config.assets.compress = false` (later for partial rails 3 compatibility).
242
+
243
+ You can provide options for image_optim used for preprocessor through config `config.assets.image_optim = {nice: 20, svgo: false}` (ruby1.8 style: `{:nice => 20, :svgo => false}`).
244
+ Check available options in [options section](#options).
245
+
246
+ Image optimization can be time consuming, so depending on your deployment process you may prefer to optimize original asset files.
247
+
248
+ ## Configuration
249
+
250
+ Configuration in YAML format will be read and prepended to options from two paths:
251
+
252
+ * `$XDG_CONFIG_HOME/image_optim.yml` (by default `~/.config/image_optim.yml`)
253
+ * `.image_optim.yml` in current working directory
254
+
255
+ Paths can be changed using `:config_paths` option and `--config-paths` argument.
256
+
257
+ Example configuration:
258
+
259
+ ```yaml
260
+ nice: 20
261
+ pngout: false # disable
262
+ optipng:
263
+ level: 5
264
+ ```
265
+
266
+ ## Options
267
+
268
+ * `:nice` — Nice level *(defaults to `10`)*
269
+ * `:threads` — Number of threads or disable *(defaults to number of processors)*
270
+ * `:verbose` — Verbose output *(defaults to `false`)*
271
+ * `:pack` — Require image\_optim\_pack or disable it, by default image\_optim\_pack will be used if available, will turn on `:skip-missing-workers` unless explicitly disabled *(defaults to `nil`)*
272
+ * `:skip_missing_workers` — Skip workers with missing or problematic binaries *(defaults to `false`)*
273
+ * `:allow_lossy` — Allow lossy workers and optimizations *(defaults to `false`)*
274
+
275
+ Worker can be disabled by passing `false` instead of options hash or by setting option `:disable` to `true`.
276
+
277
+ <!---<worker-options>-->
278
+ <!-- markdown for worker options is generated by `script/update_worker_options_in_readme` -->
279
+
280
+ ### :advpng =>
281
+ * `:level` — Compression level: `0` - don't compress, `1` - fast, `2` - normal, `3` - extra, `4` - extreme *(defaults to `4`)*
282
+
283
+ ### :gifsicle =>
284
+ * `:interlace` — Interlace: `true` - interlace on, `false` - interlace off, `nil` - as is in original image (defaults to running two instances, one with interlace off and one with on)
285
+ * `:level` — Compression level: `1` - light and fast, `2` - normal, `3` - heavy (slower) *(defaults to `3`)*
286
+ * `:careful` — Avoid bugs with some software *(defaults to `false`)*
287
+
288
+ ### :jhead =>
289
+ Worker has no options
290
+
291
+ ### :jpegoptim =>
292
+ * `:strip` — List of extra markers to strip: `:comments`, `:exif`, `:iptc`, `:icc` or `:all` *(defaults to `:all`)*
293
+ * `:max_quality` — Maximum image quality factor `0`..`100`, ignored in default/lossless mode *(defaults to `100`)*
294
+
295
+ ### :jpegrecompress =>
296
+ * `:quality` — JPEG quality preset: `0` - low, `1` - medium, `2` - high, `3` - veryhigh *(defaults to `3`)*
297
+
298
+ ### :jpegtran =>
299
+ * `:copy_chunks` — Copy all chunks *(defaults to `false`)*
300
+ * `:progressive` — Create progressive JPEG file *(defaults to `true`)*
301
+ * `:jpegrescan` — Use jpegtran through jpegrescan, ignore progressive option *(defaults to `false`)*
302
+
303
+ ### :optipng =>
304
+ * `:level` — Optimization level preset: `0` is least, `7` is best *(defaults to `6`)*
305
+ * `:interlace` — Interlace: `true` - interlace on, `false` - interlace off, `nil` - as is in original image *(defaults to `false`)*
306
+ * `:strip` — Remove all auxiliary chunks *(defaults to `true`)*
307
+
308
+ ### :pngcrush =>
309
+ * `:chunks` — List of chunks to remove or `:alla` - all except tRNS/transparency or `:allb` - all except tRNS and gAMA/gamma *(defaults to `:alla`)*
310
+ * `:fix` — Fix otherwise fatal conditions such as bad CRCs *(defaults to `false`)*
311
+ * `:brute` — Brute force try all methods, very time-consuming and generally not worthwhile *(defaults to `false`)*
312
+ * `:blacken` — Blacken fully transparent pixels *(defaults to `true`)*
313
+
314
+ ### :pngout =>
315
+ * `:copy_chunks` — Copy optional chunks *(defaults to `false`)*
316
+ * `:strategy` — Strategy: `0` - xtreme, `1` - intense, `2` - longest Match, `3` - huffman Only, `4` - uncompressed *(defaults to `0`)*
317
+
318
+ ### :pngquant =>
319
+ * `:quality` — min..max - don't save below min, use less colors below max (both in range `0..100`; in yaml - `!ruby/range 0..100`), ignored in default/lossless mode *(defaults to `100..100`)*
320
+ * `:speed` — speed/quality trade-off: `1` - slow, `3` - default, `11` - fast & rough *(defaults to `3`)*
321
+
322
+ ### :svgo =>
323
+ * `disable_plugins` - List of plugins to disable *(defaults to none)*
324
+ * `enable_plugins` - List of plugins to enable *(defaults to none)*
325
+
326
+ <!---</worker-options>-->
327
+
328
+ ## Contributing
329
+
330
+ [List](https://github.com/toy/image_optim/graphs/contributors) of contributors to `image_optim`.
331
+
332
+ If you would like to contribute - that is great and you are very welcome. Please check few notes in file [CONTRIBUTING.markdown](CONTRIBUTING.markdown).
333
+
334
+ Financial contributions can be made via [gratipay](https://gratipay.com/toy/).
335
+
336
+ [![Support via Gratipay](https://cdn.rawgit.com/gratipay/gratipay-badge/2.1.2/dist/gratipay.png)](https://gratipay.com/toy/)
337
+
338
+ ## ChangeLog
339
+
340
+ In separate file [CHANGELOG.markdown](CHANGELOG.markdown).
341
+
342
+ ## Copyright
343
+
344
+ Copyright (c) 2012-2015 Ivan Kuchin. See [LICENSE.txt](LICENSE.txt) for details.
@@ -0,0 +1,33 @@
1
+ VAGRANTFILE_API_VERSION = '2'
2
+
3
+ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
4
+ config.vm.box = 'ubuntu/trusty64'
5
+
6
+ config.vm.provision 'shell', :inline => <<-SH
7
+ set -e
8
+
9
+ cd /vagrant
10
+
11
+ echo 'Install git, node, npm, imagemagick...'
12
+ apt-get install -y git npm nodejs-legacy imagemagick > /dev/null
13
+
14
+ echo 'Update rubygems...'
15
+ REALLY_GEM_UPDATE_SYSTEM=x gem update --system > /dev/null
16
+
17
+ echo 'Install bundler...'
18
+ gem install bundler > /dev/null
19
+
20
+ echo 'Install svgo...'
21
+ npm install -g -q svgo > /dev/null
22
+
23
+ echo 'Install pngout...'
24
+ curl -s \
25
+ 'http://static.jonof.id.au/dl/kenutils/pngout-20130221-linux.tar.gz' | \
26
+ tar -xz -C /usr/local/bin --strip-components 2 \
27
+ --wildcards '*/x86_64/pngout'
28
+
29
+ echo 'Bundle...'
30
+ bundle install --jobs=3 --quiet
31
+ bundle list
32
+ SH
33
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'image_optim/runner'
5
+ require 'image_optim/runner/option_parser'
6
+
7
+ args = ARGV.dup
8
+
9
+ options = ImageOptim::Runner::OptionParser.parse!(args)
10
+
11
+ begin
12
+ if options[:verbose]
13
+ $stderr.puts ImageOptim.full_version
14
+ end
15
+
16
+ only_info = options.delete(:only_info)
17
+ runner = ImageOptim::Runner.new(options)
18
+ unless only_info
19
+ abort 'specify paths to optimize' if args.empty?
20
+ abort unless runner.run!(args)
21
+ end
22
+ rescue => e
23
+ if options[:verbose]
24
+ abort "#{e}\n#{e.backtrace.join("\n")}"
25
+ else
26
+ abort e.to_s
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'openstreetmap-image_optim'
5
+ s.version = '0.21.0.1'
6
+ s.summary = %q{Optimize (lossless compress, optionally lossy) images (jpeg, png, gif, svg) using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, pngcrush, pngout, pngquant, svgo)}
7
+ s.homepage = "http://github.com/toy/#{s.name}"
8
+ s.authors = ['Ivan Kuchin']
9
+ s.license = 'MIT'
10
+
11
+ s.rubyforge_project = s.name
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = %w[lib]
17
+
18
+ s.add_dependency 'fspath', '~> 2.1'
19
+ s.add_dependency 'image_size', '~> 1.3'
20
+ s.add_dependency 'exifr', '~> 1.2', '>= 1.2.2'
21
+ s.add_dependency 'progress', '~> 3.0', '>= 3.0.1'
22
+ s.add_dependency 'in_threads', '~> 1.3'
23
+
24
+ s.add_development_dependency 'image_optim_pack', '~> 0.2'
25
+ s.add_development_dependency 'rspec', '~> 3.0'
26
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3')
27
+ s.add_development_dependency 'rubocop', '~> 0.27', '!= 0.30.1'
28
+ end
29
+ end
@@ -0,0 +1,228 @@
1
+ require 'image_optim/bin_resolver'
2
+ require 'image_optim/config'
3
+ require 'image_optim/handler'
4
+ require 'image_optim/image_meta'
5
+ require 'image_optim/image_path'
6
+ require 'image_optim/railtie' if defined?(Rails)
7
+ require 'image_optim/worker'
8
+ require 'in_threads'
9
+ require 'shellwords'
10
+
11
+ %w[
12
+ pngcrush pngout advpng optipng pngquant
13
+ jhead jpegoptim jpegrecompress jpegtran
14
+ gifsicle
15
+ svgo
16
+ ].each do |worker|
17
+ require "image_optim/worker/#{worker}"
18
+ end
19
+
20
+ # Main interface
21
+ class ImageOptim
22
+ # Nice level
23
+ attr_reader :nice
24
+
25
+ # Number of threads to run with
26
+ attr_reader :threads
27
+
28
+ # Verbose output?
29
+ attr_reader :verbose
30
+
31
+ # Use image_optim_pack
32
+ attr_reader :pack
33
+
34
+ # Skip workers with missing or problematic binaries
35
+ attr_reader :skip_missing_workers
36
+
37
+ # Allow lossy workers and optimizations
38
+ attr_reader :allow_lossy
39
+
40
+ # Initialize workers, specify options using worker underscored name:
41
+ #
42
+ # pass false to disable worker
43
+ #
44
+ # ImageOptim.new(:pngcrush => false)
45
+ #
46
+ # or hash with options to worker
47
+ #
48
+ # ImageOptim.new(:advpng => {:level => 3}, :optipng => {:level => 2})
49
+ #
50
+ # use :threads to set number of parallel optimizers to run (passing true or
51
+ # nil determines number of processors, false disables parallel processing)
52
+ #
53
+ # ImageOptim.new(:threads => 8)
54
+ #
55
+ # use :nice to specify optimizers nice level (true or nil makes it 10, false
56
+ # makes it 0)
57
+ #
58
+ # ImageOptim.new(:nice => 20)
59
+ def initialize(options = {})
60
+ config = Config.new(options)
61
+ @verbose = config.verbose
62
+ $stderr << "config:\n#{config.to_s.gsub(/^/, ' ')}" if verbose
63
+
64
+ %w[
65
+ nice
66
+ threads
67
+ pack
68
+ skip_missing_workers
69
+ allow_lossy
70
+ ].each do |name|
71
+ instance_variable_set(:"@#{name}", config.send(name))
72
+ $stderr << "#{name}: #{send(name)}\n" if verbose
73
+ end
74
+
75
+ @bin_resolver = BinResolver.new(self)
76
+
77
+ @workers_by_format = Worker.create_all_by_format(self) do |klass|
78
+ config.for_worker(klass)
79
+ end
80
+
81
+ log_workers_by_format if verbose
82
+
83
+ config.assert_no_unused_options!
84
+ end
85
+
86
+ # Get workers for image
87
+ def workers_for_image(path)
88
+ @workers_by_format[ImagePath.convert(path).format]
89
+ end
90
+
91
+ # Optimize one file, return new path as OptimizedImagePath or nil if
92
+ # optimization failed
93
+ def optimize_image(original)
94
+ original = ImagePath.convert(original)
95
+ return unless (workers = workers_for_image(original))
96
+ result = Handler.for(original) do |handler|
97
+ workers.each do |worker|
98
+ handler.process do |src, dst|
99
+ worker.optimize(src, dst)
100
+ end
101
+ end
102
+ end
103
+ return unless result
104
+ ImagePath::Optimized.new(result, original)
105
+ end
106
+
107
+ # Optimize one file in place, return original as OptimizedImagePath or nil if
108
+ # optimization failed
109
+ def optimize_image!(original)
110
+ original = ImagePath.convert(original)
111
+ return unless (result = optimize_image(original))
112
+ result.replace(original)
113
+ ImagePath::Optimized.new(original, result.original_size)
114
+ end
115
+
116
+ # Optimize image data, return new data or nil if optimization failed
117
+ def optimize_image_data(original_data)
118
+ image_meta = ImageMeta.for_data(original_data)
119
+ return unless image_meta && image_meta.format
120
+ ImagePath.temp_file %W[image_optim .#{image_meta.format}] do |temp|
121
+ temp.binmode
122
+ temp.write(original_data)
123
+ temp.close
124
+
125
+ if (result = optimize_image(temp.path))
126
+ result.binread
127
+ end
128
+ end
129
+ end
130
+
131
+ # Optimize multiple images
132
+ # if block given yields path and result for each image and returns array of
133
+ # yield results
134
+ # else return array of path and result pairs
135
+ def optimize_images(paths, &block)
136
+ run_method_for(paths, :optimize_image, &block)
137
+ end
138
+
139
+ # Optimize multiple images in place
140
+ # if block given yields path and result for each image and returns array of
141
+ # yield results
142
+ # else return array of path and result pairs
143
+ def optimize_images!(paths, &block)
144
+ run_method_for(paths, :optimize_image!, &block)
145
+ end
146
+
147
+ # Optimize multiple image datas
148
+ # if block given yields original and result for each image data and returns
149
+ # array of yield results
150
+ # else return array of path and result pairs
151
+ def optimize_images_data(datas, &block)
152
+ run_method_for(datas, :optimize_image_data, &block)
153
+ end
154
+
155
+ # Optimization methods with default options
156
+ def self.method_missing(method, *args, &block)
157
+ if method_defined?(method) && method.to_s =~ /^optimize_image/
158
+ new.send(method, *args, &block)
159
+ else
160
+ super
161
+ end
162
+ end
163
+
164
+ # Version of image_optim gem spec loaded
165
+ def self.version
166
+ Gem.loaded_specs['image_optim'].version.to_s rescue 'DEV'
167
+ end
168
+
169
+ # Full version of image_optim
170
+ def self.full_version
171
+ "image_optim v#{version}"
172
+ end
173
+
174
+ # Are there workers for file at path?
175
+ def optimizable?(path)
176
+ !!workers_for_image(path)
177
+ end
178
+
179
+ # Check existance of binary, create symlink if ENV contains path for key
180
+ # XXX_BIN where XXX is upper case bin name
181
+ def resolve_bin!(bin)
182
+ @bin_resolver.resolve!(bin)
183
+ end
184
+
185
+ # Join resolve_dir, default path and vendor path for PATH environment variable
186
+ def env_path
187
+ @bin_resolver.env_path
188
+ end
189
+
190
+ private
191
+
192
+ def log_workers_by_format
193
+ $stderr << "Workers by format:\n"
194
+ @workers_by_format.each do |format, workers|
195
+ $stderr << "#{format}:\n"
196
+ workers.each do |worker|
197
+ $stderr << " #{worker.class.bin_sym}:\n"
198
+ worker.options.each do |name, value|
199
+ $stderr << " #{name}: #{value.inspect}\n"
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ # Run method for each item in list
206
+ # if block given yields item and result for item and returns array of yield
207
+ # results
208
+ # else return array of item and result pairs
209
+ def run_method_for(list, method_name, &block)
210
+ apply_threading(list).map do |item|
211
+ result = send(method_name, item)
212
+ if block
213
+ block.call(item, result)
214
+ else
215
+ [item, result]
216
+ end
217
+ end
218
+ end
219
+
220
+ # Apply threading if threading is allowed
221
+ def apply_threading(enum)
222
+ if threads > 1
223
+ enum.in_threads(threads)
224
+ else
225
+ enum
226
+ end
227
+ end
228
+ end