jekyll_picture_tag 1.10.2 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae8370284d1375ba3f33a17cce5aae2b5aa4a5d51dcc23623ec9e7560b7f789a
4
- data.tar.gz: 9f0c81c49fcbb700e36cc7e3b3152c8a35857cee2121de3ccfac98e8cf4eaa7c
3
+ metadata.gz: 1fdaad2f56cc244f8bce4ffcf5689630fe71ec0b7e091d91e289a1dc7d6740d8
4
+ data.tar.gz: 971d590896ef6396a86c8ce6f6dd7fef46804443b4c2b76226e5e774dc5cd204
5
5
  SHA512:
6
- metadata.gz: 76b323add40a8ba56004f7e51371baa5a7b1c5c611cdca526ed0757fb7b746714674ad03f3d598cb5c4532bbbdcebc08a6ae7e531fcfdf52917c5d12d70574d9
7
- data.tar.gz: 5911cd5b66f9638809ff7296a9a54fad639e5b87a68c892eaed47b54fa1f615eaa2afc15a2266ce59bbd1c05779ef56d0d5e7eb00bcf6e8469673e52ea250542
6
+ metadata.gz: 77fa8b7e4b3db8839a988f0613b8e3b0257ff92f7e379db12bf9de1da3644acf7d6a6bfa0fb82b900fb71d2e6775d842d2187ec1e13617cf268098f98ad41e12
7
+ data.tar.gz: b9efeae7cff9bc5a962d9c076d735c00011ad231cbac264b232a29a6f4ec3af101d9f905a0cde61283b0b739b8d9388e92ccad7f600aacccc382c2845edbd71e
@@ -1,2 +1,6 @@
1
1
  Style/FrozenStringLiteralComment:
2
2
  Enabled: false
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+
@@ -35,7 +35,7 @@ markup_presets:
35
35
  default:
36
36
  formats: [webp, original]
37
37
  ```
38
-
38
+ **Note:** Order matters! `[webp, jpg]` will offer the browser webp-images first. The browser will pick the *first* format it can work with. So if the order is reversed `[jpg, webp]` it will use the jpg image before the webp.
39
39
 
40
40
  ### Here's a more complete demonstration:
41
41
 
@@ -295,7 +295,37 @@ width (thumbnails and icons). To use a multiplier-based srcset, set `pixel_ratio
295
295
  If you don't give a setting for a particular format it'll fall back to the `quality` setting
296
296
  above, and if you don't set _that_ it'll default to 75.
297
297
 
