require 'cgi/util'
require 'digest/sha2'
require 'fileutils'
require 'fastimage'
SHA_DIR = 'sha'
ASSET_DIR = '_assets'
def rename_to_shasum(context, path)
extname = File.extname(path)
hexname = Digest::SHA2.file(path).hexdigest
cache_dir = File.join('.jekyll-cache', 'sha_files')
FileUtils.makedirs(File.join(cache_dir, SHA_DIR))
base_shaname = "#{hexname}#{extname}"
cache_fname = File.join(cache_dir, SHA_DIR, base_shaname)
site = context.registers[:site]
rel_path = File.join(SHA_DIR, base_shaname)
already_added = false
for file in site.static_files
if file.relative_path == rel_path
already_added = true
break
end
end
if File.exists?(cache_fname)
FileUtils.rm(cache_fname)
end
FileUtils.link_entry(path, cache_fname)
if not already_added
site.static_files << Jekyll::StaticFile.new(site, cache_dir, SHA_DIR, base_shaname)
end
return "/#{SHA_DIR}/#{base_shaname}"
end
def prefix_file_dir_to_input(context, input)
raw_name = context.registers[:page]['path']
folder_dir = File.basename(raw_name, '.*')
return folder_dir + File::Separator + input
end
class ShaFileTag < Liquid::Tag
def initialize(tag_name, input, tokens)
super
@path = input.strip
end
def render(context)
CGI.escape_html rename_to_shasum(context, File.join(ASSET_DIR, @path))
end
end
Liquid::Template.register_tag('shafile', ShaFileTag)
class LocalShaFileTag < ShaFileTag
def render(context)
@path = prefix_file_dir_to_input(context, @path)
super
end
end
Liquid::Template.register_tag('lshafile', LocalShaFileTag)
def image_mimetype(ext)
case ext
when '.jpg', '.jpeg'
return 'image/jpeg'
when '.png'
return 'image/png'
else
raise "image_mimetype: unknown extension #{ext}"
end
end
class ShaImageTag < Liquid::Tag
def initialize(tag_name, input, tokens)
super
@input = input
end
def parse_args(context)
args = @input.strip.split('|')
if args.length != 2 and args.length != 3
bad_file = context.registers[:page]['path']
err_msg = "On #{bad_file}: '#{@input}' does not contain 2 or 3 arguments"
raise err_msg
end
ret = Hash.new
ret[:path] = args[0].strip
ret[:title] = args[1].strip
ret[:alt] = ret[:title]
if args.length == 3
ret[:alt] = args[2].strip
end
return ret
end
def render(context)
args = parse_args(context)
fname = File.join(ASSET_DIR, args[:path])
extname = File.extname(fname)
original_mimetype = image_mimetype(extname)
source_tags = []
src = CGI.escape_html rename_to_shasum(context, fname)
orig_width, orig_height = FastImage.size(fname, :raise_on_failure=>true)
alt = CGI.escape_html args[:alt]
title = CGI.escape_html args[:title]
orig_src = "<source srcset=\"#{src}\" type=\"#{original_mimetype}\" width=\"#{orig_width}\" height=\"#{orig_height}\">"
source_tags.push(orig_src)
img_tag = "<img src=\"#{src}\" alt=\"#{alt}\" title=\"#{title}\" width=\"#{orig_width}\" height=\"#{orig_height}\">"
webp_fname = fname[0..-extname.length()-1] + '.webp'
has_webp = File.file?(webp_fname)
if has_webp
webp_src = CGI.escape_html rename_to_shasum(context, webp_fname)
webp_width, webp_height = FastImage.size(webp_fname, :raise_on_failure=>true)
webp_src = "<source srcset=\"#{webp_src}\" type=\"image/webp\" width=\"#{webp_width}\" height=\"#{webp_height}\">"
source_tags.push(webp_src)
end
avif_fname = fname[0..-extname.length()-1] + '.avif'
has_avif = File.file?(avif_fname)
if has_avif
avif_src = CGI.escape_html rename_to_shasum(context, avif_fname)
avif_width, avif_height = FastImage.size(avif_fname, :raise_on_failure=>true)
avif_src = "<source srcset=\"#{avif_src}\" type=\"image/avif\" width=\"#{avif_width}\" height=\"#{avif_height}\">"
source_tags.push(avif_src)
end
if source_tags.length == 1
Jekyll.logger.warn "#{fname} has no .webp/.avif equivalent. Consider creating one"
return img_tag
end
return "<picture>#{source_tags.reverse.join}#{img_tag}</picture>"
end
end
Liquid::Template.register_tag('shaimg', ShaImageTag)
class LocalShaImageTag < ShaImageTag
def parse_args(context)
@input = prefix_file_dir_to_input(context, @input)
super
end
end
Liquid::Template.register_tag('lshaimg', LocalShaImageTag)
class LocalIncludeTag < Liquid::Tag
def initialize(tag_name, input, tokens)
super
@input = input.strip
end
def render(context)
input = File.join(ASSET_DIR, prefix_file_dir_to_input(context, @input))
return File.read(input)
end
end
Liquid::Template.register_tag('linclude', LocalIncludeTag)