jekyll-images 0.1.0 → 0.2.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/README.md +85 -13
- data/jekyll-images.gemspec +1 -0
- data/lib/jekyll-images.rb +4 -0
- data/lib/jekyll/filters/thumbnail.rb +10 -4
- data/lib/jekyll/images/jpeg_optim.rb +18 -0
- data/lib/jekyll/images/oxipng.rb +18 -0
- data/lib/jekyll/images/runner.rb +62 -0
- data/lib/jekyll/images/thumbnail.rb +52 -14
- data/lib/jekyll/images/version.rb +1 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b16d950e3f192f849e1577a16fc572c9bb698604de4bf31a64b1c62285ac00ef
|
4
|
+
data.tar.gz: 1072440b92d9e2351a799100d1aef666fe14441bc0d52c93ac40ed71eba14cfb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c87cae74ddaacce079c5a192af9c1e1315a198176730a8b6f0b82dbf5e1868c0cc179ef191519ee502c6c839928ea56339a2a212099c87a21f53cd69c6c14ae
|
7
|
+
data.tar.gz: a89803b7bc464ab3b1a27a135946406814016ca61374ad7c904c93144369d912249b06f6b1e32cbc3c988e1ecda7515cc0ecce0cea8f4524ee87a58a80479a66
|
data/README.md
CHANGED
@@ -1,15 +1,27 @@
|
|
1
|
-
# Jekyll
|
1
|
+
# Image optimization for Jekyll
|
2
2
|
|
3
|
-
|
3
|
+
This Jekyll plugin helps you optimize images by thumbnailing them to
|
4
|
+
the sizes you specify in templates and in the configuration for images
|
5
|
+
in posts and collections.
|
4
6
|
|
5
|
-
|
7
|
+
It uses [Libvips](https://libvips.github.io/libvips/) for performance
|
8
|
+
and low resource usage. It'll also cache the thumbnails so it only runs
|
9
|
+
once, but it'll update the thumbnail if the source image changes (or is
|
10
|
+
`touch`ed).
|
11
|
+
|
12
|
+
The thumbnails are smartly cropped by default to point of attention, so
|
13
|
+
they're more useful than just cropping at the center, but you can change
|
14
|
+
this (see below).
|
15
|
+
|
16
|
+
It will run `jpegoptim` and `oxipng` if it can find the binaries
|
17
|
+
installed for extra optimization.
|
6
18
|
|
7
19
|
## Installation
|
8
20
|
|
9
|
-
Add this line to your
|
21
|
+
Add this line to your site's Gemfile:
|
10
22
|
|
11
23
|
```ruby
|
12
|
-
gem 'jekyll-
|
24
|
+
gem 'jekyll-images'
|
13
25
|
```
|
14
26
|
|
15
27
|
And then execute:
|
@@ -18,26 +30,86 @@ And then execute:
|
|
18
30
|
|
19
31
|
Or install it yourself as:
|
20
32
|
|
21
|
-
$ gem install jekyll-
|
33
|
+
$ gem install jekyll-images
|
22
34
|
|
23
35
|
## Usage
|
24
36
|
|
25
|
-
|
37
|
+
### Liquid templates
|
38
|
+
|
39
|
+
In your templates, you can use the `thumbnail` filter:
|
40
|
+
|
41
|
+
```html
|
42
|
+
<img src="{{ page.image.path | thumbnail: 100 }}" />
|
43
|
+
|
44
|
+
<!-- responsive images -->
|
45
|
+
|
46
|
+
<picture>
|
47
|
+
{% for size in site.images.sizes %}
|
48
|
+
<source srcset="{{ page.image.path | thumbnail: size }}" media="(max-width: {{ size }}px)" />
|
49
|
+
{% endfor %}
|
50
|
+
|
51
|
+
<img src="{{ page.image.path | thumbnail: 750 }}" />
|
52
|
+
</picture>
|
53
|
+
```
|
54
|
+
|
55
|
+
Options for this filter are in the following order:
|
26
56
|
|
27
|
-
|
57
|
+
* `width` (required), the desired width for the image
|
58
|
+
|
59
|
+
* `height`, if provided, the thumbnail will crop to this size. If not,
|
60
|
+
the image is scaled down proportionally to the width.
|
61
|
+
|
62
|
+
* `crop` the smart cropping algorithm. One of `none`, `centre`,
|
63
|
+
`entropy` or `attention` (default). See
|
64
|
+
[Vips::Interesting](https://www.rubydoc.info/gems/ruby-vips/Vips/Interesting)
|
65
|
+
for documentation.
|
66
|
+
|
67
|
+
* `auto_rotate`, a boolean (defaults to `true`). If the image has
|
68
|
+
orientation metadata, this controls if it's automatically rotated.
|
69
|
+
|
70
|
+
If you want to pass `crop` and `auto_rotate` but not `height`, just set
|
71
|
+
`height` to `0` or `''`.
|
72
|
+
|
73
|
+
### Configuration and posts
|
74
|
+
|
75
|
+
Images in a post content need to be configured globally:
|
76
|
+
|
77
|
+
```yaml
|
78
|
+
# _config.yml
|
79
|
+
images:
|
80
|
+
# These are bootstrap4 breakpoints in pixels
|
81
|
+
sizes:
|
82
|
+
- 576
|
83
|
+
- 768
|
84
|
+
- 992
|
85
|
+
- 1200
|
86
|
+
thumbnail:
|
87
|
+
width: 600
|
88
|
+
height: 0 # Leave this out for proportional thumbnailing
|
89
|
+
crop: attention # See Vips::Interesting
|
90
|
+
auto_rotate: true
|
91
|
+
```
|
28
92
|
|
29
|
-
|
93
|
+
## TODO
|
30
94
|
|
31
|
-
|
95
|
+
* Use `<picture>` and the `srcset` attribute automatically for posts
|
96
|
+
<https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture>
|
32
97
|
|
33
98
|
## Contributing
|
34
99
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at
|
100
|
+
Bug reports and pull requests are welcome on GitHub at
|
101
|
+
<https://0xacab.org/sutty/jekyll/jekyll-images>. This project is
|
102
|
+
intended to be a welcoming space for collaboration, and contributors are
|
103
|
+
expected to adhere to the [Sutty code of
|
104
|
+
conduct](https://sutty.nl/en/code-of-conduct/).
|
36
105
|
|
37
106
|
## License
|
38
107
|
|
39
|
-
The gem is available as
|
108
|
+
The gem is available as free software under the terms of the GPL3
|
109
|
+
License.
|
40
110
|
|
41
111
|
## Code of Conduct
|
42
112
|
|
43
|
-
Everyone interacting in the
|
113
|
+
Everyone interacting in the jekyll-images project’s codebases, issue
|
114
|
+
trackers, chat rooms and mailing lists is expected to follow the [code
|
115
|
+
of conduct](https://sutty.nl/en/code-of-conduct/).
|
data/jekyll-images.gemspec
CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.require_paths = ['lib']
|
32
32
|
|
33
33
|
spec.add_dependency 'ruby-vips', '~> 2'
|
34
|
+
spec.add_dependency 'ruby-filemagic', '~> 0.7'
|
34
35
|
|
35
36
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
36
37
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
data/lib/jekyll-images.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'pry'
|
3
4
|
require_relative 'jekyll/images/thumbnail'
|
4
5
|
require_relative 'jekyll/filters/thumbnail'
|
6
|
+
require_relative 'jekyll/hooks/thumbnail'
|
7
|
+
require_relative 'jekyll/images/oxipng'
|
8
|
+
require_relative 'jekyll/images/jpeg_optim'
|
@@ -4,15 +4,21 @@ module Jekyll
|
|
4
4
|
module Filters
|
5
5
|
# Liquid filter for use in templates
|
6
6
|
module Thumbnail
|
7
|
-
# Generates a thumbnail and returns its destination
|
8
|
-
def thumbnail(input, width, height, crop = :attention, auto_rotate = true)
|
9
|
-
|
7
|
+
# Generates a thumbnail and returns its alternate destination
|
8
|
+
def thumbnail(input, width, height = nil, crop = :attention, auto_rotate = true)
|
9
|
+
return unless input
|
10
|
+
|
11
|
+
height = height if height.to_i > 1
|
12
|
+
image = Jekyll::Images::Thumbnail.new(@context.registers[:site],
|
13
|
+
input,
|
10
14
|
width: width,
|
11
15
|
height: height,
|
12
16
|
crop: crop,
|
13
17
|
auto_rotate: auto_rotate)
|
14
|
-
image.write
|
15
18
|
|
19
|
+
# XXX: This won't run optimizations more than once but it won't
|
20
|
+
# also optimize source images.
|
21
|
+
image.write && image.optimize
|
16
22
|
image.dest
|
17
23
|
end
|
18
24
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'runner'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module Images
|
7
|
+
# Runs jpegoptim on JPEG files
|
8
|
+
class JpegOptim < Runner
|
9
|
+
BINARY = 'jpegoptim'.freeze
|
10
|
+
|
11
|
+
def command
|
12
|
+
[binary, '--strip-all', '--quiet', file]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Jekyll::Images::Runner.register('jpeg', Jekyll::Images::JpegOptim)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'runner'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module Images
|
7
|
+
# Runs oxipng on PNG files
|
8
|
+
class Oxipng < Runner
|
9
|
+
BINARY = 'oxipng'.freeze
|
10
|
+
|
11
|
+
def command
|
12
|
+
[binary, '--strip', 'all', '--quiet', file]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Jekyll::Images::Runner.register('png', Jekyll::Images::Oxipng)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'filemagic'
|
5
|
+
|
6
|
+
module Jekyll
|
7
|
+
module Images
|
8
|
+
# Runner for optimizations
|
9
|
+
class Runner
|
10
|
+
attr_reader :binary, :bytes_after, :bytes_before, :file
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# XXX: This only allows one optimization per file type, we could
|
14
|
+
# make it an array but how do we garantee order? Maybe adding a
|
15
|
+
# priority field?
|
16
|
+
def register(mime, class_name)
|
17
|
+
@@runners ||= {}
|
18
|
+
@@runners[mime] = class_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def runners
|
22
|
+
@@runners
|
23
|
+
end
|
24
|
+
|
25
|
+
def mime(file)
|
26
|
+
@@mime ||= FileMagic.new
|
27
|
+
|
28
|
+
@@mime.file(file, true)
|
29
|
+
end
|
30
|
+
|
31
|
+
def run(file)
|
32
|
+
type = mime(file)
|
33
|
+
runners[type].new(file).run if runners[type]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(file)
|
38
|
+
@file = file
|
39
|
+
@bytes_before = bytes_after
|
40
|
+
end
|
41
|
+
|
42
|
+
def bytes_after
|
43
|
+
File.size file
|
44
|
+
end
|
45
|
+
|
46
|
+
def exist?
|
47
|
+
@binary, _, status = Open3.capture3("which #{self.class::BINARY}")
|
48
|
+
@binary.chomp!
|
49
|
+
|
50
|
+
status.success?
|
51
|
+
end
|
52
|
+
|
53
|
+
def run
|
54
|
+
return unless exist?
|
55
|
+
|
56
|
+
_, status = Open3.capture2e(*command)
|
57
|
+
|
58
|
+
[bytes_before, bytes_after] if status.success?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -8,38 +8,76 @@ module Jekyll
|
|
8
8
|
#
|
9
9
|
# We're assuming every image is going to be thumbnailed
|
10
10
|
class Thumbnail
|
11
|
-
attr_reader :image, :filename, :width, :height
|
11
|
+
attr_reader :site, :image, :thumbnail, :filename, :width, :height
|
12
12
|
|
13
|
-
def initialize(filename, **args)
|
13
|
+
def initialize(site, filename, **args)
|
14
14
|
unless File.exist? filename
|
15
15
|
raise ArgumentError, "File not found: #{filename}"
|
16
16
|
end
|
17
|
-
raise ArgumentError, 'Missing width' unless width
|
18
|
-
raise ArgumentError, 'Missing height' unless height
|
17
|
+
raise ArgumentError, 'Missing width' unless args[:width]
|
19
18
|
|
20
|
-
@
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
@site = site
|
20
|
+
@filename = filename
|
21
|
+
@width = args[:width]
|
22
|
+
@height = args[:height]
|
23
|
+
@image = Vips::Image.new_from_file filename, access: :sequential
|
24
|
+
@thumbnail = image.thumbnail_image width,
|
25
|
+
height: height || proportional_height,
|
26
|
+
auto_rotate: args[:auto_rotate],
|
27
|
+
crop: args[:crop].to_sym
|
24
28
|
end
|
25
29
|
|
26
|
-
#
|
30
|
+
# Finds a height that's proportional to the width
|
31
|
+
def proportional_height
|
32
|
+
@height = (image.height * (width / image.width.to_f)).round
|
33
|
+
end
|
34
|
+
|
35
|
+
# Generates a destination from filename only if we're downsizing
|
27
36
|
def dest
|
28
|
-
|
37
|
+
@dest ||= if image.width > width
|
38
|
+
filename.gsub(/#{extname}\z/, "_#{width}x#{height}#{extname}")
|
39
|
+
else
|
40
|
+
Jekyll.logger.info "Not thumbnailing #{filename}"
|
41
|
+
filename
|
42
|
+
end
|
29
43
|
end
|
30
44
|
|
31
45
|
def extname
|
32
46
|
@extname ||= File.extname filename
|
33
47
|
end
|
34
48
|
|
35
|
-
# Only write when the
|
49
|
+
# Only write when the source is newer than the thumbnail
|
36
50
|
def write?
|
37
|
-
File.mtime(filename) > File.mtime(dest)
|
51
|
+
!File.exist?(dest) || File.mtime(filename) > File.mtime(dest)
|
38
52
|
end
|
39
53
|
|
40
|
-
# Save the file into destination if needed
|
54
|
+
# Save the file into destination if needed and add to files to
|
55
|
+
# copy
|
41
56
|
def write
|
42
|
-
|
57
|
+
return unless write?
|
58
|
+
|
59
|
+
Jekyll.logger.info "Thumbnailing #{filename} => #{dest}"
|
60
|
+
thumbnail.write_to_file(dest)
|
61
|
+
|
62
|
+
# Add it to the static files so Jekyll copies them. Once they
|
63
|
+
# are written they're copied when the site is loaded.
|
64
|
+
site.static_files << Jekyll::StaticFile.new(site, site.source,
|
65
|
+
File.dirname(dest),
|
66
|
+
File.basename(dest))
|
67
|
+
|
68
|
+
# The file was updated, so it exists and is newer than source
|
69
|
+
!write?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Run optimizations
|
73
|
+
def optimize
|
74
|
+
before, after = Runner.run(dest)
|
75
|
+
|
76
|
+
return unless before
|
77
|
+
|
78
|
+
pct = ((after.to_f / before) * -100 + 100).round(2)
|
79
|
+
|
80
|
+
Jekyll.logger.info "Reduced #{filename} from #{before} to #{after} bytes (%#{pct})"
|
43
81
|
end
|
44
82
|
end
|
45
83
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-images
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- f
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-vips
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-filemagic
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.7'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,6 +112,9 @@ files:
|
|
98
112
|
- jekyll-images.gemspec
|
99
113
|
- lib/jekyll-images.rb
|
100
114
|
- lib/jekyll/filters/thumbnail.rb
|
115
|
+
- lib/jekyll/images/jpeg_optim.rb
|
116
|
+
- lib/jekyll/images/oxipng.rb
|
117
|
+
- lib/jekyll/images/runner.rb
|
101
118
|
- lib/jekyll/images/thumbnail.rb
|
102
119
|
- lib/jekyll/images/version.rb
|
103
120
|
homepage: https://0xacab.org/sutty/jekyll/jekyll-images
|