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 +7 -0
- data/lib/jekyll/srcset_tag/image/instance.rb +72 -0
- data/lib/jekyll/srcset_tag/image/source.rb +20 -0
- data/lib/jekyll/srcset_tag/image.rb +106 -0
- data/lib/jekyll/srcset_tag/resizer.rb +24 -0
- data/lib/jekyll/srcset_tag/srcset_source_tag.rb +46 -0
- data/lib/jekyll/srcset_tag/srcset_tag.rb +80 -0
- data/lib/jekyll/srcset_tag/version.rb +5 -0
- data/lib/jekyll-srcset-tag.rb +5 -0
- metadata +53 -0
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
|
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: []
|