jekyll_picture_tag 1.10.2 → 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 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