72 lines
2.5 KiB
Ruby
72 lines
2.5 KiB
Ruby
require 'nokogiri'
|
|
|
|
module GalleryItems
|
|
JPEG_START_OF_FRAME = [0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7,
|
|
0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF].freeze
|
|
|
|
# Read just the dimensions from a local JPEG. The rendered gallery needs
|
|
# aspect ratios to size each chronological pair without cropping either photo.
|
|
def jpeg_dimensions(path)
|
|
File.open(path, 'rb') do |file|
|
|
return nil unless file.read(2) == "\xFF\xD8".b
|
|
|
|
while (prefix = file.read(1))
|
|
next unless prefix.getbyte(0) == 0xFF
|
|
|
|
marker = file.read(1)&.getbyte(0)
|
|
break unless marker
|
|
next if marker == 0xFF || marker == 0x01 || (0xD0..0xD7).cover?(marker)
|
|
break if marker == 0xD9 || marker == 0xDA
|
|
|
|
size = file.read(2)&.unpack1('n')
|
|
break unless size && size >= 2
|
|
if JPEG_START_OF_FRAME.include?(marker)
|
|
dimensions = file.read(5)
|
|
return nil unless dimensions&.length == 5
|
|
|
|
height, width = dimensions.byteslice(1, 4).unpack('n2')
|
|
return [width, height] if width.positive? && height.positive?
|
|
return nil
|
|
end
|
|
file.seek(size - 2, IO::SEEK_CUR)
|
|
end
|
|
end
|
|
nil
|
|
rescue Errno::ENOENT, EOFError
|
|
nil
|
|
end
|
|
|
|
def gallery_image_ratio(content)
|
|
image = Nokogiri::HTML::DocumentFragment.parse(content).at_css('img')
|
|
source = image&.[]('src')
|
|
return 1.5 unless source&.start_with?('/img/arts/')
|
|
|
|
path = File.join(File.expand_path('..', __dir__), source.delete_prefix('/'))
|
|
width, height = jpeg_dimensions(path)
|
|
return 1.5 unless width && height
|
|
|
|
(width.to_f / height).round(4)
|
|
end
|
|
|
|
# Uploaded batches share one source document, but each photo is a gallery item.
|
|
# Keeping this at render time also fixes batches that have already been published.
|
|
def gallery_items(content)
|
|
fragment = Nokogiri::HTML::DocumentFragment.parse(content)
|
|
images = fragment.css('img')
|
|
return [content] unless images.length > 1 && images.all? { |image|
|
|
image['src'].to_s.start_with?('/img/arts/uploads/') && image.parent.name == 'p'
|
|
}
|
|
|
|
captions = fragment.css('.image-details').map(&:to_html).join
|
|
images.each { |image| image.parent.remove }
|
|
fragment.css('.image-details').each(&:remove)
|
|
# Preserve the batch's existing anchor exactly once, before the first photo.
|
|
prefix = fragment.to_html
|
|
images.each_with_index.map do |image, index|
|
|
"#{index.zero? ? prefix : ''}<p>#{image.to_html}</p>#{captions}"
|
|
end
|
|
end
|
|
end
|
|
|
|
Liquid::Template.register_filter(GalleryItems)
|