openstreetmap-image_optim 0.21.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +65 -0
- data/.travis.yml +42 -0
- data/CHANGELOG.markdown +272 -0
- data/CONTRIBUTING.markdown +10 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +344 -0
- data/Vagrantfile +33 -0
- data/bin/image_optim +28 -0
- data/image_optim.gemspec +29 -0
- data/lib/image_optim.rb +228 -0
- data/lib/image_optim/bin_resolver.rb +144 -0
- data/lib/image_optim/bin_resolver/bin.rb +105 -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/cmd.rb +49 -0
- data/lib/image_optim/config.rb +205 -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 +25 -0
- data/lib/image_optim/image_path.rb +68 -0
- data/lib/image_optim/non_negative_integer_range.rb +11 -0
- data/lib/image_optim/option_definition.rb +32 -0
- data/lib/image_optim/option_helpers.rb +17 -0
- data/lib/image_optim/railtie.rb +38 -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 +227 -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 +159 -0
- data/lib/image_optim/worker/advpng.rb +35 -0
- data/lib/image_optim/worker/class_methods.rb +91 -0
- data/lib/image_optim/worker/gifsicle.rb +63 -0
- data/lib/image_optim/worker/jhead.rb +43 -0
- data/lib/image_optim/worker/jpegoptim.rb +58 -0
- data/lib/image_optim/worker/jpegrecompress.rb +44 -0
- data/lib/image_optim/worker/jpegtran.rb +46 -0
- data/lib/image_optim/worker/optipng.rb +45 -0
- data/lib/image_optim/worker/pngcrush.rb +54 -0
- data/lib/image_optim/worker/pngout.rb +38 -0
- data/lib/image_optim/worker/pngquant.rb +51 -0
- data/lib/image_optim/worker/svgo.rb +32 -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 +60 -0
- data/script/worker_analysis +599 -0
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +37 -0
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +57 -0
- data/spec/image_optim/bin_resolver_spec.rb +272 -0
- data/spec/image_optim/cmd_spec.rb +66 -0
- data/spec/image_optim/config_spec.rb +217 -0
- data/spec/image_optim/handler_spec.rb +95 -0
- data/spec/image_optim/hash_helpers_spec.rb +76 -0
- data/spec/image_optim/image_path_spec.rb +54 -0
- data/spec/image_optim/railtie_spec.rb +121 -0
- data/spec/image_optim/runner/glob_helpers_spec.rb +25 -0
- data/spec/image_optim/runner/option_parser_spec.rb +99 -0
- data/spec/image_optim/space_spec.rb +25 -0
- data/spec/image_optim/worker_spec.rb +192 -0
- data/spec/image_optim_spec.rb +242 -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 +64 -0
- data/vendor/jpegrescan +143 -0
- metadata +308 -0
data/README.markdown
ADDED
@@ -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.
|
data/Vagrantfile
ADDED
@@ -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
|
data/bin/image_optim
ADDED
@@ -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
|
data/image_optim.gemspec
ADDED
@@ -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
|
data/lib/image_optim.rb
ADDED
@@ -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
|