Skip to content

Instantly share code, notes, and snippets.

Created June 16, 2012 14:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/2941419 to your computer and use it in GitHub Desktop.
Save anonymous/2941419 to your computer and use it in GitHub Desktop.
Nanoc filter that computes image dimensions and injects height and width attributes into img tags for much faster and smoother image loading in browsers
# vim: set ts=2 sw=2 et ai ft=ruby:
# Idea from here:
# http://userprimary.net/posts/2011/01/10/optimizing-nanoc-based-websites/
# Also uses code from other filters that are shipped with Nanoc itself.
#
# Implementation enhanced by Pascal Bleser <loki@fosdem.org>,
# under either GPL2 (GNU General Public License) or ASL2.1 (Apache Software License)
# or BSD-3-Clause, as you wish (short version: do whatever you want with it ;)).
#
# Put this file into lib/helpers/ (or lib/filters/, would be better structured)
# and add an invocation of "filter :imagesize" in your Rules, e.g. like this:
# compile '*' do
# filter :imagesize unless item.binary?
# end
class ImageSizeFilter < Nanoc::Filter
type :text
identifier :imagesize
@@SELECTORS = [ 'img' ]
def run(content, params={})
# Set assigns so helper function can be used
@item_rep = assigns[:item_rep] if @item_rep.nil?
selectors = params.fetch(:select) { @@SELECTORS }
namespaces = params[:namespaces] || {}
require 'nokogiri'
klass = ::Nokogiri::HTML
add_image_size(content, selectors, namespaces, klass, 'xhtml')
end
protected
def add_image_size(content, selectors, namespaces, klass, type)
# Ensure that all prefixes are strings
namespaces = namespaces.inject({}) { |new, (prefix, uri)| new.merge(prefix.to_s => uri) }
doc = content =~ /<html[\s>]/ ? klass.parse(content) : klass.fragment(content)
selectors.map { |sel| "#{sel}" }.each do |selector|
doc.xpath(selector, namespaces)
.select { |node| node.is_a? Nokogiri::XML::Element }
.select { |img| img.has_attribute?('src') }
.each do |img|
path = img['src']
dimensions = image_size(path)
dimensions.each{|k,v| img[k.to_s] = v.to_s}
end
end
result = doc.send("to_#{type}")
result
end
def image_size(path)
require 'image_size'
path = '/' + path unless path[0, 1] == '/'
img = ImageSize.new(IO.read("output#{path}"))
{ :height => img.height, :width => img.width }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment