jekyll_picture_tag 1.9.0 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/.travis.yml +4 -7
  4. data/Dockerfile +9 -0
  5. data/docs/Gemfile +4 -2
  6. data/docs/Gemfile.lock +186 -89
  7. data/docs/_config.yml +6 -10
  8. data/docs/devs/contributing/code.md +44 -0
  9. data/docs/devs/contributing/docs.md +13 -0
  10. data/docs/devs/contributing/index.md +15 -0
  11. data/docs/devs/contributing/setup.md +13 -0
  12. data/docs/devs/contributing/testing.md +41 -0
  13. data/docs/devs/index.md +7 -0
  14. data/docs/{releases.md → devs/releases.md} +35 -13
  15. data/docs/index.md +58 -36
  16. data/docs/users/configuration/cdn.md +35 -0
  17. data/docs/users/configuration/directories.md +34 -0
  18. data/docs/users/configuration/disable.md +24 -0
  19. data/docs/users/configuration/fast_build.md +28 -0
  20. data/docs/users/configuration/ignore_missing.md +23 -0
  21. data/docs/users/configuration/index.md +29 -0
  22. data/docs/users/configuration/kramdown_fix.md +20 -0
  23. data/docs/users/configuration/relative_urls.md +15 -0
  24. data/docs/users/configuration/suppress_warnings.md +16 -0
  25. data/docs/users/index.md +7 -0
  26. data/docs/users/installation.md +52 -0
  27. data/docs/users/liquid_tag/argument_reference/alternate_images.md +18 -0
  28. data/docs/users/liquid_tag/argument_reference/attributes.md +42 -0
  29. data/docs/users/liquid_tag/argument_reference/base_image.md +12 -0
  30. data/docs/users/liquid_tag/argument_reference/crop.md +48 -0
  31. data/docs/users/liquid_tag/argument_reference/link.md +16 -0
  32. data/docs/users/liquid_tag/argument_reference/preset.md +17 -0
  33. data/docs/users/liquid_tag/argument_reference/readme.md +9 -0
  34. data/docs/users/liquid_tag/examples.md +81 -0
  35. data/docs/users/liquid_tag/index.md +31 -0
  36. data/docs/users/notes/git_lfs.md +7 -0
  37. data/docs/users/notes/github_pages.md +5 -0
  38. data/docs/users/notes/html_attributes.md +5 -0
  39. data/docs/users/notes/index.md +6 -0
  40. data/docs/users/notes/input_checking.md +6 -0
  41. data/docs/users/notes/kramdown_bug.md +41 -0
  42. data/docs/users/notes/managing_images.md +21 -0
  43. data/docs/{migration.md → users/notes/migration.md} +0 -0
  44. data/docs/users/presets/cropping.md +61 -0
  45. data/docs/users/presets/default.md +23 -0
  46. data/docs/users/presets/examples.md +79 -0
  47. data/docs/users/presets/fallback_image.md +28 -0
  48. data/docs/users/presets/html_attributes.md +26 -0
  49. data/docs/users/presets/image_formats.md +21 -0
  50. data/docs/users/presets/image_quality.md +43 -0
  51. data/docs/users/presets/index.md +101 -0
  52. data/docs/users/presets/link_source.md +16 -0
  53. data/docs/users/presets/markup_formats/fragments.md +48 -0
  54. data/docs/users/presets/markup_formats/javascript_friendly.md +57 -0
  55. data/docs/users/presets/markup_formats/readme.md +43 -0
  56. data/docs/users/presets/markup_formats/standard_html.md +25 -0
  57. data/docs/users/presets/media_queries.md +36 -0
  58. data/docs/users/presets/nomarkdown_override.md +17 -0
  59. data/docs/users/presets/pixel_ratio_srcsets.md +32 -0
  60. data/docs/users/presets/width_height_attributes.md +34 -0
  61. data/docs/users/presets/width_srcsets.md +85 -0
  62. data/jekyll_picture_tag.gemspec +1 -1
  63. data/lib/jekyll_picture_tag.rb +1 -0
  64. data/lib/jekyll_picture_tag/cache.rb +3 -0
  65. data/lib/jekyll_picture_tag/cache/base.rb +59 -0
  66. data/lib/jekyll_picture_tag/cache/generated.rb +20 -0
  67. data/lib/jekyll_picture_tag/cache/source.rb +19 -0
  68. data/lib/jekyll_picture_tag/defaults/presets.yml +2 -0
  69. data/lib/jekyll_picture_tag/generated_image.rb +52 -41
  70. data/lib/jekyll_picture_tag/img_uri.rb +1 -0
  71. data/lib/jekyll_picture_tag/instructions.rb +1 -0
  72. data/lib/jekyll_picture_tag/instructions/arg_splitter.rb +69 -0
  73. data/lib/jekyll_picture_tag/instructions/configuration.rb +1 -1
  74. data/lib/jekyll_picture_tag/instructions/preset.rb +40 -15
  75. data/lib/jekyll_picture_tag/instructions/set.rb +23 -9
  76. data/lib/jekyll_picture_tag/instructions/tag_parser.rb +59 -69
  77. data/lib/jekyll_picture_tag/output_formats/basic.rb +34 -15
  78. data/lib/jekyll_picture_tag/output_formats/img.rb +11 -0
  79. data/lib/jekyll_picture_tag/output_formats/picture.rb +22 -0
  80. data/lib/jekyll_picture_tag/router.rb +9 -0
  81. data/lib/jekyll_picture_tag/source_image.rb +60 -44
  82. data/lib/jekyll_picture_tag/srcsets/basic.rb +29 -7
  83. data/lib/jekyll_picture_tag/utils.rb +18 -0
  84. data/lib/jekyll_picture_tag/version.rb +1 -1
  85. data/readme.md +40 -16
  86. metadata +67 -20
  87. data/docs/_layouts/directory.html +0 -32
  88. data/docs/assets/style.css +0 -31
  89. data/docs/contributing.md +0 -75
  90. data/docs/example_presets.md +0 -116
  91. data/docs/global_configuration.md +0 -173
  92. data/docs/installation.md +0 -30
  93. data/docs/notes.md +0 -91
  94. data/docs/output.md +0 -63
  95. data/docs/presets.md +0 -309
  96. data/docs/usage.md +0 -113
@@ -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
@@ -71,22 +71,30 @@ module PictureTag
71
71
 
72
72
  # GeneratedImage class, not HTML
73
73
  def build_fallback_image
74
- fallback = GeneratedImage.new(
74
+ return fallback_candidate if fallback_candidate.exists?
75
+
76
+ image = GeneratedImage.new(
75
77
  source_file: PictureTag.source_images.first,
76
78
  format: PictureTag.fallback_format,
77
- width: PictureTag.fallback_width
79
+ width: checked_fallback_width,
80
+ crop: PictureTag.crop,
81
+ gravity: PictureTag.gravity
78
82
  )
79
83
 
80
- return fallback if fallback.exists?
84
+ image.generate
81
85
 
82
- build_new_fallback_image
86
+ image
83
87
  end
84
88
 
85
- def build_new_fallback_image
86
- GeneratedImage.new(
89
+ # It's only a candidate, because we don't know if the fallback width
90
+ # setting is larger than the source file.
91
+ def fallback_candidate
92
+ @fallback_candidate ||= GeneratedImage.new(
87
93
  source_file: PictureTag.source_images.first,
88
94
  format: PictureTag.fallback_format,
89
- width: checked_fallback_width
95
+ width: PictureTag.fallback_width,
96
+ crop: PictureTag.crop,
97
+ gravity: PictureTag.gravity
90
98
  )
91
99
  end
92
100
 
@@ -107,15 +115,26 @@ module PictureTag
107
115
  content.add_parent anchor
108
116
  end
109
117
 
118
+ def source
119
+ PictureTag.source_images.first
120
+ end
121
+
122
+ def source_width
123
+ if PictureTag.crop
124
+ fallback_candidate.source_width
125
+ else
126
+ source.width
127
+ end
128
+ end
129
+
110
130
  def checked_fallback_width
111
- source = PictureTag.source_images.first
112
131
  target = PictureTag.fallback_width
113
132
 
114
- if target > source.width
133
+ if target > source_width
115
134
  Utils.warning "#{source.shortname} is smaller than the " \
116
- "requested fallback width of #{target}px. Using #{source.width}" \
135
+ "requested fallback width of #{target}px. Using #{source_width}" \
117
136
  ' px instead.'
118
- source.width
137
+ source_width
119
138
  else
120
139
  target
121
140
  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
@@ -47,6 +48,14 @@ module PictureTag
47
48
  @instructions.source_images
48
49
  end
49
50
 
51
+ def crop(media = nil)
52
+ @instructions.crop(media)
53
+ end
54
+
55
+ def gravity(media = nil)
56
+ @instructions.gravity(media)
57
+ end
58
+
50
59
  # Config Forwarding
51
60
 
52
61
  def source_dir
@@ -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
@@ -65,26 +74,37 @@ module PictureTag
65
74
  end
66
75
 
67
76
  def checked_targets
68
- if target_files.any? { |f| f.width > @source_image.width }
77
+ if target_files.any? { |f| f.width > source_width }
69
78
 
70
79
  small_source_warn
71
80
 
72
- files = target_files.reject { |f| f.width >= @source_image.width }
73
- files.push(generate_file(@source_image.width))
81
+ files = target_files.reject { |f| f.width >= source_width }
82
+ files.push(generate_file(source_width))
74
83
  end
75
84
 
76
85
  files || target_files
77
86
  end
78
87
 
88
+ def source_width
89
+ @source_width ||= if PictureTag.crop(@media)
90
+ target_files.first.source_width
91
+ else
92
+ @source_image.width
93
+ end
94
+ end
95
+
79
96
  def target_files
80
97
  @target_files ||= widths.collect { |w| generate_file(w) }
81
98
  end
82
99
 
83
100
  def small_source_warn
84
101
  Utils.warning(
85
- " #{@source_image.shortname} is #{@source_image.width}px wide,"\
86
- " smaller than at least one size in the set #{widths}. Will not"\
87
- ' enlarge.'
102
+ <<~HEREDOC
103
+ #{@source_image.shortname}
104
+ is #{source_width}px wide (after cropping, if applicable),
105
+ smaller than at least one size in the set #{widths}.
106
+ Will not enlarge.
107
+ HEREDOC
88
108
  )
89
109
  end
90
110
 
@@ -92,7 +112,9 @@ module PictureTag
92
112
  GeneratedImage.new(
93
113
  source_file: @source_image,
94
114
  width: width,
95
- format: @input_format
115
+ format: @input_format,
116
+ crop: PictureTag.crop(@media),
117
+ gravity: PictureTag.gravity(@media)
96
118
  )
97
119
  end
98
120
  end
@@ -2,6 +2,24 @@ module PictureTag
2
2
  # This is a little module to hold logic that doesn't fit other places. If it
3
3
  # starts getting big, refactor.
4
4
  module Utils
5
+ # These are valid ImageMagick gravity arguments (relevant to our use
6
+ # case):
7
+ GRAVITIES =
8
+ %w[center
9
+ north
10
+ northeast
11
+ east
12
+ southeast
13
+ south
14
+ southwest
15
+ west
16
+ northwest].freeze
17
+
18
+ # This is an attempt to recognize valid imagemagick geometry arguments
19
+ # with regex. It only tries to match values which don't preserve aspect
20
+ # ratio, as they're the ones people might actually need here.
21
+ GEOMETRY_REGEX = /\A\d*%?[x:]?\d*[%!]?([+-]\d+){,2}\Z/i.freeze
22
+
5
23
  class << self
6
24
  # Configure Jekyll to keep our generated files
7
25
  def keep_files
@@ -1,3 +1,3 @@
1
1
  module PictureTag
2
- VERSION = '1.9.0'.freeze
2
+ VERSION = '1.12.0'.freeze
3
3
  end
data/readme.md CHANGED
@@ -1,21 +1,20 @@
1
1
  # Jekyll Picture Tag
2
2
 
3
- **Easy responsive images for Jekyll.**
3
+ **Responsive Images done correctly.**
4
4
 
5
- It's easy to throw an image on a webpage and call it a day. Doing justice to your users by serving
6
- it efficiently on all browsers and screen sizes is tedious and tricky. Tedious, tricky things should be
7
- automated.
5
+ It's simple to throw a photo on a page and call it a day, but doing justice to users on all
6
+ different browsers and devices is tedious and tricky. Tedious, tricky things should be automated.
8
7
 
9
- Jekyll Picture Tag adds responsive images to your [Jekyll](http://jekyllrb.com) static site. It
10
- automatically creates resized, reformatted source images, is fully configurable, implements sensible
11
- defaults, and solves both the art direction and resolution switching problems with a little YAML
12
- configuration and a simple template tag.
8
+ Jekyll Picture Tag automatically builds cropped, resized, and reformatted images, builds several
9
+ kinds of markup, offers extensive configuration while requiring none, and solves both the art
10
+ direction and resolution switching problems with a little YAML configuration and a simple template
11
+ tag.
13
12
 
14
13
  ## Why use Responsive Images?
15
14
 
16
- **Performance:** The fastest sites are static sites, but when you plonk a 2mb picture of your dog at
17
- the top of a blog post you're throwing it all away. Responsive images allow you to keep your site
18
- fast, without compromising image quality.
15
+ **Performance:** The fastest sites are static sites, but if you plonk a 2mb picture of your dog at
16
+ the top of a blog post you throw it all away. Responsive images allow you to keep your site fast,
17
+ without compromising image quality.
19
18
 
20
19
  **Design:** Your desktop image may not work well on mobile, regardless of its resolution. We often
21
20
  want to do more than just resize images for different screen sizes, we want to crop them or use a
@@ -24,15 +23,40 @@ different image entirely.
24
23
  ## Why use Jekyll Picture Tag?
25
24
 
26
25
  **Developer Sanity:** If you want to serve multiple images in multiple formats and resolutions, you
27
- have a litany of markup to write and a big pile of images to generate. Jekyll Picture Tag is your
28
- responsive images minion - give it simple instructions and it'll handle the rest.
26
+ have a litany of markup to write and a big pile of images to generate and organize. Jekyll Picture
27
+ Tag is your responsive images minion - give it simple instructions and it'll handle the rest.
29
28
 
30
29
  ## Features
31
30
 
32
- * Automatic generation and organization of resized image files in basically any format(s).
33
- * Automatic generation of complex markup in several different formats.
34
- * No configuration required, extensive configuration available.
31
+ * Generate piles of cropped, resized, and converted image files.
32
+ * Generate corresponding markup in several different formats.
33
+ * Configure it easily, or not at all.
34
+ * Make Lighthouse happy.
35
35
 
36
36
  ## Documentation:
37
37
 
38
38
  https://rbuchberger.github.io/jekyll_picture_tag/
39
+
40
+ ## Changelog:
41
+
42
+ https://rbuchberger.github.io/jekyll_picture_tag/releases
43
+
44
+ Latest versions:
45
+
46
+ * 1.10.1 July 2, 2020
47
+ * Bugfix for erroneously regenerated images
48
+ * 1.10.2 July 6, 2020
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.
55
+
56
+ ## Help Wanted
57
+
58
+ Writing code is only part of the job; often the harder part is knowing what needs to be changed. Any
59
+ and all feedback is greatly appreciated, especially in regards to documentation. What are your pain
60
+ points? See the [contributing
61
+ guidelines](https://rbuchberger.github.io/jekyll_picture_tag/contributing), or the
62
+ [issues](https://github.com/rbuchberger/jekyll_picture_tag/issues) page for more.