298
- * **HTML Attributes**
298
+ * **Width & Height attributes (Anti-Loading-Jank)**
299
+
300
+ _Format:_
301
+
302
+ ```yml
303
+ dimension_attributes: true | false
304
+ ```
305
+
306
+ _Example:_
307
+
308
+ ```yml
309
+ dimension_attributes: true
310
+ ```
311
+
312
+ _Default:_ `false`
313
+
314
+ Prevent page reflow (aka jank) while images are loading, by adding `width` and `height` attributes
315
+ to the `<img>` tag in the correct aspect ratio.
316
+
317
+ For an explanation of why and how you want to do this, [here](https://youtu.be/4-d_SoCHeWE) is a
318
+ great explanation.
319
+
320
+ Caveats:
321
+ * You need `width: 100%;` and `height: auto;` (or vice versa) set in CSS on the `<img>`
322
+ tags, or they will be stretched weirdly.
323
+ * This works on `<img>` tags and `<picture>` tags when offering images in multiple widths and
324
+ formats, but it does not work if you are using art direction (in other words, if you have
325
+ multiple source images). This is because these attributes can only be applied to the `<img>`
326
+ tag, of which there is exactly one.
327
+
328
+ * **Arbitrary HTML Attributes**
299
329
 
300
330
  _Format:_
301
331
 
@@ -1,6 +1,11 @@
1
1
  ---
2
2
  ---
3
3
  # Release History
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.
4
9
  * 1.10.2 July 6, 2020
5
10
  * Bugfix for fallback image files not actually getting generated
6
11
  * 1.10.1 July 2, 2020
@@ -3,6 +3,8 @@
3
3
 
4
4
  # How to use the Liquid Tag:
5
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
+
6
8
  ## Format:
7
9
 
8
10
  {% raw %}
@@ -12,11 +14,22 @@
12
14
  The only required argument is the base image. Line breaks and extra spaces are fine, and you can
13
15
  use liquid variables anywhere.
14
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
+
15
26
  ## Examples:
16
27
 
17
28
  {% raw %}
18
29
  `{% picture example.jpg %}`
19
30
 
31
+ `{% picture {{ page.example_liquid_picture_var }} %}`
32
+
20
33
  `{% picture thumbnail example.jpg 1:1 --alt Example Image %}`
21
34
 
22
35
  `{% picture example.jpg 16:9 north --picture class="attribute-demo" %}`
@@ -47,7 +60,7 @@ Given in order:
47
60
 
48
61
  Select a [markup preset]({{ site.baseurl }}/presets#markup-presets), or omit to use the `default` preset. Presets
49
62
  are collections of settings that determine nearly everything about JPT's output, from the image
50
- formats used to the exact format your markup will take.
63
+ formats used to the exact format your final HTML-markup will take.
51
64
 
52
65
  - **Base Image** (Required)
53
66
 
@@ -141,3 +154,4 @@ Given in order:
141
154
 
142
155
  `--parent` will be applied to the `<picture>` if present, otherwise the `<img>`; useful when
143
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).
@@ -38,7 +38,6 @@ 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'
42
41
  spec.add_dependency 'mime-types', '~> 3'
43
42
  spec.add_dependency 'mini_magick', '~> 4'
44
43
  spec.add_dependency 'objective_elements', '~> 1.1.2'
@@ -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
@@ -0,0 +1,3 @@
1
+ require_relative 'cache/base'
2
+ require_relative 'cache/source'
3
+ require_relative 'cache/generated'
@@ -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
@@ -8,3 +8,4 @@ link_source: false
8
8
  quality: 75
9
9
  data_sizes: true
10
10
  gravity: center
11
+ dimension_attributes: false
@@ -1,10 +1,10 @@
1
1
  require 'mini_magick'
2
- require 'base32'
3
2
 
4
3
  module PictureTag
5
4
  # Represents a generated image file.
6
5
  class GeneratedImage
7
6
  attr_reader :width, :format
7
+
8
8
  include MiniMagick
9
9
 
10
10
  def initialize(source_file:, width:, format:, crop: nil, gravity: '')
@@ -32,7 +32,7 @@ module PictureTag
32
32
  # /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
33
33
  # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
34
34
  def name
35
- name_left + digest + name_right
35
+ @name ||= "#{@source.base_name}-#{@width}-#{id}.#{@format}"
36
36
  end
37
37
 
38
38
  # https://example.com/assets/images/myimage-100-123abc.jpg
@@ -41,75 +41,47 @@ module PictureTag
41
41
  ImgURI.new(name).to_s
42
42
  end
43
43
 
44
- def cropped_source_width
45
- image.width
46
- end
47
-
48
- private
44
+ # Post crop
45
+ def source_width
46
+ update_cache unless cache[:width]
49
47
 
50
- # /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
51
- # ^^^^^^^^^^^^^^^^^^^^^^^
52
- def name_left
53
- "#{@source.base_name}-#{@width}-"
48
+ cache[:width]
54
49
  end
55
50
 
56
- # /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
57
- # ^^^^^^
58
- def digest
59
- guess_digest if !@source.digest_guess && PictureTag.fast_build?
60
-
61
- @source.digest
62
- end
51
+ # Post crop
52
+ def source_height
53
+ update_cache unless cache[:height]
63
54
 
64
- # /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
65
- # ^^^^
66
- def name_right
67
- crop_code + '.' + @format
55
+ cache[:height]
68
56
  end
69
57
 
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
58
+ private
76
59
 
77
- Base32.encode(
78
- Digest::MD5.hexdigest(@crop + @gravity)
79
- )[0..2]
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}")
80
63
  end
