jekyll-srcset-tag 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 69a4d80e9d2114444f958b0372cd93b96d0af16d
4
+ data.tar.gz: a950dd78d6fce273fa55b21671e5ba888c66a5db
5
+ SHA512:
6
+ metadata.gz: a3768328c0f8bf41e00756dbb37d866527e0fbd6b099533f02cf3eb29ad2ef09caf502971069e640c11eb43c15ed634fa5d3ae6a67d06811d02f9d2f9ea9d636
7
+ data.tar.gz: ed06ca9b5025efe2ff0234641ddc2e05ff1d61f72fc89276909c02d2ea2cafda724de0bb9348faeeeca047ca019c87a173746622280775835fa514d582c13862
@@ -0,0 +1,72 @@
1
+ module Jekyll
2
+ module SrcsetTag
3
+ class Image::Instance
4
+
5
+ attr_reader :width, :height, :image, :extension, :image_width, :image_height, :output_width, :output_height,
6
+ :undersized
7
+
8
+ def initialize(width:, height:, extension:)
9
+ @width = width
10
+ @height = height
11
+ @extension = extension
12
+ end
13
+
14
+ def for_image!(image)
15
+ @image = image
16
+ @image_width = image[:width].to_i
17
+ @image_height = image[:height].to_i
18
+ calculate_output_dimensions!
19
+ end
20
+
21
+ def filename
22
+ output_width.to_s + 'x' + output_height.to_s + extension
23
+ end
24
+
25
+ def undersized?
26
+ @undersized
27
+ end
28
+
29
+ protected
30
+
31
+ # This is pretty much taken verbatim from https://github.com/robwierzbowski/jekyll-picture-tag
32
+ def calculate_output_dimensions!
33
+ @undersized = false
34
+ image_ratio = image_width.to_f / image_height.to_f
35
+
36
+ generated_width = if width
37
+ width.to_f
38
+ elsif height
39
+ image_ratio * height.to_f
40
+ else
41
+ image_width.to_f
42
+ end
43
+ generated_height = if height
44
+ height.to_f
45
+ elsif width
46
+ width.to_f / image_ratio
47
+ else
48
+ image_height
49
+ end
50
+
51
+ generated_ratio = generated_width / generated_height
52
+
53
+ if image_width < generated_width || image_height < generated_height
54
+ @undersized = true
55
+ generated_width = if image_ratio > generated_ratio
56
+ image_width
57
+ else
58
+ image_height / generated_ratio
59
+ end
60
+ generated_height = if image_ratio > generated_ratio
61
+ image_height
62
+ else
63
+ image_width / generated_ratio
64
+ end
65
+ end
66
+
67
+ @output_width = generated_width.round
68
+ @output_height = generated_height.round
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,20 @@
1
+ module Jekyll
2
+ module SrcsetTag
3
+ class Image::Source
4
+
5
+ attr_reader :media, :size, :width, :height
6
+
7
+ def initialize(width: nil, height: nil, media: nil, size: nil)
8
+ @media = media
9
+ @size = size
10
+ @width = width
11
+ @height = height
12
+ end
13
+
14
+ def size_string
15
+ (media ? media + ' ' : '') + (size || '100vw')
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,106 @@
1
+ require 'digest/sha1'
2
+ require 'cgi'
3
+ require 'mini_magick'
4
+ require_relative 'resizer'
5
+
6
+ module Jekyll
7
+ module SrcsetTag
8
+ class Image
9
+
10
+ attr_reader :source_path, :output_path, :web_output_path, :image_path, :ppi, :sources, :html_attributes
11
+
12
+ def initialize(source_path:, output_path:, web_output_path:, image_path:, ppi:, sources:, html_attributes:)
13
+ @source_path = source_path
14
+ @output_path = output_path
15
+ @web_output_path = web_output_path
16
+ @image_path = image_path
17
+ @ppi = ppi.respond_to?(:sort_by) ? ppi : ppi.split(',').map(&:to_i)
18
+ @sources = sources
19
+ @html_attributes = html_attributes
20
+ end
21
+
22
+ def generate_images!
23
+ unless images_exist?
24
+ original = MiniMagick::Image.open File.join(source_path, image_path)
25
+ instances.each { |instance| instance.for_image!(original) }
26
+ resize_images! uniq_instances, original
27
+ end
28
+ end
29
+
30
+ def images_exist?
31
+ Dir.exist? output_dir
32
+ end
33
+
34
+ def instances
35
+ @instances ||= create_instances
36
+ end
37
+
38
+ def uniq_instances
39
+ instances.uniq { |instance| [ instance.output_width, instance.output_height ] }
40
+ end
41
+
42
+ def to_html
43
+ srcs = image_srcs
44
+ src = CGI.escape_html(srcs.last[0])
45
+ srcset = CGI.escape_html(srcs.map {|path, size| "#{path} #{size}w" }.join(', '))
46
+ sizes = CGI::escape_html(source_sizes.join(', '))
47
+ "<img src=\"#{src}\" srcset=\"#{srcset}\" sizes=\"#{sizes}\" #{html_attributes}/>\n"
48
+ end
49
+
50
+ def digest
51
+ unless @digest
52
+ file = Digest::SHA1.file(File.join(source_path, image_path)).to_s
53
+ sizes = Digest::SHA1.hexdigest instances.map { |i| i.width.to_s + '-' + i.height.to_s }.join(' ')
54
+ @digest = Digest::SHA1.hexdigest file + sizes
55
+ end
56
+ @digest
57
+ end
58
+
59
+ def output_dir
60
+ File.join(output_path, image_path + '-' + digest.slice(0, 7))
61
+ end
62
+
63
+ def web_output_dir
64
+ File.join(web_output_path, image_path + '-' + digest.slice(0, 7))
65
+ end
66
+
67
+ protected
68
+
69
+ def create_instances
70
+ reverse_ppi = ppi.sort_by { |s| -s }
71
+ all_sources = sources.map do |source|
72
+ reverse_ppi.map { |ppi| [source, ppi] }
73
+ end.flatten(1)
74
+ all_sources.map do |(source, ppi)|
75
+ Instance.new width: (source.width ? (source.width.to_f * ppi).round : nil),
76
+ height: (source.height ? (source.height.to_f * ppi).round : nil),
77
+ extension: File.extname(image_path)
78
+ end
79
+ end
80
+
81
+ def image_srcs
82
+ uniq_instances.map do |instance|
83
+ [File.join(web_output_dir, instance.filename), instance.output_width.to_s]
84
+ end
85
+ end
86
+
87
+ def source_sizes
88
+ sources.map { |source| source.size_string }
89
+ end
90
+
91
+ def resize_images!(instances, original)
92
+ if instances.any? { |i| i.undersized }
93
+ warn "Warning:".yellow + " #{image_path} is smaller than the requested output file. " +
94
+ "It will be resized without upscaling."
95
+ end
96
+ instances.each do |instance|
97
+ Resizer::resize(original, output_dir, instance.filename, instance.output_width, instance.output_height)
98
+ puts "Generated #{File.join(output_dir, instance.filename)}"
99
+ end
100
+ end
101
+
102
+ end
103
+ end
104
+ end
105
+
106
+ require_relative 'image/instance'
@@ -0,0 +1,24 @@
1
+ require 'fileutils'
2
+
3
+ module Jekyll
4
+ module SrcsetTag
5
+ module Resizer
6
+ def self.resize(image, destination_dir, filename, width, height)
7
+ FileUtils.mkdir_p(destination_dir) unless File.exist?(destination_dir)
8
+ image.strip
9
+ if image['format'] == 'JPEG'
10
+ image.quality 80
11
+ image.depth 8
12
+ image.interlace "plane"
13
+ end
14
+ image.combine_options do |i|
15
+ i.resize "#{width}x#{height}^"
16
+ i.gravity "center"
17
+ i.crop "#{width}x#{height}+0+0"
18
+ end
19
+
20
+ image.write File.join(destination_dir, filename)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ require 'cgi'
2
+
3
+ module Jekyll
4
+ module SrcsetTag
5
+ class SrcsetSourceTag < Liquid::Tag
6
+
7
+ def initialize(tag_name, markup, tokens)
8
+ @markup = markup
9
+ super
10
+ end
11
+
12
+ def render(context)
13
+ render_markup = Liquid::Template.parse(@markup)
14
+ .render(context)
15
+ .gsub(/\\\{\\\{|\\\{\\%/, '\{\{' => '{{', '\{\%' => '{%')
16
+ hash = markup_to_hash(render_markup)
17
+ markup = hash.map { |(key, value)| "#{key}=\"#{CGI.escape_html(value)}\""}
18
+ '<source ' + markup.join(' ') + ' />'
19
+ end
20
+
21
+ def markup_to_hash(markup)
22
+ matches = markup.scan(markup_regex)
23
+ matches.each_with_object({}) do |match, memo|
24
+ key, value = match.split(':', 2)
25
+ memo[key.strip] = value.gsub(/\A("|')|("|')\Z/, '')
26
+ end
27
+ end
28
+
29
+ def markup_regex
30
+ %r{
31
+ \w+
32
+ \s*
33
+ \:
34
+ \s*
35
+ (?:
36
+ "(?:[^"\\]|\\.)*"
37
+ |
38
+ '(?:[^'\\]|\\.)*'
39
+ |
40
+ [^\s]+
41
+ )
42
+ }x
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,80 @@
1
+ require_relative 'image'
2
+ require_relative 'image/source'
3
+ require 'nokogiri'
4
+
5
+ module Jekyll
6
+ module SrcsetTag
7
+ class SrcsetTag < Liquid::Block
8
+
9
+ def initialize(tag_name, markup, tokens)
10
+ @markup = markup
11
+ super
12
+ end
13
+
14
+ def render(context)
15
+ render_markup = Liquid::Template.parse(@markup)
16
+ .render(context)
17
+ .gsub(/\\\{\\\{|\\\{\\%/, '\{\{' => '{{', '\{\%' => '{%')
18
+
19
+ # set keep files
20
+ settings = srcset_settings(context)
21
+ site = context.registers[:site]
22
+ site.config['keep_files'] << settings['output'] unless site.config['keep_files'].include?(settings['output'])
23
+
24
+ image = image File.join(site.source, settings['source']),
25
+ File.join(site.dest, settings['output']),
26
+ '/' + settings['output'],
27
+ render_markup,
28
+ sources(super)
29
+ image.generate_images!
30
+ image.to_html
31
+ end
32
+
33
+ def sources(content)
34
+ # return sources objects
35
+ html = Nokogiri::HTML(content)
36
+ html.css('source').map do |source|
37
+ Image::Source.new(width: source.attr('width'),
38
+ height: source.attr('height'),
39
+ media: source.attr('media'),
40
+ size: source.attr('size'))
41
+ end
42
+
43
+ end
44
+
45
+ def image(source_path, output_path, web_output_path, markup, sources)
46
+ markup = markup_regex.match(markup)
47
+ unless markup
48
+ raise "srcset tag doesn't look right - it should be {% srcset image_src [ppi:1,2] [html_attributes] %}"
49
+ end
50
+ Image.new(source_path: source_path,
51
+ output_path: output_path,
52
+ web_output_path: web_output_path,
53
+ image_path: markup[:image_src],
54
+ ppi: markup[:ppi] || 1,
55
+ sources: sources,
56
+ html_attributes: markup[:html_attr])
57
+ end
58
+
59
+ def markup_regex
60
+ %r{
61
+ ^
62
+ (?<image_src>[^\s]+\.[a-zA-Z0-9]{3,4})
63
+ \s*
64
+ (ppi:(?<ppi>(\d(\.\d\d?)?,)*\d(\.\d\d?)?))?
65
+ \s*
66
+ (?<html_attr>[\s\S]+)?
67
+ $
68
+ }x
69
+ end
70
+
71
+ def srcset_settings(context)
72
+ settings = context.registers[:site].config['srcset']
73
+ settings ||= {}
74
+ settings['source'] ||= '_assets/images/fullsize'
75
+ settings['output'] ||= 'images/generated'
76
+ settings
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,5 @@
1
+ module Jekyll
2
+ module SrcsetTag
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'jekyll/srcset_tag/srcset_tag'
2
+ require 'jekyll/srcset_tag/srcset_source_tag'
3
+
4
+ Liquid::Template.register_tag('srcset_source', Jekyll::SrcsetTag::SrcsetSourceTag)
5
+ Liquid::Template.register_tag('srcset', Jekyll::SrcsetTag::SrcsetTag)
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll-srcset-tag
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Dew
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Provides a block that can take a source image and generate it to all
14
+ sizes for srcset usage
15
+ email:
16
+ - kevindew@me.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/jekyll-srcset-tag.rb
22
+ - lib/jekyll/srcset_tag/image.rb
23
+ - lib/jekyll/srcset_tag/image/instance.rb
24
+ - lib/jekyll/srcset_tag/image/source.rb
25
+ - lib/jekyll/srcset_tag/resizer.rb
26
+ - lib/jekyll/srcset_tag/srcset_source_tag.rb
27
+ - lib/jekyll/srcset_tag/srcset_tag.rb
28
+ - lib/jekyll/srcset_tag/version.rb
29
+ homepage: https://github.com/kevindew/jekyll-srcset-tag
30
+ licenses:
31
+ - MIT
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 2.1.0
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 2.4.8
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: A Jekyll plugin to use img srcset-w images
53
+ test_files: []