jekyll-srcset-tag 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []