middleman-srcset_images 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 47d0832736b0b4db47c6118f7db98a8e4f50e5ea5bb2629a0a181251f5c49822
4
+ data.tar.gz: cc58dd2ab388a3b991840294784e6c20bd3e1f9557fa409a806396d33f6473db
5
+ SHA512:
6
+ metadata.gz: 01ee0c374636bd776273c8ea06d5706bf5353e2c622b7e06b48e9100854d41e1e8eed410b0bddc12a6ddc909423303a375fed57386666db9410dedd233f0cb18
7
+ data.tar.gz: fdbe6fc29729677f9722c0d90bcaab54b2de7d50525662e8a86c5b09d7f986eeab3367c3c887831c422939978f0161a03bb8badbccda99dcdb57fa5b35be170e
@@ -0,0 +1,5 @@
1
+ # Ignore bundler lock file
2
+ /Gemfile.lock
3
+
4
+ # Ignore pkg folder
5
+ /pkg
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # If you do not have OpenSSL installed, update
2
+ # the following line to use "http://" instead
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in middleman-srcset_images.gemspec
6
+ gemspec
7
+
8
+ group :development do
9
+ gem 'rake'
10
+ gem 'rdoc'
11
+ gem 'yard'
12
+ end
13
+
14
+ group :test do
15
+ gem 'cucumber'
16
+ gem 'aruba'
17
+ gem 'rspec'
18
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Jens Krämer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,161 @@
1
+ # Srcset Image Tags for Middleman
2
+
3
+ ## Prerequisites
4
+
5
+ Install [libvips](https://jcupitt.github.io/libvips) 8.6 or higher.
6
+ Unfortunately on Debian that means compiling it from source.
7
+
8
+ ## Usage
9
+
10
+ Add the gem to your site's Gemfile.
11
+
12
+ ~~~~
13
+ gem 'middleman-srcset_images', github: 'jkraemer/middleman-srcset_images'
14
+ ~~~~
15
+
16
+ Create a configuration file as outlined below.
17
+
18
+ In your Markdown files, use this syntax for images:
19
+
20
+ ~~~
21
+ ![Alt text](/path/to/image.jpg!half)
22
+ ~~~
23
+
24
+ where _half_ is one of the configured image sizes (see below). The result will
25
+ be an image tag with _srcset_ and _sizes_ attributes. To create a linked image,
26
+ add another exclamation mark followed by the destination URL:
27
+
28
+ ~~~
29
+ ![Linked Image](/path/to/image.jpg!half!/path/or/url)
30
+ ~~~
31
+
32
+
33
+ Relative image paths are assumed to be local to the article, and the file
34
+ extension is assumed to be `jpg` if not present. So when using the 'one
35
+ directory per page' approach where you have a file layout like this:
36
+
37
+ ~~~
38
+ source/
39
+ some-article.html.md
40
+ some-article/
41
+ image.jpg
42
+ another_image.jpg
43
+ ~~~
44
+
45
+ you can significantly shorten your markup like this:
46
+
47
+ ~~~
48
+ ![Lorem Ipsum](image!full)
49
+ ~~~
50
+
51
+
52
+
53
+ ## Configuration
54
+
55
+ Configuration takes place in `data/srcset_images.yml`.
56
+
57
+ ### Sizes
58
+
59
+ The keys in this hash are the sizes that can be used in Markdown / with the
60
+ `image_tag` helper. The value is put into the _sizes_ attribute of the
61
+ resulting `img` tag. The purpose of this attribute is to give the browser a hint
62
+ about how big this image will actually be rendered (relative to the screen
63
+ size) due to your CSS rules.
64
+
65
+ The sample below is for a site which can display content images in 3
66
+ different sizes, and that has a separate config for teaser images. On small
67
+ devices, all images are rendered at full width, while on larger devices, they
68
+ only take a fraction of the actual screen width. The separate _teaser_ config
69
+ is only there to allow for different cropping rules in the image versions
70
+ config.
71
+
72
+ ### Image Versions
73
+
74
+ Configure scaling options for landscape and portrait images. These rules
75
+ determine which sizes of images will be created when building your site, and
76
+ also what goes into the `srcset` attribute of the `img` tag.
77
+
78
+ Besides the _landscape_ and _portrait_ keys, which act as fallbacks for images
79
+ of landscape or portrait dimensions, you can add any other keys here for
80
+ different use cases (i.e. cropping to a fixed xy ratio for teaser images as is
81
+ done in the sample below).
82
+
83
+ When rendering an `img` tag, the image version config to be used is picked as
84
+ follows:
85
+
86
+ - if there is a key matching the _size_ parameter, use this config. This would
87
+ be the case for _teaser_ images in the sample below.
88
+ - otherwise, check the layout of the image and pick the _portrait_ config if
89
+ the image is higher than wide, and the _landscape_ otherwise.
90
+
91
+
92
+ ### Sample data/srcset\_images.yml
93
+
94
+ ~~~~
95
+
96
+ ---
97
+
98
+ sizes:
99
+ full: "(min-width: 768px) 90vw, 100vw"
100
+ half: "(min-width: 768px) 45vw, 100vw"
101
+ third: "(min-width: 768px) 30vw, 100vw"
102
+ teaser: "(min-width: 768px) 30vw, 100vw"
103
+
104
+ images: posts/**/*.jpg
105
+ # in case you have symlinked directories to your actual photos, use something
106
+ # like this:
107
+ # images: posts/**{,/*/**}/*.jpg
108
+
109
+ image_versions:
110
+ # configuration for landscape and square images
111
+ landscape:
112
+ quality: 80
113
+ srcset:
114
+ -
115
+ width: 2000
116
+ -
117
+ width: 1400
118
+ is_default: true
119
+ -
120
+ width: 800
121
+ -
122
+ width: 400
123
+
124
+ # portrait content images, cropped to 3:4
125
+ portrait:
126
+ quality: 80
127
+ crop: true
128
+ srcset:
129
+ -
130
+ height: 1800
131
+ width: 1350
132
+ -
133
+ height: 1200
134
+ width: 900
135
+ is_default: true
136
+ -
137
+ height: 800
138
+ width: 600
139
+
140
+ # teaser image, cropped to landscape 3:2
141
+ teaser:
142
+ crop: true
143
+ quality: 80
144
+ srcset:
145
+ -
146
+ width: 1800
147
+ height: 1200
148
+ -
149
+ width: 1200
150
+ height: 800
151
+ is_default: true
152
+ -
153
+ width: 600
154
+ height: 400
155
+
156
+ ~~~~
157
+
158
+ License
159
+ -------
160
+
161
+ MIT, see LICENSE
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'cucumber/rake/task'
5
+
6
+ Cucumber::Rake::Task.new(:cucumber, 'Run features that should pass') do |t|
7
+ t.cucumber_opts = '--color --tags ~@wip --strict'
8
+ end
9
+
10
+ require 'rake/clean'
11
+
12
+ task test: ['cucumber']
13
+
14
+ task default: :test
@@ -0,0 +1,6 @@
1
+ require "middleman-core"
2
+
3
+ Middleman::Extensions.register :srcset_images do
4
+ require "middleman-srcset_images/extension"
5
+ SrcsetImages::Extension
6
+ end
@@ -0,0 +1,79 @@
1
+ require 'fileutils'
2
+ require 'mini_magick'
3
+
4
+ module SrcsetImages
5
+ class CreateImageVersion
6
+
7
+ # CreateImageVersion.(source, destination, width: 800, height: 600, ...)
8
+ def self.call(*_)
9
+ new(*_).call
10
+ end
11
+
12
+ def initialize(source_path, destination_path, options = {})
13
+ @source = source_path
14
+ @destination = destination_path
15
+
16
+ @width = options[:width]
17
+ @height = options[:height]
18
+ @crop = !!options[:crop]
19
+
20
+ @gravity = options.fetch :gravity, 'Center'
21
+ @quality = options.fetch :quality, 90
22
+ @ratio = options.fetch :ratio, 1
23
+ end
24
+
25
+
26
+ def call
27
+ FileUtils.mkdir_p File.dirname(@destination)
28
+ image = MiniMagick::Image.open(@source)
29
+ if @crop
30
+ crop image
31
+ else
32
+ resize image
33
+ end
34
+ image.write @destination
35
+ true
36
+ end
37
+
38
+ private
39
+
40
+ def resize(img)
41
+ img.combine_options do |cmd|
42
+ cmd.resize "#{@width}x#{@height}>"
43
+ trim_down cmd
44
+ end
45
+ end
46
+
47
+ def crop(img)
48
+ cols, rows = img[:dimensions]
49
+
50
+ img.combine_options do |cmd|
51
+ if @width != cols || @height != rows
52
+ scale_x = @width/cols.to_f
53
+ scale_y = @height/rows.to_f
54
+ if scale_x >= scale_y
55
+ cols = (scale_x * (cols + 0.5)).round
56
+ rows = (scale_x * (rows + 0.5)).round
57
+ cmd.resize "#{cols}"
58
+ else
59
+ cols = (scale_y * (cols + 0.5)).round
60
+ rows = (scale_y * (rows + 0.5)).round
61
+ cmd.resize "x#{rows}"
62
+ end
63
+ end
64
+ cmd.gravity @gravity
65
+ cmd.background "rgba(255,255,255,0.0)"
66
+ cmd.extent "#{@width}x#{@height}" if cols != @width || rows != @height
67
+ trim_down cmd
68
+ end
69
+ end
70
+
71
+ def trim_down(cmd)
72
+ cmd.strip
73
+ cmd.quality @quality
74
+ cmd.depth "8"
75
+ cmd.interlace "plane"
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,34 @@
1
+ module SrcsetImages
2
+ module DimensionsPatch
3
+
4
+ # from
5
+ # https://github.com/planio-gmbh/html2odt/commit/36335201cb5440a5ba3ca05f9eb6d8ac556a98ca
6
+ # Default implemenation of IO#peek from GEM_PATH/dimensions-1.3.0/lib/dimensions/io.rb:
7
+ #
8
+ # def peek
9
+ # unless no_peeking?
10
+ # read(pos + 1024) while @reader.width.nil? && pos < 6144
11
+ # rewind
12
+ # end
13
+ # end
14
+ #
15
+ # It had two problems:
16
+ #
17
+ # a) if the file is shorter than 6144 bytes, it would keep reading infinitely
18
+ # b) if the width can only be detected after the 6144 limit, it would not work
19
+ # as expected
20
+ #
21
+ # Now we keep reading the file, until we can determine a width or until
22
+ # there's nothing left to read.
23
+ #
24
+ def peek
25
+ return if no_peeking?
26
+
27
+ while read(pos + 1024) && @reader.width.nil?
28
+ end
29
+
30
+ rewind
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,83 @@
1
+ require 'fileutils'
2
+ require 'middleman-core'
3
+ require 'middleman-srcset_images/view_helpers'
4
+
5
+ # Extension namespace
6
+ module SrcsetImages
7
+ class Extension < ::Middleman::Extension
8
+
9
+ option :cache_dir, 'tmp/srcset_images-cache', 'Directory (relative to project root) for cached image versions.'
10
+
11
+ helpers ViewHelpers
12
+
13
+ attr_reader :image_versions, :scaled_images, :sizes
14
+
15
+ def initialize(app, options_hash={}, &block)
16
+ # Call super to build options from the options_hash
17
+ super
18
+
19
+ # Require libraries only when activated
20
+ require 'middleman-srcset_images/img'
21
+ require 'middleman-srcset_images/version_resource'
22
+ require 'middleman-srcset_images/html_converter'
23
+ require 'middleman-srcset_images/srcset_config'
24
+
25
+ # set up your extension
26
+ # puts options.my_option
27
+
28
+ @config = app.data['srcset_images'] || {}
29
+ @image_versions = @config['image_versions'] || {}
30
+ @images = @config['images']
31
+ @sizes = @config['sizes'] || {}
32
+ @scaled_images = Hash.new{|h,k| h[k] = []}
33
+
34
+ puts "Image versions: #{image_versions.keys.join ", "}"
35
+
36
+ HtmlConverter.install
37
+ end
38
+
39
+ def after_configuration
40
+ FileUtils.mkdir_p options.cache_dir
41
+ end
42
+
43
+ def manipulate_resource_list(resources)
44
+ basedir = File.absolute_path(File.join(app.root, app.config[:source]))
45
+ Dir.chdir(basedir) do
46
+ versions = []
47
+ cache_dir = File.absolute_path(options.cache_dir, app.root)
48
+
49
+ configurations = image_versions.map do |name, config|
50
+ SrcsetImages::SrcsetConfig.new name, config, cache_dir: cache_dir
51
+ end
52
+
53
+ images = Dir.glob(@images).map{|f| SrcsetImages::Img.new(f)}
54
+
55
+ # loop over configurations for landscape, portrait, teasers
56
+ configurations.each do |config|
57
+
58
+ #loop over original image files
59
+ images.each do |img|
60
+
61
+ # loop over different image sizes of configuration
62
+ config.image_versions(img).each do |v|
63
+ v.app = app
64
+ v.prepare_image
65
+ @scaled_images[img.path] << v
66
+ versions << VersionResource.new(app.sitemap, v)
67
+ end
68
+
69
+ end
70
+ end
71
+ versions.flatten!
72
+ versions.compact!
73
+ puts "added #{versions.size} image versions"
74
+ resources + versions
75
+ end
76
+ end
77
+
78
+ # helpers do
79
+ # def a_helper
80
+ # end
81
+ # end
82
+ end
83
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'middleman-core/renderers/kramdown'
4
+
5
+ module SrcsetImages
6
+ module HtmlConverter
7
+
8
+ def self.install
9
+ unless Middleman::Renderers::MiddlemanKramdownHTML < self
10
+ Middleman::Renderers::MiddlemanKramdownHTML.prepend self
11
+ end
12
+ end
13
+
14
+ def convert_img(el, indent)
15
+ attrs = el.attr.dup
16
+
17
+ attrs['title'] ||= attrs['alt']
18
+
19
+ src = attrs.delete "src"
20
+
21
+ path, size, link_to = src.split(?!)
22
+ # default to jpg as image file extension
23
+ path += ".jpg" unless path =~ /\.[a-z]{3}\z/i
24
+
25
+ if link_to
26
+ attrs[:link] = link_to
27
+ end
28
+
29
+ if size
30
+ attrs[:size] = size
31
+
32
+ %{<div class="item #{size}">} + scope.image_tag(path, attrs) + "</div>"
33
+ else
34
+ scope.image_tag path, attrs
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+
@@ -0,0 +1,115 @@
1
+ #require 'middleman-srcset_images/create_image_version'
2
+ require 'middleman-srcset_images/vips_create_image_version'
3
+
4
+ module SrcsetImages
5
+ class ImageVersion
6
+
7
+ attr_reader :img, :resized_img_path, :config, :name, :width
8
+ attr_accessor :app
9
+
10
+ # resized_img_path is the wrong path here
11
+ # (posts/2013/08-17-kilimanjaro/bay_ls_0.jpg instead of
12
+ # 2013/08/kilimanjaro/bay_ls_0.jpg)
13
+ # but it does not seem to matter since this is apparently fixed by
14
+ # middleman itself through the VersionResource
15
+ def initialize(img, resized_img_path, config)
16
+ @img = img
17
+ @resized_img_path = resized_img_path
18
+ @config = config
19
+
20
+ @default_for_orientation = config[:name] == img.orientation
21
+
22
+ @width = config[:width]
23
+ @height = config[:height]
24
+
25
+ if @width.nil? && @height.nil?
26
+ raise ArgumentError, "need at least width or height!\nconfig was: #{config}"
27
+ end
28
+
29
+ ratio = config[:ratio] || img.xy_ratio
30
+ if @width.blank?
31
+ @width = (@height.to_f * ratio).to_i
32
+ end
33
+ if @height.blank?
34
+ @height = (@width.to_f / ratio).to_i
35
+ end
36
+
37
+ @crop = config.fetch :crop, false
38
+ @quality = config.fetch :quality, 80
39
+ @cache_dir = config[:cache_dir]
40
+ end
41
+
42
+
43
+ def img_path
44
+ img.rel_path
45
+ end
46
+
47
+ def default?
48
+ !!config[:is_default]
49
+ end
50
+
51
+ def default_for_orientation?
52
+ @default_for_orientation
53
+ end
54
+
55
+ def base64_data
56
+ Base64.strict_encode64 render
57
+ end
58
+
59
+ def render
60
+ prepare_image
61
+ File.read cached_resized_img_abs_path
62
+ end
63
+
64
+
65
+ #def middleman_resized_abs_path
66
+ # #middleman_abs_path.gsub(img.filename, resized_image_name)
67
+ # File.join File.dirname(img.abs_path), resized_image_name
68
+ #end
69
+
70
+ #def middleman_abs_path
71
+ # img_path.start_with?('/') ? img_path : File.join(images_dir, img_path)
72
+ #end
73
+
74
+ def cached_resized_img_abs_path
75
+ File.join(@cache_dir, resized_img_path).split('.').tap { |a|
76
+ a.insert(-2, img.checksum)
77
+ }.join('.')
78
+ end
79
+
80
+ def prepare_image
81
+ unless cached_image_available?
82
+ save_cached_image
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def source_dir
89
+ File.absolute_path(app.config[:source], @app.root)
90
+ end
91
+
92
+ def images_dir
93
+ app.config[:images_dir]
94
+ end
95
+
96
+ def build_dir
97
+ app.config[:build_dir]
98
+ end
99
+
100
+ def save_cached_image
101
+ FileUtils.mkdir_p(File.dirname(cached_resized_img_abs_path))
102
+ VipsCreateImageVersion.(
103
+ img.vips, cached_resized_img_abs_path,
104
+ width: @width, height: @height, quality: @quality, crop: @crop
105
+ )
106
+ end
107
+
108
+ def cached_image_available?
109
+ File.exist?(cached_resized_img_abs_path)
110
+ end
111
+
112
+ end
113
+ end
114
+
115
+
@@ -0,0 +1,74 @@
1
+ require 'dimensions'
2
+ require 'image_processing/vips'
3
+ require 'middleman-srcset_images/dimensions_patch'
4
+
5
+ module SrcsetImages
6
+ class Img
7
+
8
+ attr_reader :path, :abs_path, :width, :height
9
+
10
+ def initialize(path)
11
+ @path = path
12
+ @abs_path = Pathname(path).absolute? ? path : File.join(Dir.pwd, path)
13
+ end
14
+
15
+ def orientation
16
+ landscape? ? 'landscape' : 'portrait'
17
+ end
18
+
19
+ def rel_path
20
+ @rel_path ||= Pathname(abs_path).relative_path_from(Pathname(Dir.pwd))
21
+ end
22
+
23
+ # true if landscape or square
24
+ def landscape?
25
+ xy_ratio >= 1
26
+ end
27
+
28
+ # true if portrait
29
+ def portrait?
30
+ xy_ratio < 1
31
+ end
32
+
33
+ def vips
34
+ @vips ||= ImageProcessing::Vips.source(abs_path)
35
+ end
36
+
37
+ def path_for_version(cfg_name, idx)
38
+ "#{File.dirname rel_path}/#{basename}_#{cfg_name}_#{idx}#{ext}"
39
+ end
40
+
41
+ def basename
42
+ @basename ||= File.basename path, ext
43
+ end
44
+
45
+ def filename
46
+ File.basename path
47
+ end
48
+
49
+ def ext
50
+ @ext ||= File.extname path
51
+ end
52
+
53
+ def checksum
54
+ @checksum ||= Digest::SHA2.file(abs_path).hexdigest[0..16]
55
+ end
56
+
57
+ # TODO can get dimensions from vips?
58
+ def xy_ratio
59
+ @xy_ratio ||= begin
60
+ File.open(abs_path, 'rb') do |io|
61
+ Dimensions(io)
62
+ io.extend DimensionsPatch
63
+ @width, @height = io.dimensions
64
+ @width.to_f / @height
65
+ end
66
+ end
67
+ rescue
68
+ nil
69
+ end
70
+
71
+ end
72
+ end
73
+
74
+
@@ -0,0 +1,44 @@
1
+ require 'middleman-srcset_images/image_version'
2
+
3
+ module SrcsetImages
4
+ class SrcsetConfig
5
+
6
+ attr_reader :name, :config
7
+
8
+ def initialize(name, config, cache_dir:)
9
+ @name = name
10
+ @config = config
11
+
12
+ @base_config = {
13
+ name: name,
14
+ crop: config.fetch(:crop, false),
15
+ quality: config.fetch(:quality, 80),
16
+ cache_dir: cache_dir
17
+ }
18
+ end
19
+
20
+ def image_versions(img)
21
+ result = []
22
+
23
+ if applies_to?(img)
24
+
25
+
26
+ config.srcset.each_with_index do |config, idx|
27
+ result << ImageVersion.new(
28
+ img,
29
+ img.path_for_version(name, idx),
30
+ @base_config.merge(config.symbolize_keys)
31
+ )
32
+ end
33
+ end
34
+
35
+ result
36
+ end
37
+
38
+ def applies_to?(img)
39
+ not ((name == 'landscape' && img.portrait?) or
40
+ (name == 'portrait' && img.landscape?))
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ module SrcsetImages
2
+ class VersionResource < ::Middleman::Sitemap::Resource
3
+ def initialize(store, image_version)
4
+ super store, image_version.resized_img_path, image_version.cached_resized_img_abs_path
5
+ end
6
+
7
+ def ignored?
8
+ false
9
+ end
10
+
11
+ def template?
12
+ false
13
+ end
14
+
15
+ def binary?
16
+ true
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SrcsetImages
4
+ module ViewHelpers
5
+
6
+ # options can be:
7
+ #
8
+ # size: pick an image version
9
+ # link: Set to an url to link to
10
+ #
11
+ def image_tag(path, options = {})
12
+ # allow for images in article directories to be referenced just by file name
13
+ unless path[?/]
14
+ page_path = current_page.path
15
+ dir = File.dirname page_path
16
+ path = File.join dir, File.basename(page_path, '.html'), path
17
+ end
18
+
19
+ # collect srcset info
20
+ options = options.dup
21
+ ext = app.extensions[:srcset_images]
22
+ rel_path = path.sub(/\A\/?/, "")
23
+ versions = nil
24
+
25
+ if size = options.delete(:size)
26
+ options[:sizes] = ext.sizes[size]
27
+
28
+ scaled_images = ext.scaled_images[rel_path]
29
+ versions = scaled_images.select{|v| v.name == size.to_s}
30
+ unless versions.any?
31
+ versions = scaled_images.select{|v| v.default_for_orientation?}
32
+ end
33
+
34
+ if versions.any?
35
+ path = (versions.detect{|v|v.default?} || versions.first).resized_img_path
36
+ options[:srcset] = versions.map { |v|
37
+ "#{v.resized_img_path} #{v.width}w"
38
+ }.join ", "
39
+ end
40
+ end
41
+
42
+ link = options.delete(:link)
43
+ img = super path, options
44
+
45
+ if link
46
+ link_to img, link
47
+ else
48
+ img
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+
@@ -0,0 +1,46 @@
1
+ require 'fileutils'
2
+ require 'image_processing/vips'
3
+
4
+ module SrcsetImages
5
+ class VipsCreateImageVersion
6
+
7
+ # VipsCreateImageVersion.(source, destination, width: 800, height: 600, ...)
8
+ def self.call(*_)
9
+ new(*_).call
10
+ end
11
+
12
+ def initialize(img, destination_path, options = {})
13
+ @source = if img.is_a?(String) || img.is_a?(Pathname)
14
+ ImageProcessing::Vips.source(img)
15
+ else
16
+ img
17
+ end
18
+
19
+ @destination = destination_path
20
+
21
+ @width = options[:width]
22
+ @height = options[:height]
23
+ @crop = !!options[:crop]
24
+
25
+ @quality = options.fetch :quality, 90
26
+ end
27
+
28
+
29
+ def call
30
+ img = if @crop
31
+ @source.resize_to_fill @width, @height, crop: :attention
32
+ else
33
+ @source.resize_to_limit @width, @height
34
+ end
35
+ processed = img
36
+ .saver(strip: true, quality: @quality, interlace: true)
37
+ .call
38
+
39
+ FileUtils.mkdir_p File.dirname(@destination)
40
+ FileUtils.mv processed, @destination
41
+ true
42
+ end
43
+
44
+ end
45
+ end
46
+
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "middleman-srcset_images"
6
+ s.version = "0.2.0"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Jens Kraemer"]
9
+ s.email = ["jk@jkraemer.net"]
10
+ s.homepage = "https://github.com/jkraemer/middleman-srcset_images"
11
+ s.summary = %q{Responsive images for Middleman}
12
+ s.description = %q{Middleman plugin for automatic img tags with proper srcset attributes. You can configure any number of image size sets for different use cases (i.e. different image sizes for teasers, portrait and landscape images). Scaled images are generated using libvips.}
13
+ s.license = 'MIT'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.require_paths = ["lib"]
18
+
19
+ # The version of middleman-core your extension depends on
20
+ s.add_runtime_dependency("middleman-core", ["~> 4.2"])
21
+
22
+ # Additional dependencies
23
+ s.add_runtime_dependency("dimensions", ["~> 1.3"])
24
+ s.add_runtime_dependency("image_processing", ["~> 1.0"])
25
+ s.add_runtime_dependency("ruby-vips", ["~> 2.0"])
26
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: middleman-srcset_images
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Jens Kraemer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: middleman-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dimensions
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: image_processing
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby-vips
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ description: Middleman plugin for automatic img tags with proper srcset attributes.
70
+ You can configure any number of image size sets for different use cases (i.e. different
71
+ image sizes for teasers, portrait and landscape images). Scaled images are generated
72
+ using libvips.
73
+ email:
74
+ - jk@jkraemer.net
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - Gemfile
81
+ - LICENSE
82
+ - README.md
83
+ - Rakefile
84
+ - lib/middleman-srcset_images.rb
85
+ - lib/middleman-srcset_images/create_image_version.rb
86
+ - lib/middleman-srcset_images/dimensions_patch.rb
87
+ - lib/middleman-srcset_images/extension.rb
88
+ - lib/middleman-srcset_images/html_converter.rb
89
+ - lib/middleman-srcset_images/image_version.rb
90
+ - lib/middleman-srcset_images/img.rb
91
+ - lib/middleman-srcset_images/srcset_config.rb
92
+ - lib/middleman-srcset_images/version_resource.rb
93
+ - lib/middleman-srcset_images/view_helpers.rb
94
+ - lib/middleman-srcset_images/vips_create_image_version.rb
95
+ - middleman-srcset_images.gemspec
96
+ homepage: https://github.com/jkraemer/middleman-srcset_images
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.7.3
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Responsive images for Middleman
120
+ test_files: []