jekyll_picture_tag 1.9.0 → 1.10.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/.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/index.md +26 -20
- data/docs/installation.md +22 -7
- data/docs/presets.md +106 -54
- data/docs/releases.md +4 -0
- data/docs/usage.md +68 -38
- data/jekyll_picture_tag.gemspec +2 -1
- data/lib/jekyll_picture_tag/defaults/presets.yml +1 -0
- data/lib/jekyll_picture_tag/generated_image.rb +47 -15
- data/lib/jekyll_picture_tag/instructions.rb +1 -0
- data/lib/jekyll_picture_tag/instructions/arg_splitter.rb +68 -0
- data/lib/jekyll_picture_tag/instructions/preset.rb +34 -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 +28 -11
- data/lib/jekyll_picture_tag/router.rb +8 -0
- data/lib/jekyll_picture_tag/srcsets/basic.rb +20 -7
- data/lib/jekyll_picture_tag/utils.rb +18 -0
- data/lib/jekyll_picture_tag/version.rb +1 -1
- data/readme.md +35 -16
- metadata +21 -5
data/docs/releases.md
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
---
|
3
3
|
# Release History
|
4
4
|
|
5
|
+
* 1.10.0 May 11, 2020
|
6
|
+
* **Image Cropping support!** access the power of ImageMagick's `crop` function.
|
7
|
+
* Don't issue a warning when `default` preset is not found.
|
8
|
+
* Documentation improvements
|
5
9
|
* 1.9.0 Feb 2, 2020
|
6
10
|
* Add `fast_build` global setting
|
7
11
|
* Add `disabled` global setting
|
data/docs/usage.md
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
---
|
2
2
|
---
|
3
|
+
|
3
4
|
# How to use the Liquid Tag:
|
4
5
|
|
5
6
|
## Format:
|
6
7
|
|
7
8
|
{% raw %}
|
8
|
-
`{% picture [preset] (
|
9
|
+
`{% picture [preset] (image) [crop] [alternate images & crops] [attributes] %}`
|
9
10
|
{% endraw %}
|
10
11
|
|
11
12
|
The only required argument is the base image. Line breaks and extra spaces are fine, and you can
|
@@ -16,50 +17,79 @@ use liquid variables anywhere.
|
|
16
17
|
{% raw %}
|
17
18
|
`{% picture example.jpg %}`
|
18
19
|
|
19
|
-
`{% picture thumbnail example.jpg --alt Example Image %}`
|
20
|
+
`{% picture thumbnail example.jpg 1:1 --alt Example Image %}`
|
20
21
|
|
21
|
-
`{% picture example.jpg --picture class="attribute-demo" %}`
|
22
|
+
`{% picture example.jpg 16:9 north --picture class="attribute-demo" %}`
|
22
23
|
|
23
24
|
`{% picture blog_index {{ post.image }} --link {{ post.url }} %}`
|
24
25
|
|
25
26
|
`{% picture "some example.jpg" mobile: other\ example.jpg %}`
|
26
27
|
|
27
28
|
```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"
|
29
|
+
{% picture
|
30
|
+
hero
|
31
|
+
example.jpg 16:9 east
|
32
|
+
tablet: example_cropped.jpg 3:2 east
|
33
|
+
mobile: example_cropped_more.jpg 1:1-50+0 east
|
34
|
+
--alt Happy Puppy
|
35
|
+
--picture class="hero"
|
35
36
|
--link /
|
36
37
|
%}
|
37
38
|
```
|
39
|
+
|
38
40
|
{% endraw %}
|
39
41
|
|
40
42
|
## Argument reference
|
41
43
|
|
42
44
|
Given in order:
|
43
45
|
|
44
|
-
|
46
|
+
- **Preset**
|
45
47
|
|
46
48
|
Select a [markup preset]({{ site.baseurl }}/presets#markup-presets), or omit to use the `default` preset. Presets
|
47
49
|
are collections of settings that determine nearly everything about JPT's output, from the image
|
48
50
|
formats used to the exact format your markup will take.
|
49
51
|
|
50
|
-
|
52
|
+
- **Base Image** (Required)
|
51
53
|
|
52
54
|
Can be any raster image (as long as you have the required ImageMagick delegate). Relative to
|
53
55
|
jekyll's root directory, or the `source` [setting]({{ site.baseurl }}/global_configuration) if you've configured it.
|
54
56
|
|
55
|
-
For filenames with spaces, either use double quotes (`"my image.jpg"`) or a backslash (`my\
|
56
|
-
|
57
|
+
For filenames with spaces, either use double quotes (`"my image.jpg"`) or a backslash (`my\ image.jpg`).
|
58
|
+
|
59
|
+
- **Crop**
|
60
|
+
|
61
|
+
**Check the [ installation guide ](installation) before using this feature.**
|
62
|
+
|
63
|
+
Crop an image to a given aspect ratio or size. This argument is given as a `geometry` and
|
64
|
+
(optionally) a `gravity`, which can appear in either order and are thin wrappers around
|
65
|
+
ImageMagick's [geometry](http://www.imagemagick.org/script/command-line-processing.php#geometry)
|
66
|
+
and [gravity](http://www.imagemagick.org/script/command-line-options.php#gravity) settings. The
|
67
|
+
values given here will override the preset settings (if present), can be given after every image,
|
68
|
+
and apply only to the preceding image.
|
69
|
+
|
70
|
+
Geometry can take many forms, but most likely you'll want to set an aspect ratio-- given in the
|
71
|
+
standard `width:height` ratio such as `3:2`. Gravity sets which portion of the image to keep, and
|
72
|
+
is given in compass directions (`north`, `southeast`, etc) or `center` (default). Cropping happens
|
73
|
+
before resizing; the preset `widths` setting is a post-crop value.
|
57
74
|
|
58
|
-
|
75
|
+
If you'd like more fine-grained control, this can be offset by appending `+|-x` and (optionally)
|
76
|
+
`y` pixel values to the _geometry_ (not the gravity!). Example: `1:1+400 west` means "Crop to a
|
77
|
+
1:1 aspect ratio, starting 400 pixels from the left side.", and `north 3:2+0+100` means "Crop to
|
78
|
+
3:2, starting 100 pixels from the top." These can get a bit persnickety; there's nothing to stop
|
79
|
+
you from running off the side of the image. Pay attention.
|
59
80
|
|
60
|
-
|
81
|
+
For detailed documentation, see ImageMagick's
|
82
|
+
[crop](http://www.imagemagick.org/script/command-line-options.php#crop) tool.
|
61
83
|
|
62
|
-
|
84
|
+
_Note:_ If you do a lot of trial and error with these, it's a good idea to manually delete your
|
85
|
+
generated images folder more often as each change will build a new set of images without removing
|
86
|
+
the old ones.
|
87
|
+
|
88
|
+
- **Alternate images**
|
89
|
+
|
90
|
+
_Format:_ `(media query preset): (filename) (...)`
|
91
|
+
|
92
|
+
_Example:_ `tablet: img_cropped.jpg mobile: img_cropped_more.jpg`
|
63
93
|
|
64
94
|
Optionally specify any number of alternate base images for given [screen
|
65
95
|
sizes]({{ site.baseurl }}/presets/#media-presets) (specified in `_data/picture.yml`). This is called [art
|
@@ -70,44 +100,44 @@ Given in order:
|
|
70
100
|
be provided to the browser in reverse order, and it will select the first one with an applicable
|
71
101
|
media query.
|
72
102
|
|
73
|
-
|
103
|
+
- **Attributes**
|
74
104
|
|
75
105
|
Optionally specify any number of HTML attributes, or an href target. These will be added to any
|
76
106
|
attributes you've set in a preset.
|
77
107
|
|
78
|
-
|
108
|
+
- **`--link`**
|
79
109
|
|
80
|
-
|
110
|
+
_Format:_ `--link (some url)`
|
81
111
|
|
82
|
-
|
112
|
+
_Examples_: `--link https://example.com`, `--link /blog/some_post/`
|
83
113
|
|
84
|
-
|
85
|
-
|
114
|
+
Wrap the image in an anchor tag, with the `href` attribute set to whatever value you give it.
|
115
|
+
This will override automatic source image linking, if you have enabled it.
|
86
116
|
|
87
|
-
|
88
|
-
|
117
|
+
**Note**: If you get either mangled HTML or extra {::nomarkdown} tags when using this, read
|
118
|
+
[here]({{ site.baseurl }}/notes).
|
89
119
|
|
90
|
-
|
91
|
-
|
92
|
-
*Format:* `--alt (alt text)`
|
120
|
+
- **`--alt`**
|
93
121
|
|
94
|
-
|
122
|
+
_Format:_ `--alt (alt text)`
|
123
|
+
|
124
|
+
_Example:_ `--alt Here is my alt text!`
|
95
125
|
|
96
126
|
Convenience shortcut for `--img alt="..."`
|
97
|
-
|
98
|
-
* **`--(element)`**
|
99
127
|
|
100
|
-
|
128
|
+
- **`--(element)`**
|
129
|
+
|
130
|
+
_Format:_ `--(picture|img|source|a|parent) (Standard HTML attributes)`
|
101
131
|
|
102
|
-
|
132
|
+
_Example:_ `--img class="awesome-fade-in" id="coolio" --a data-awesomeness="11"`
|
103
133
|
|
104
134
|
Apply attributes to a given HTML element. Your options are:
|
105
135
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
136
|
+
- `picture`
|
137
|
+
- `img`
|
138
|
+
- `source`
|
139
|
+
- `a` (anchor tag)
|
140
|
+
- `parent`
|
111
141
|
|
112
142
|
`--parent` will be applied to the `<picture>` if present, otherwise the `<img>`; useful when
|
113
143
|
using an `auto` output format.
|
data/jekyll_picture_tag.gemspec
CHANGED
@@ -38,9 +38,10 @@ Gem::Specification.new do |spec|
|
|
38
38
|
spec.add_development_dependency 'solargraph'
|
39
39
|
|
40
40
|
spec.add_dependency 'addressable', '~> 2.6'
|
41
|
+
spec.add_dependency 'base32', '~> 0.3'
|
41
42
|
spec.add_dependency 'mime-types', '~> 3'
|
42
43
|
spec.add_dependency 'mini_magick', '~> 4'
|
43
|
-
spec.add_dependency 'objective_elements', '~> 1.1.
|
44
|
+
spec.add_dependency 'objective_elements', '~> 1.1.2'
|
44
45
|
|
45
46
|
spec.add_runtime_dependency 'jekyll', '< 5'
|
46
47
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mini_magick'
|
2
|
+
require 'base32'
|
2
3
|
|
3
4
|
module PictureTag
|
4
5
|
# Represents a generated image file.
|
@@ -6,10 +7,12 @@ module PictureTag
|
|
6
7
|
attr_reader :width, :format
|
7
8
|
include MiniMagick
|
8
9
|
|
9
|
-
def initialize(source_file:, width:, format:)
|
10
|
+
def initialize(source_file:, width:, format:, crop: nil, gravity: '')
|
10
11
|
@source = source_file
|
11
12
|
@width = width
|
12
13
|
@format = process_format format
|
14
|
+
@crop = crop
|
15
|
+
@gravity = gravity
|
13
16
|
end
|
14
17
|
|
15
18
|
def exists?
|
@@ -38,6 +41,10 @@ module PictureTag
|
|
38
41
|
ImgURI.new(name).to_s
|
39
42
|
end
|
40
43
|
|
44
|
+
def cropped_source_width
|
45
|
+
image.width
|
46
|
+
end
|
47
|
+
|
41
48
|
private
|
42
49
|
|
43
50
|
# /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
|
@@ -57,7 +64,19 @@ module PictureTag
|
|
57
64
|
# /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
|
58
65
|
# ^^^^
|
59
66
|
def name_right
|
60
|
-
'.' + @format
|
67
|
+
crop_code + '.' + @format
|
68
|
+
end
|
69
|
+
|
70
|
+
# Hash the crop settings, so we can detect when they change. We use a
|
71
|
+
# base32 encoding scheme to pack more information into fewer characters,
|
72
|
+
# without dealing with various filesystem naming limitations that would crop
|
73
|
+
# up using base64 (such as NTFS being case insensitive).
|
74
|
+
def crop_code
|
75
|
+
return '' unless @crop
|
76
|
+
|
77
|
+
Base32.encode(
|
78
|
+
Digest::MD5.hexdigest(@crop + @gravity)
|
79
|
+
)[0..2]
|
61
80
|
end
|
62
81
|
|
63
82
|
# Used for the fast build option: look for a file which matches everything
|
@@ -77,15 +96,12 @@ module PictureTag
|
|
77
96
|
|
78
97
|
# Returns a list of images which are probably correct.
|
79
98
|
def dest_glob
|
80
|
-
|
81
|
-
|
99
|
+
query = File.join(
|
100
|
+
PictureTag.dest_dir,
|
101
|
+
name_left + '?' * 6 + name_right
|
102
|
+
)
|
82
103
|
|
83
|
-
|
84
|
-
@image ||= Image.open(@source.name)
|
85
|
-
end
|
86
|
-
|
87
|
-
def dest_dir
|
88
|
-
File.dirname absolute_filename
|
104
|
+
Dir.glob query
|
89
105
|
end
|
90
106
|
|
91
107
|
def generate_image
|
@@ -94,10 +110,26 @@ module PictureTag
|
|
94
110
|
write_image
|
95
111
|
end
|
96
112
|
|
113
|
+
def image
|
114
|
+
@image ||= open_image
|
115
|
+
end
|
116
|
+
|
117
|
+
def open_image
|
118
|
+
image_base = Image.open(@source.name)
|
119
|
+
image_base.combine_options do |i|
|
120
|
+
i.auto_orient
|
121
|
+
if @crop
|
122
|
+
i.gravity @gravity
|
123
|
+
i.crop @crop
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
image_base
|
128
|
+
end
|
129
|
+
|
97
130
|
def process_image
|
98
131
|
image.combine_options do |i|
|
99
132
|
i.resize "#{@width}x"
|
100
|
-
i.auto_orient
|
101
133
|
i.strip
|
102
134
|
end
|
103
135
|
|
@@ -106,16 +138,16 @@ module PictureTag
|
|
106
138
|
end
|
107
139
|
|
108
140
|
def write_image
|
109
|
-
|
141
|
+
# Make sure destination directory exists:
|
142
|
+
FileUtils.mkdir_p(dest_dir) unless Dir.exist?(dest_dir)
|
110
143
|
|
111
144
|
image.write absolute_filename
|
112
145
|
|
113
146
|
FileUtils.chmod(0o644, absolute_filename)
|
114
147
|
end
|
115
148
|
|
116
|
-
|
117
|
-
|
118
|
-
FileUtils.mkdir_p(dest_dir) unless Dir.exist?(dest_dir)
|
149
|
+
def dest_dir
|
150
|
+
File.dirname absolute_filename
|
119
151
|
end
|
120
152
|
|
121
153
|
def process_format(format)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# This class takes in the arguments passed to the liquid tag, and splits it
|
4
|
+
# up into 'words' (correctly handling quotes and backslash escapes.)
|
5
|
+
#
|
6
|
+
# To handle quotes and backslash escaping, we have to parse the string by
|
7
|
+
# characters to break it up correctly. I'm sure there's a library to do
|
8
|
+
# this, but it's not that much code honestly. If this starts getting big,
|
9
|
+
# we'll pull in a new dependency.
|
10
|
+
#
|
11
|
+
class ArgSplitter
|
12
|
+
attr_reader :words
|
13
|
+
|
14
|
+
def initialize(raw_params)
|
15
|
+
@words = []
|
16
|
+
@word = ''
|
17
|
+
@in_quotes = false
|
18
|
+
@escaped = false
|
19
|
+
|
20
|
+
raw_params.each_char { |c| handle_char(c) }
|
21
|
+
|
22
|
+
add_word # We have to explicitly add the last one.
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def handle_char(char)
|
28
|
+
# last character was a backslash:
|
29
|
+
if @escaped
|
30
|
+
close_escape char
|
31
|
+
|
32
|
+
# char is a backslash or a quote:
|
33
|
+
elsif char.match?(/["\\]/)
|
34
|
+
handle_special char
|
35
|
+
|
36
|
+
# Character isn't whitespace, or it's inside double quotes:
|
37
|
+
elsif @in_quotes || char.match(/\S/)
|
38
|
+
@word << char
|
39
|
+
|
40
|
+
# Character is whitespace outside of double quotes:
|
41
|
+
else
|
42
|
+
add_word
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_word
|
47
|
+
return if @word.empty?
|
48
|
+
|
49
|
+
@words << @word
|
50
|
+
@word = ''
|
51
|
+
end
|
52
|
+
|
53
|
+
def handle_special(char)
|
54
|
+
if char == '\\'
|
55
|
+
@escaped = true
|
56
|
+
elsif char == '"'
|
57
|
+
@in_quotes = !@in_quotes
|
58
|
+
@word << char
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def close_escape(char)
|
63
|
+
@word << char
|
64
|
+
@escaped = false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -12,13 +12,6 @@ module PictureTag
|
|
12
12
|
@content[key]
|
13
13
|
end
|
14
14
|
|
15
|
-
# Returns the set of widths to use for a given media query.
|
16
|
-
def widths(media)
|
17
|
-
width_hash = self['media_widths'] || {}
|
18
|
-
width_hash.default = self['widths']
|
19
|
-
width_hash[media]
|
20
|
-
end
|
21
|
-
|
22
15
|
def formats
|
23
16
|
@content['formats']
|
24
17
|
end
|
@@ -41,15 +34,38 @@ module PictureTag
|
|
41
34
|
end
|
42
35
|
end
|
43
36
|
|
37
|
+
# Image widths to generate for a given media query.
|
38
|
+
def widths(media = nil)
|
39
|
+
setting_lookup('widths', 'media', media)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Image quality setting, possibly dependent on format.
|
44
43
|
def quality(format = nil)
|
45
|
-
|
46
|
-
|
44
|
+
setting_lookup('quality', 'format', format)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Gravity setting (for imagemagick cropping)
|
48
|
+
def gravity(media = nil)
|
49
|
+
setting_lookup('gravity', 'media', media)
|
50
|
+
end
|
47
51
|
|
48
|
-
|
52
|
+
# Crop value
|
53
|
+
def crop(media = nil)
|
54
|
+
setting_lookup('crop', 'media', media)
|
49
55
|
end
|
50
56
|
|
51
57
|
private
|
52
58
|
|
59
|
+
# Return arbitrary setting values, taking their defaults into account.
|
60
|
+
# Ex: quality can be set for all image formats, or individually per
|
61
|
+
# format. Per-format settings should override the general setting.
|
62
|
+
def setting_lookup(setting, prefix, lookup)
|
63
|
+
media_values = @content[prefix + '_' + setting] || {}
|
64
|
+
media_values.default = @content[setting]
|
65
|
+
|
66
|
+
media_values[lookup]
|
67
|
+
end
|
68
|
+
|
53
69
|
def build_preset
|
54
70
|
# The _data/picture.yml file is optional.
|
55
71
|
picture_data_file = grab_data_file
|
@@ -70,10 +86,14 @@ module PictureTag
|
|
70
86
|
end
|
71
87
|
|
72
88
|
def no_preset
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
89
|
+
unless @name == 'default'
|
90
|
+
Utils.warning(
|
91
|
+
<<~HEREDOC
|
92
|
+
Preset "#{@name}" not found in {PictureTag.config['data_dir']}/picture.yml
|
93
|
+
under markup_presets key. Using default values."
|
94
|
+
HEREDOC
|
95
|
+
)
|
96
|
+
end
|
77
97
|
|
78
98
|
{}
|
79
99
|
end
|