81
64
 
82
- # Used for the fast build option: look for a file which matches everything
83
- # we know about the destination file without calculating a digest on the
84
- # source file, and if it exists we assume it's the right one.
85
- def guess_digest
86
- matches = dest_glob
87
- return unless matches.length == 1
65
+ def update_cache
66
+ return if @source.missing
88
67
 
89
- # Start and finish of the destination image's hash value
90
- finish = -name_right.length
91
- start = finish - 6
68
+ # Ensure it's generated:
69
+ image
92
70
 
93
- # The source image keeps track of this guess, so we hand it off:
94
- @source.digest_guess = matches.first[start...finish]
95
- end
71
+ cache[:width] = @source_dimensions[:width]
72
+ cache[:height] = @source_dimensions[:height]
96
73
 
97
- # Returns a list of images which are probably correct.
98
- def dest_glob
99
- query = File.join(
100
- PictureTag.dest_dir,
101
- name_left + '?' * 6 + name_right
102
- )
103
-
104
- Dir.glob query
74
+ cache.write
105
75
  end
106
76
 
107
- def generate_image
108
- puts 'Generating new image file: ' + name
109
- process_image
110
- write_image
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]
111
82
  end
112
83
 
84
+ # Post crop, before resizing and reformatting
113
85
  def image
114
86
  @image ||= open_image
115
87
  end
@@ -124,9 +96,21 @@ module PictureTag
124
96
  end
125
97
  end
126
98
 
99
+ @source_dimensions = { width: image_base.width, height: image_base.height }
100
+
127
101
  image_base
128
102
  end
129
103
 
104
+ def generate_image
105
+ puts 'Generating new image file: ' + name
106
+ process_image
107
+ write_image
108
+ end
109
+
110
+ def quality
111
+ PictureTag.quality(@format)
112
+ end
113
+
130
114
  def process_image
131
115
  image.combine_options do |i|
132
116
  i.resize "#{@width}x"
@@ -134,22 +118,17 @@ module PictureTag
134
118
  end
135
119
 
136
120
  image.format @format
137
- image.quality PictureTag.quality(@format)
121
+ image.quality quality
138
122
  end
139
123
 
140
124
  def write_image
141
- # Make sure destination directory exists:
142
- FileUtils.mkdir_p(dest_dir) unless Dir.exist?(dest_dir)
125
+ FileUtils.mkdir_p(File.dirname(absolute_filename))
143
126
 
144
127
  image.write absolute_filename
145
128
 
146
129
  FileUtils.chmod(0o644, absolute_filename)
147
130
  end
148
131
 
149
- def dest_dir
150
- File.dirname absolute_filename
151
- end
152
-
153
132
  def process_format(format)
154
133
  if format.casecmp('original').zero?
155
134
  @source.ext
@@ -8,6 +8,7 @@ module PictureTag
8
8
  # image. Call to_s on it to get the link.
9
9
  class ImgURI
10
10
  attr_reader :filename, :source_image
11
+
11
12
  def initialize(filename, source_image: false)
12
13
  @source_image = source_image
13
14
  @filename = filename
@@ -51,9 +51,10 @@ module PictureTag
51
51
  end
52
52
 
53
53
  def handle_special(char)
54
- if char == '\\'
54
+ case char
55
+ when '\\'
55
56
  @escaped = true
56
- elsif char == '"'
57
+ when '"'
57
58
  @in_quotes = !@in_quotes
58
59
  @word << char
59
60
  end
@@ -3,6 +3,7 @@ module PictureTag
3
3
  # Handles the specific tag image set to construct.
4
4
  class Preset
5
5
  attr_reader :name
6
+
6
7
  def initialize(name)
7
8
  @name = name
8
9
  @content = build_preset
@@ -6,6 +6,10 @@ module PictureTag
6
6
  class Basic
7
7
  include ObjectiveElements
8
8
 
9
+ def to_s
10
+ wrap(base_markup).to_s
11
+ end
12
+
9
13
  # Used for both the fallback image, and for the complete markup.
10
14
  def build_base_img
