jekyll_picture_tag 1.8.0 → 1.11.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/.rubocop.yml +4 -0
- data/.travis.yml +4 -7
- data/Dockerfile +9 -0
- data/docs/Gemfile.lock +183 -88
- data/docs/contributing.md +50 -16
- data/docs/example_presets.md +1 -1
- data/docs/global_configuration.md +55 -2
- data/docs/index.md +27 -21
- data/docs/installation.md +22 -7
- data/docs/presets.md +137 -55
- data/docs/releases.md +20 -1
- data/docs/usage.md +83 -39
- data/jekyll_picture_tag.gemspec +1 -1
- data/lib/jekyll_picture_tag.rb +28 -10
- data/lib/jekyll_picture_tag/cache.rb +3 -0
- data/lib/jekyll_picture_tag/cache/base.rb +59 -0
- data/lib/jekyll_picture_tag/cache/generated.rb +20 -0
- data/lib/jekyll_picture_tag/cache/source.rb +19 -0
- data/lib/jekyll_picture_tag/defaults/global.yml +2 -0
- data/lib/jekyll_picture_tag/defaults/presets.yml +2 -0
- data/lib/jekyll_picture_tag/generated_image.rb +85 -20
- data/lib/jekyll_picture_tag/img_uri.rb +1 -0
- data/lib/jekyll_picture_tag/instructions.rb +1 -0
- data/lib/jekyll_picture_tag/instructions/arg_splitter.rb +69 -0
- data/lib/jekyll_picture_tag/instructions/configuration.rb +47 -11
- data/lib/jekyll_picture_tag/instructions/preset.rb +35 -14
- data/lib/jekyll_picture_tag/instructions/set.rb +18 -8
- data/lib/jekyll_picture_tag/instructions/tag_parser.rb +59 -69
- data/lib/jekyll_picture_tag/output_formats/basic.rb +42 -11
- data/lib/jekyll_picture_tag/output_formats/img.rb +11 -0
- data/lib/jekyll_picture_tag/output_formats/picture.rb +22 -0
- data/lib/jekyll_picture_tag/router.rb +17 -0
- data/lib/jekyll_picture_tag/source_image.rb +60 -39
- data/lib/jekyll_picture_tag/srcsets/basic.rb +54 -19
- data/lib/jekyll_picture_tag/srcsets/pixel_ratio.rb +1 -3
- data/lib/jekyll_picture_tag/srcsets/width.rb +1 -1
- data/lib/jekyll_picture_tag/utils.rb +18 -0
- data/lib/jekyll_picture_tag/version.rb +1 -1
- data/readme.md +40 -16
- metadata +14 -8
data/docs/releases.md
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
---
|
|
3
3
|
# Release History
|
|
4
|
-
|
|
4
|
+
* 1.11.0 July 27, 2020
|
|
5
|
+
* **Width and height attribute support!** Begone, page reflow.
|
|
6
|
+
* Cache image information between builds
|
|
7
|
+
* Change image naming format. This update will trigger all images to be regenerated, so you may
|
|
8
|
+
want to delete your generated images folder beforehand.
|
|
9
|
+
* 1.10.2 July 6, 2020
|
|
10
|
+
* Bugfix for fallback image files not actually getting generated
|
|
11
|
+
* 1.10.1 July 2, 2020
|
|
12
|
+
* Bugfix for erroneously regenerated images
|
|
13
|
+
* 1.10.0 May 11, 2020
|
|
14
|
+
* **Image Cropping support!** access the power of ImageMagick's `crop` function.
|
|
15
|
+
* Don't issue a warning when `default` preset is not found.
|
|
16
|
+
* Documentation improvements
|
|
17
|
+
* 1.9.0 Feb 2, 2020
|
|
18
|
+
* Add `fast_build` global setting
|
|
19
|
+
* Add `disabled` global setting
|
|
20
|
+
* Reduce unnecessary disk IO; sites with many source images should see build times improve when
|
|
21
|
+
no new images need to be generated.
|
|
22
|
+
* Add support for empty attributes; specifically so best-practice for decorative images (`alt=""`)
|
|
23
|
+
is possible.
|
|
5
24
|
* 1.8.0 Nov 25, 2019
|
|
6
25
|
* Add `data_sizes` setting for the `data_` family of output formats.
|
|
7
26
|
* 1.7.1 Nov 14, 2019
|
data/docs/usage.md
CHANGED
|
@@ -1,65 +1,108 @@
|
|
|
1
1
|
---
|
|
2
2
|
---
|
|
3
|
+
|
|
3
4
|
# How to use the Liquid Tag:
|
|
4
5
|
|
|
6
|
+
This section describes how to use JPT's liquid tag; what options it takes and what kind of information you can pass through it to influence the form of the final HTML-markup.
|
|
7
|
+
|
|
5
8
|
## Format:
|
|
6
9
|
|
|
7
10
|
{% raw %}
|
|
8
|
-
`{% picture [preset] (
|
|
11
|
+
`{% picture [preset] (image) [crop] [alternate images & crops] [attributes] %}`
|
|
9
12
|
{% endraw %}
|
|
10
13
|
|
|
11
14
|
The only required argument is the base image. Line breaks and extra spaces are fine, and you can
|
|
12
15
|
use liquid variables anywhere.
|
|
13
16
|
|
|
17
|
+
The `preset` determines the actual HTML code that gets written to your files;
|
|
18
|
+
it is basically a recipy that takes information that you provide in the
|
|
19
|
+
liquid tag and turns it into HTML markup. When the `preset` is omitted
|
|
20
|
+
default settings will be used, in the simplest case resulting in an `img`-tag
|
|
21
|
+
containing an srcset pointing to your newly generated image sizes. You are
|
|
22
|
+
free to override the `default` preset and define your own presets, giving
|
|
23
|
+
full flexibility in what JPT will write to your files. See [markup preset]({{
|
|
24
|
+
site.baseurl }}/presets#markup-presets) for more information on this.
|
|
25
|
+
|
|
14
26
|
## Examples:
|
|
15
27
|
|
|
16
28
|
{% raw %}
|
|
17
29
|
`{% picture example.jpg %}`
|
|
18
30
|
|
|
19
|
-
`{% picture
|
|
31
|
+
`{% picture {{ page.example_liquid_picture_var }} %}`
|
|
32
|
+
|
|
33
|
+
`{% picture thumbnail example.jpg 1:1 --alt Example Image %}`
|
|
20
34
|
|
|
21
|
-
`{% picture example.jpg --picture class="attribute-demo" %}`
|
|
35
|
+
`{% picture example.jpg 16:9 north --picture class="attribute-demo" %}`
|
|
22
36
|
|
|
23
37
|
`{% picture blog_index {{ post.image }} --link {{ post.url }} %}`
|
|
24
38
|
|
|
25
39
|
`{% picture "some example.jpg" mobile: other\ example.jpg %}`
|
|
26
40
|
|
|
27
41
|
```md
|
|
28
|
-
{% picture
|
|
29
|
-
hero
|
|
30
|
-
example.jpg
|
|
31
|
-
tablet: example_cropped.jpg
|
|
32
|
-
mobile: example_cropped_more.jpg
|
|
33
|
-
--alt Happy Puppy
|
|
34
|
-
--picture class="hero"
|
|
42
|
+
{% picture
|
|
43
|
+
hero
|
|
44
|
+
example.jpg 16:9 east
|
|
45
|
+
tablet: example_cropped.jpg 3:2 east
|
|
46
|
+
mobile: example_cropped_more.jpg 1:1-50+0 east
|
|
47
|
+
--alt Happy Puppy
|
|
48
|
+
--picture class="hero"
|
|
35
49
|
--link /
|
|
36
50
|
%}
|
|
37
51
|
```
|
|
52
|
+
|
|
38
53
|
{% endraw %}
|
|
39
54
|
|
|
40
55
|
## Argument reference
|
|
41
56
|
|
|
42
57
|
Given in order:
|
|
43
58
|
|
|
44
|
-
|
|
59
|
+
- **Preset**
|
|
45
60
|
|
|
46
61
|
Select a [markup preset]({{ site.baseurl }}/presets#markup-presets), or omit to use the `default` preset. Presets
|
|
47
62
|
are collections of settings that determine nearly everything about JPT's output, from the image
|
|
48
|
-
formats used to the exact format your markup will take.
|
|
63
|
+
formats used to the exact format your final HTML-markup will take.
|
|
49
64
|
|
|
50
|
-
|
|
65
|
+
- **Base Image** (Required)
|
|
51
66
|
|
|
52
67
|
Can be any raster image (as long as you have the required ImageMagick delegate). Relative to
|
|
53
68
|
jekyll's root directory, or the `source` [setting]({{ site.baseurl }}/global_configuration) if you've configured it.
|
|
54
69
|
|
|
55
|
-
For filenames with spaces, either use double quotes (`"my image.jpg"`) or a backslash (`my\
|
|
56
|
-
|
|
70
|
+
For filenames with spaces, either use double quotes (`"my image.jpg"`) or a backslash (`my\ image.jpg`).
|
|
71
|
+
|
|
72
|
+
- **Crop**
|
|
73
|
+
|
|
74
|
+
**Check the [ installation guide ](installation) before using this feature.**
|
|
57
75
|
|
|
58
|
-
|
|
76
|
+
Crop an image to a given aspect ratio or size. This argument is given as a `geometry` and
|
|
77
|
+
(optionally) a `gravity`, which can appear in either order and are thin wrappers around
|
|
78
|
+
ImageMagick's [geometry](http://www.imagemagick.org/script/command-line-processing.php#geometry)
|
|
79
|
+
and [gravity](http://www.imagemagick.org/script/command-line-options.php#gravity) settings. The
|
|
80
|
+
values given here will override the preset settings (if present), can be given after every image,
|
|
81
|
+
and apply only to the preceding image.
|
|
59
82
|
|
|
60
|
-
|
|
83
|
+
Geometry can take many forms, but most likely you'll want to set an aspect ratio-- given in the
|
|
84
|
+
standard `width:height` ratio such as `3:2`. Gravity sets which portion of the image to keep, and
|
|
85
|
+
is given in compass directions (`north`, `southeast`, etc) or `center` (default). Cropping happens
|
|
86
|
+
before resizing; the preset `widths` setting is a post-crop value.
|
|
61
87
|
|
|
62
|
-
|
|
88
|
+
If you'd like more fine-grained control, this can be offset by appending `+|-x` and (optionally)
|
|
89
|
+
`y` pixel values to the _geometry_ (not the gravity!). Example: `1:1+400 west` means "Crop to a
|
|
90
|
+
1:1 aspect ratio, starting 400 pixels from the left side.", and `north 3:2+0+100` means "Crop to
|
|
91
|
+
3:2, starting 100 pixels from the top." These can get a bit persnickety; there's nothing to stop
|
|
92
|
+
you from running off the side of the image. Pay attention.
|
|
93
|
+
|
|
94
|
+
For detailed documentation, see ImageMagick's
|
|
95
|
+
[crop](http://www.imagemagick.org/script/command-line-options.php#crop) tool.
|
|
96
|
+
|
|
97
|
+
_Note:_ If you do a lot of trial and error with these, it's a good idea to manually delete your
|
|
98
|
+
generated images folder more often as each change will build a new set of images without removing
|
|
99
|
+
the old ones.
|
|
100
|
+
|
|
101
|
+
- **Alternate images**
|
|
102
|
+
|
|
103
|
+
_Format:_ `(media query preset): (filename) (...)`
|
|
104
|
+
|
|
105
|
+
_Example:_ `tablet: img_cropped.jpg mobile: img_cropped_more.jpg`
|
|
63
106
|
|
|
64
107
|
Optionally specify any number of alternate base images for given [screen
|
|
65
108
|
sizes]({{ site.baseurl }}/presets/#media-presets) (specified in `_data/picture.yml`). This is called [art
|
|
@@ -70,44 +113,45 @@ Given in order:
|
|
|
70
113
|
be provided to the browser in reverse order, and it will select the first one with an applicable
|
|
71
114
|
media query.
|
|
72
115
|
|
|
73
|
-
|
|
116
|
+
- **Attributes**
|
|
74
117
|
|
|
75
118
|
Optionally specify any number of HTML attributes, or an href target. These will be added to any
|
|
76
119
|
attributes you've set in a preset.
|
|
77
120
|
|
|
78
|
-
|
|
121
|
+
- **`--link`**
|
|
122
|
+
|
|
123
|
+
_Format:_ `--link (some url)`
|
|
79
124
|
|
|
80
|
-
|
|
125
|
+
_Examples_: `--link https://example.com`, `--link /blog/some_post/`
|
|
81
126
|
|
|
82
|
-
|
|
127
|
+
Wrap the image in an anchor tag, with the `href` attribute set to whatever value you give it.
|
|
128
|
+
This will override automatic source image linking, if you have enabled it.
|
|
83
129
|
|
|
84
|
-
|
|
85
|
-
|
|
130
|
+
**Note**: If you get either mangled HTML or extra {::nomarkdown} tags when using this, read
|
|
131
|
+
[here]({{ site.baseurl }}/notes).
|
|
86
132
|
|
|
87
|
-
|
|
88
|
-
[here]({{ site.baseurl }}/notes).
|
|
133
|
+
- **`--alt`**
|
|
89
134
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
*Format:* `--alt (alt text)`
|
|
135
|
+
_Format:_ `--alt (alt text)`
|
|
93
136
|
|
|
94
|
-
|
|
137
|
+
_Example:_ `--alt Here is my alt text!`
|
|
95
138
|
|
|
96
139
|
Convenience shortcut for `--img alt="..."`
|
|
97
|
-
|
|
98
|
-
* **`--(element)`**
|
|
99
140
|
|
|
100
|
-
|
|
141
|
+
- **`--(element)`**
|
|
142
|
+
|
|
143
|
+
_Format:_ `--(picture|img|source|a|parent) (Standard HTML attributes)`
|
|
101
144
|
|
|
102
|
-
|
|
145
|
+
_Example:_ `--img class="awesome-fade-in" id="coolio" --a data-awesomeness="11"`
|
|
103
146
|
|
|
104
147
|
Apply attributes to a given HTML element. Your options are:
|
|
105
148
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
149
|
+
- `picture`
|
|
150
|
+
- `img`
|
|
151
|
+
- `source`
|
|
152
|
+
- `a` (anchor tag)
|
|
153
|
+
- `parent`
|
|
111
154
|
|
|
112
155
|
`--parent` will be applied to the `<picture>` if present, otherwise the `<img>`; useful when
|
|
113
156
|
using an `auto` output format.
|
|
157
|
+
**Note:** Attributes that are set in the liquid picture tag, but don't occur in the used `preset` will be ignored (e.g. adding `--picture class="cool-css"` with a preset that does not write a html `<picture>` tag).
|
data/jekyll_picture_tag.gemspec
CHANGED
|
@@ -40,7 +40,7 @@ Gem::Specification.new do |spec|
|
|
|
40
40
|
spec.add_dependency 'addressable', '~> 2.6'
|
|
41
41
|
spec.add_dependency 'mime-types', '~> 3'
|
|
42
42
|
spec.add_dependency 'mini_magick', '~> 4'
|
|
43
|
-
spec.add_dependency 'objective_elements', '~> 1.1.
|
|
43
|
+
spec.add_dependency 'objective_elements', '~> 1.1.2'
|
|
44
44
|
|
|
45
45
|
spec.add_runtime_dependency 'jekyll', '< 5'
|
|
46
46
|
end
|
data/lib/jekyll_picture_tag.rb
CHANGED
|
@@ -9,6 +9,7 @@ require_relative 'jekyll_picture_tag/srcsets'
|
|
|
9
9
|
require_relative 'jekyll_picture_tag/utils'
|
|
10
10
|
require_relative 'jekyll_picture_tag/img_uri'
|
|
11
11
|
require_relative 'jekyll_picture_tag/router'
|
|
12
|
+
require_relative 'jekyll_picture_tag/cache'
|
|
12
13
|
|
|
13
14
|
# Title: Jekyll Picture Tag
|
|
14
15
|
# Authors: Rob Wierzbowski : @robwierzbowski
|
|
@@ -18,9 +19,9 @@ require_relative 'jekyll_picture_tag/router'
|
|
|
18
19
|
#
|
|
19
20
|
# Description: Easy responsive images for Jekyll.
|
|
20
21
|
#
|
|
21
|
-
# Download: https://
|
|
22
|
-
# Documentation: https://github.
|
|
23
|
-
# Issues: https://github.com/rbuchberger/jekyll_picture_tag/
|
|
22
|
+
# Download: https://rubygems.org/gems/jekyll_picture_tag
|
|
23
|
+
# Documentation: https://rbuchberger.github.io/jekyll_picture_tag/
|
|
24
|
+
# Issues: https://github.com/rbuchberger/jekyll_picture_tag/
|
|
24
25
|
#
|
|
25
26
|
# Syntax:
|
|
26
27
|
# {% picture [preset] img.jpg [media_query: alt-img.jpg] [attributes] %}
|
|
@@ -39,30 +40,47 @@ require_relative 'jekyll_picture_tag/router'
|
|
|
39
40
|
#
|
|
40
41
|
# See the documentation for full configuration and usage instructions.
|
|
41
42
|
module PictureTag
|
|
43
|
+
# The router module is important. If you're looking for the actual code which
|
|
44
|
+
# handles a `PictureTag.(some method)`, start there.
|
|
42
45
|
extend Router
|
|
46
|
+
|
|
43
47
|
ROOT_PATH = __dir__
|
|
44
48
|
|
|
45
49
|
# This is the actual liquid tag, which provides the interface with Jekyll.
|
|
46
50
|
class Picture < Liquid::Tag
|
|
51
|
+
# First jekyll initializes our class with a few arguments, of which we only
|
|
52
|
+
# care about the params (arguments passed to the liquid tag). Jekyll makes
|
|
53
|
+
# no attempt to parse them; they're given as a string.
|
|
47
54
|
def initialize(tag_name, raw_params, tokens)
|
|
48
55
|
@raw_params = raw_params
|
|
49
56
|
super
|
|
50
57
|
end
|
|
51
58
|
|
|
59
|
+
# Then jekyll calls the 'render' method and passes it a mostly undocumented
|
|
60
|
+
# context object, which appears to hold the entire site including its
|
|
61
|
+
# configuration and the parsed _data dir.
|
|
52
62
|
def render(context)
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
setup(context)
|
|
64
|
+
|
|
65
|
+
if PictureTag.disabled?
|
|
66
|
+
''
|
|
67
|
+
else
|
|
68
|
+
PictureTag.output_class.new.to_s
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def setup(context)
|
|
55
75
|
PictureTag.context = context
|
|
56
76
|
|
|
57
|
-
#
|
|
77
|
+
# Now that we have both the tag parameters and the context object, we can
|
|
78
|
+
# build our instruction set.
|
|
58
79
|
PictureTag.instructions = Instructions::Set.new(@raw_params)
|
|
59
80
|
|
|
60
81
|
# We need to explicitly prevent jekyll from overwriting our generated
|
|
61
|
-
# files:
|
|
82
|
+
# image files:
|
|
62
83
|
Utils.keep_files
|
|
63
|
-
|
|
64
|
-
# Return a string:
|
|
65
|
-
PictureTag.output_class.new.to_s
|
|
66
84
|
end
|
|
67
85
|
end
|
|
68
86
|
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module PictureTag
|
|
4
|
+
module Cache
|
|
5
|
+
# Basic image information cache functionality
|
|
6
|
+
module Base
|
|
7
|
+
def initialize(base_name)
|
|
8
|
+
@base_name = base_name
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def [](key)
|
|
12
|
+
data[key]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def []=(key, value)
|
|
16
|
+
raise ArgumentError unless template.keys.include? key
|
|
17
|
+
|
|
18
|
+
data[key] = value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Call after updating data.
|
|
22
|
+
def write
|
|
23
|
+
FileUtils.mkdir_p(File.join(base_directory, sub_directory))
|
|
24
|
+
|
|
25
|
+
File.open(filename, 'w+') do |f|
|
|
26
|
+
f.write JSON.generate(data)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def data
|
|
33
|
+
@data ||= if File.exist?(filename)
|
|
34
|
+
JSON.parse(File.read(filename)).transform_keys(&:to_sym)
|
|
35
|
+
else
|
|
36
|
+
template
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# /home/dave/my_blog/.jekyll-cache/jpt/(cache_dir)/assets/myimage.jpg.json
|
|
41
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
42
|
+
def base_directory
|
|
43
|
+
File.join(PictureTag.site.cache_dir, 'jpt', cache_dir)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# /home/dave/my_blog/.jekyll-cache/jpt/(cache_dir)/assets/myimage.jpg.json
|
|
47
|
+
# ^^^^^^^^
|
|
48
|
+
def sub_directory
|
|
49
|
+
File.dirname(@base_name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# /home/dave/my_blog/.jekyll-cache/jpt/somefolder/myimage.jpg.json
|
|
53
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
54
|
+
def filename
|
|
55
|
+
File.join(base_directory, @base_name + '.json')
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module PictureTag
|
|
2
|
+
module Cache
|
|
3
|
+
# Caches generated image details, so we can skip expensive operations whenever
|
|
4
|
+
# possible.
|
|
5
|
+
# Stored width and height are values for the source image, after cropping.
|
|
6
|
+
class Generated
|
|
7
|
+
include Base
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def cache_dir
|
|
12
|
+
'generated'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template
|
|
16
|
+
{ width: nil, height: nil }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module PictureTag
|
|
2
|
+
module Cache
|
|
3
|
+
# Caches source image details, so we can skip expensive operations whenever
|
|
4
|
+
# possible.
|
|
5
|
+
class Source
|
|
6
|
+
include Base
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def template
|
|
11
|
+
{ digest: nil, width: nil, height: nil }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def cache_dir
|
|
15
|
+
'source'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,47 +1,104 @@
|
|
|
1
1
|
require 'mini_magick'
|
|
2
2
|
|
|
3
3
|
module PictureTag
|
|
4
|
-
#
|
|
5
|
-
# Represents a generated source file.
|
|
4
|
+
# Represents a generated image file.
|
|
6
5
|
class GeneratedImage
|
|
7
6
|
attr_reader :width, :format
|
|
7
|
+
|
|
8
8
|
include MiniMagick
|
|
9
9
|
|
|
10
|
-
def initialize(source_file:, width:, format:)
|
|
10
|
+
def initialize(source_file:, width:, format:, crop: nil, gravity: '')
|
|
11
11
|
@source = source_file
|
|
12
12
|
@width = width
|
|
13
13
|
@format = process_format format
|
|
14
|
+
@crop = crop
|
|
15
|
+
@gravity = gravity
|
|
16
|
+
end
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
def exists?
|
|
19
|
+
File.exist?(absolute_filename)
|
|
16
20
|
end
|
|
17
21
|
|
|
18
|
-
def
|
|
19
|
-
|
|
22
|
+
def generate
|
|
23
|
+
generate_image unless @source.missing || exists?
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
# /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
|
|
27
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
22
28
|
def absolute_filename
|
|
23
29
|
@absolute_filename ||= File.join(PictureTag.dest_dir, name)
|
|
24
30
|
end
|
|
25
31
|
|
|
32
|
+
# /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
|
|
33
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
34
|
+
def name
|
|
35
|
+
@name ||= "#{@source.base_name}-#{@width}-#{id}.#{@format}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# https://example.com/assets/images/myimage-100-123abc.jpg
|
|
39
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
26
40
|
def uri
|
|
27
41
|
ImgURI.new(name).to_s
|
|
28
42
|
end
|
|
29
43
|
|
|
44
|
+
# Post crop
|
|
45
|
+
def source_width
|
|
46
|
+
update_cache unless cache[:width]
|
|
47
|
+
|
|
48
|
+
cache[:width]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Post crop
|
|
52
|
+
def source_height
|
|
53
|
+
update_cache unless cache[:height]
|
|
54
|
+
|
|
55
|
+
cache[:height]
|
|
56
|
+
end
|
|
57
|
+
|
|
30
58
|
private
|
|
31
59
|
|
|
60
|
+
# We exclude width and format from the cache name, since it isn't specific to them.
|
|
61
|
+
def cache
|
|
62
|
+
@cache ||= Cache::Generated.new("#{@source.base_name}-#{id}")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def update_cache
|
|
66
|
+
return if @source.missing
|
|
67
|
+
|
|
68
|
+
# Ensure it's generated:
|
|
69
|
+
image
|
|
70
|
+
|
|
71
|
+
cache[:width] = @source_dimensions[:width]
|
|
72
|
+
cache[:height] = @source_dimensions[:height]
|
|
73
|
+
|
|
74
|
+
cache.write
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Hash all inputs and truncate, so we know when they change without getting too long.
|
|
78
|
+
# /home/dave/my_blog/_site/generated/somefolder/myimage-100-1234abcde.jpg
|
|
79
|
+
# ^^^^^^^^^
|
|
80
|
+
def id
|
|
81
|
+
@id ||= Digest::MD5.hexdigest([@source.digest, @crop, @gravity, quality].join)[0..8]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Post crop, before resizing and reformatting
|
|
32
85
|
def image
|
|
33
|
-
@image ||=
|
|
86
|
+
@image ||= open_image
|
|
34
87
|
end
|
|
35
88
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
89
|
+
def open_image
|
|
90
|
+
image_base = Image.open(@source.name)
|
|
91
|
+
image_base.combine_options do |i|
|
|
39
92
|
i.auto_orient
|
|
40
|
-
|
|
93
|
+
if @crop
|
|
94
|
+
i.gravity @gravity
|
|
95
|
+
i.crop @crop
|
|
96
|
+
end
|
|
41
97
|
end
|
|
42
98
|
|
|
43
|
-
|
|
44
|
-
|
|
99
|
+
@source_dimensions = { width: image_base.width, height: image_base.height }
|
|
100
|
+
|
|
101
|
+
image_base
|
|
45
102
|
end
|
|
46
103
|
|
|
47
104
|
def generate_image
|
|
@@ -50,20 +107,28 @@ module PictureTag
|
|
|
50
107
|
write_image
|
|
51
108
|
end
|
|
52
109
|
|
|
110
|
+
def quality
|
|
111
|
+
PictureTag.quality(@format)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def process_image
|
|
115
|
+
image.combine_options do |i|
|
|
116
|
+
i.resize "#{@width}x"
|
|
117
|
+
i.strip
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
image.format @format
|
|
121
|
+
image.quality quality
|
|
122
|
+
end
|
|
123
|
+
|
|
53
124
|
def write_image
|
|
54
|
-
|
|
125
|
+
FileUtils.mkdir_p(File.dirname(absolute_filename))
|
|
55
126
|
|
|
56
127
|
image.write absolute_filename
|
|
57
128
|
|
|
58
129
|
FileUtils.chmod(0o644, absolute_filename)
|
|
59
130
|
end
|
|
60
131
|
|
|
61
|
-
# Make sure destination directory exists
|
|
62
|
-
def check_dest_dir
|
|
63
|
-
dir = File.dirname absolute_filename
|
|
64
|
-
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
132
|
def process_format(format)
|
|
68
133
|
if format.casecmp('original').zero?
|
|
69
134
|
@source.ext
|