11
15
  img = SingleTag.new 'img'
@@ -23,10 +27,6 @@ module PictureTag
23
27
  img
24
28
  end
25
29
 
26
- def to_s
27
- wrap(base_markup).to_s
28
- end
29
-
30
30
  private
31
31
 
32
32
  # Handles various wrappers around basic markup
@@ -86,6 +86,8 @@ module PictureTag
86
86
  image
87
87
  end
88
88
 
89
+ # It's only a candidate, because we don't know if the fallback width
90
+ # setting is larger than the source file.
89
91
  def fallback_candidate
90
92
  @fallback_candidate ||= GeneratedImage.new(
91
93
  source_file: PictureTag.source_images.first,
@@ -119,7 +121,7 @@ module PictureTag
119
121
 
120
122
  def source_width
121
123
  if PictureTag.crop
122
- fallback_candidate.cropped_source_width
124
+ fallback_candidate.source_width
123
125
  else
124
126
  source.width
125
127
  end
@@ -3,6 +3,8 @@ module PictureTag
3
3
  # Represents a bare <img> tag with a srcset attribute.
4
4
  # Used when <picture> is unnecessary.
5
5
  class Img < Basic
6
+ private
7
+
6
8
  def srcset
7
9
  @srcset ||= build_srcset(
8
10
  PictureTag.source_images.first, PictureTag.formats.first
@@ -17,8 +19,17 @@ module PictureTag
17
19
 
18
20
  img.attributes << PictureTag.html_attributes['parent']
19
21
 
22
+ add_dimensions(img, srcset)
23
+
20
24
  img
21
25
  end
26
+
27
+ def add_dimensions(img, srcset)
28
+ return unless PictureTag.preset['dimension_attributes']
29
+
30
+ img.width = srcset.width_attribute
31
+ img.height = srcset.height_attribute
32
+ end
22
33
  end
23
34
  end
24
35
  end
@@ -3,7 +3,13 @@ module PictureTag
3
3
  # Represents a <picture> tag, enclosing at least 2 <source> tags and an
4
4
  # <img> tag.
5
5
  class Picture < Basic
6
+ private
7
+
6
8
  def srcsets
9
+ @srcsets ||= build_srcsets
10
+ end
11
+
12
+ def build_srcsets
7
13
  formats = PictureTag.formats
8
14
  # Source images are provided in reverse order and must be flipped:
9
15
  images = PictureTag.source_images.reverse
@@ -37,6 +43,22 @@ module PictureTag
37
43
  source
38
44
  end
39
45
 
46
+ def build_base_img
47
+ img = super
48
+
49
+ # Only add source dimensions if there is a single source image.
50
+ # Currently you can't use both art direction and intrinsic image sizes.
51
+ if PictureTag.preset['dimension_attributes'] &&
52
+ PictureTag.source_images.length == 1
53
+ img.attributes << {
54
+ width: srcsets.first.width_attribute,
55
+ height: srcsets.first.height_attribute
56
+ }
57
+ end
58
+
59
+ img
60
+ end
61
+
40
62
  def base_markup
41
63
  picture = DoubleTag.new(
42
64
  'picture',
@@ -9,6 +9,7 @@ module PictureTag
9
9
  # gets big, I'll refactor.
10
10
  module Router
11
11
  attr_accessor :instructions, :context
12
+
12
13
  # Context forwarding
13
14
 
14
15
  # Global site data
@@ -3,84 +3,100 @@ module PictureTag
3
3
  # advantage by storing expensive file reads and writes in instance variables,
4
4
  # to be reused by many different generated images.
5
5
  class SourceImage
6
- attr_reader :name, :shortname, :missing, :media_preset
7
- attr_accessor :digest_guess
6
+ attr_reader :shortname, :missing, :media_preset
7
+
8
8
  include MiniMagick
9
9
 
10
10
  def initialize(relative_filename, media_preset = nil)
11
+ # /home/dave/my_blog/assets/images/somefolder/myimage.jpg
12
+ # ^^^^^^^^^^^^^^^^^^^^^^
11
13
  @shortname = relative_filename
12
- @name = grab_file relative_filename
13
14
  @media_preset = media_preset
15
+
16
+ @missing = missing?
17
+ check_cache
18
+ end
19
+
20
+ def digest
21
+ @digest ||= cache[:digest] || ''
14
22
  end
15
23
 
16
24
  def width
17
- @width ||= if @missing
18
- 999_999
19
- else
20
- image.width
21
- end
25
+ @width ||= cache[:width] || 999_999
22
26
  end
23
27
 
24
- def digest
25
- @digest ||= if @missing
26
- 'x' * 6
27
- elsif PictureTag.fast_build? && @digest_guess
28
- # Digest guess will be handed off to this class by the first
29
- # generated image which needs it (via attr_accessor).
30
- @digest_guess
31
- else
32
- Digest::MD5.hexdigest(File.read(@name))[0..5]
33
- end
28
+ def height
29
+ @height ||= cache[:height] || 999_999
34
30
  end
35
31
 
36
- # Includes path relative to default source folder and the original filename.
32
+ # /home/dave/my_blog/assets/images/somefolder/myimage.jpg
33
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
34
+ def name
35
+ @name ||= File.join(PictureTag.source_dir, @shortname)
36
+ end
37
+
38
+ # /home/dave/my_blog/assets/images/somefolder/myimage.jpg
39
+ # ^^^^^^^^^^^^^^^^^^
37
40
  def base_name
38
41
  @shortname.delete_suffix File.extname(@shortname)
39
42
  end
40
43
 
41
- # File exention
44
+ # /home/dave/my_blog/assets/images/somefolder/myimage.jpg
45
+ # ^^^
42
46
  def ext
43
- # [1..-1] will strip the leading period.
44
- @ext ||= File.extname(@name)[1..-1].downcase
47
+ @ext ||= File.extname(name)[1..-1].downcase
45
48
  end
46
49
 
47
50
  private
48
51
 
49
- def image
50
- @image ||= Image.open(@name)
52
+ def cache
53
+ @cache ||= Cache::Source.new(@shortname)
51
54
  end
52
55
 
53
- # Turn a relative filename into an absolute one, and make sure it exists.
54
- def grab_file(source_file)
55
- source_name = File.join(PictureTag.source_dir, source_file)
56
-
57
- if File.exist? source_name
58
- @missing = false
56
+ def missing?
57
+ if File.exist? name
58
+ false
59
59
 
60
60
  elsif PictureTag.continue_on_missing?
61
- @missing = true
62
- Utils.warning missing_image_warning(source_name)
61
+ Utils.warning(missing_image_warning)
62
+ true
63
63
 
64
64
  else
65
- raise ArgumentError, missing_image_error(source_name)
65
+ raise ArgumentError, missing_image_error
66
66
  end
67
+ end
67
68
 
68
- source_name
69
+ def check_cache
70
+ return if @missing
71
+ return if cache[:digest] && PictureTag.fast_build?
72
+
73
+ update_cache if source_digest != cache[:digest]
69
74
  end
70
75
 
71
- def missing_image_warning(source_name)
72
- <<~HEREDOC
73
- Could not find #{source_name}. Your site will have broken images in it.
74
- Continuing.
75
- HEREDOC
76
+ def update_cache
77
+ cache[:digest] = source_digest
78
+ cache[:width] = image.width
79
+ cache[:height] = image.height
80
+
81
+ cache.write
82
+ end
83
+
84
+ def image
85
+ @image ||= Image.open(name)
86
+ end
87
+
88
+ def source_digest
89
+ @source_digest ||= Digest::MD5.hexdigest(File.read(name))
90
+ end
91
+
92
+ def missing_image_warning
93
+ "JPT Could not find #{name}. Your site will have broken images. Continuing."
76
94
  end
77
95
 
78
- def missing_image_error(source_name)
96
+ def missing_image_error
79
97
  <<~HEREDOC
80
- Jekyll Picture Tag could not find #{source_name}. You can force the
81
- build to continue anyway by setting "picture: ignore_missing_images:
82
- true" in "_config.yml". This setting can also accept a jekyll build
83
- environment, or an array of environments.
98
+ Could not find #{name}. You can force the build to continue anyway by
99
+ setting "picture: ignore_missing_images: true" in "_config.yml".
84
100
  HEREDOC
85
101
  end
86
102
  end
@@ -23,6 +23,7 @@ module PictureTag
23
23
  @format ||= files.first.format
24
24
  end
25
25
 
26
+ # GeneratedImage class
26
27
  def files
27
28
  @files ||= build_files
28
29
  end
@@ -51,6 +52,14 @@ module PictureTag
51
52
  "(#{PictureTag.media_presets[@media]})"
52
53
  end
53
54
 
55
+ def width_attribute
56
+ files.first.source_width.to_s
57
+ end
58
+
59
+ def height_attribute
60
+ files.first.source_height.to_s
61
+ end
62
+
54
63
  private
55
64
 
56
65
  def build_files
@@ -78,7 +87,7 @@ module PictureTag
78
87
 
79
88
  def source_width
80
89
  @source_width ||= if PictureTag.crop(@media)
81
- target_files.first.cropped_source_width
90
+ target_files.first.source_width
82
91
  else
83
92
  @source_image.width
84
93
  end
@@ -1,3 +1,3 @@
1
1
  module PictureTag
2
- VERSION = '1.10.2'.freeze
2
+ VERSION = '1.11.0'.freeze
3
3
  end
data/readme.md CHANGED
@@ -43,14 +43,15 @@ https://rbuchberger.github.io/jekyll_picture_tag/releases
43
43
 
44
44
  Latest versions:
45
45
 
46
- * 1.10.0 May 11, 2020
47
- * **Image Cropping support!** access the power of ImageMagick's `crop` function.
48
- * Don't issue a warning when `default` preset is not found.
49
- * Documentation improvements
50
46
  * 1.10.1 July 2, 2020
51
47
  * Bugfix for erroneously regenerated images
52
48
  * 1.10.2 July 6, 2020
53
49
  * Bugfix for fallback image files not actually getting generated
50
+ * 1.11.0 July 27, 2020
51
+ * **Width and height attribute support!** Begone, page reflow.
52
+ * Cache image information between builds
53
+ * Change image naming format. This update will trigger all images to be regenerated, so you may
54
+ want to delete your generated images folder beforehand.
54
55
 
55
56
  ## Help Wanted
56
57
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll_picture_tag
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.2
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Wierzbowski
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-07-06 00:00:00.000000000 Z
13
+ date: 2020-07-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -152,20 +152,6 @@ dependencies:
152
152
  - - "~>"
153
153
  - !ruby/object:Gem::Version
154
154
  version: '2.6'
155
- - !ruby/object:Gem::Dependency
156
- name: base32
157
- requirement: !ruby/object:Gem::Requirement
158
- requirements:
159
- - - "~>"
160
- - !ruby/object:Gem::Version
161
- version: '0.3'
162
- type: :runtime
163
- prerelease: false
164
- version_requirements: !ruby/object:Gem::Requirement
165
- requirements:
166
- - - "~>"
167
- - !ruby/object:Gem::Version
168
- version: '0.3'
169
155
  - !ruby/object:Gem::Dependency
170
156
  name: mime-types
171
157
  requirement: !ruby/object:Gem::Requirement
@@ -263,6 +249,10 @@ files:
263
249
  - jekyll_picture_tag.gemspec
264
250
  - lib/jekyll-picture-tag.rb
265
251
  - lib/jekyll_picture_tag.rb
252
+ - lib/jekyll_picture_tag/cache.rb
253
+ - lib/jekyll_picture_tag/cache/base.rb
254
+ - lib/jekyll_picture_tag/cache/generated.rb
255
+ - lib/jekyll_picture_tag/cache/source.rb
266
256
  - lib/jekyll_picture_tag/defaults/global.yml
267
257
  - lib/jekyll_picture_tag/defaults/presets.yml
268
258
  - lib/jekyll_picture_tag/generated_image.rb