Class: Jekyll::Images::Thumbnail

Inherits:
Object
  • Object
show all
Defined in:
lib/jekyll/images/thumbnail.rb

Constant Summary collapse

EXT_MAP =
{
  '.heic' => '.jpg',
  nil => '.jpg'
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(site:, filename:, image:, **args) ⇒ Thumbnail

Returns a new instance of Thumbnail.



15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/jekyll/images/thumbnail.rb', line 15

def initialize(site:, filename:, image:, **args)
  if args.slice(:width, :height).values.none?
    raise ArgumentError, "#{filename} thumbnail needs width or height"
  end

  @site = site
  @filename = filename
  @image = image
  @width = args[:width] || proportional_width(args[:height])
  @height = args[:height] || proportional_height(args[:width])
  @crop = args[:crop]&.to_sym || :attention
  @auto_rotate = args[:auto_rotate].nil? ? true : args[:auto_rotate]
end

Instance Attribute Details

#auto_rotateObject (readonly)

Returns the value of attribute auto_rotate.



8
9
10
# File 'lib/jekyll/images/thumbnail.rb', line 8

def auto_rotate
  @auto_rotate
end

#cropObject (readonly)

Returns the value of attribute crop.



8
9
10
# File 'lib/jekyll/images/thumbnail.rb', line 8

def crop
  @crop
end

#filenameObject (readonly)

Returns the value of attribute filename.



8
9
10
# File 'lib/jekyll/images/thumbnail.rb', line 8

def filename
  @filename
end

#heightObject (readonly)

Returns the value of attribute height.



8
9
10
# File 'lib/jekyll/images/thumbnail.rb', line 8

def height
  @height
end

#imageObject (readonly)

Returns the value of attribute image.



8
9
10
# File 'lib/jekyll/images/thumbnail.rb', line 8

def image
  @image
end

#siteObject (readonly)

Returns the value of attribute site.



8
9
10
# File 'lib/jekyll/images/thumbnail.rb', line 8

def site
  @site
end

#widthObject (readonly)

Returns the value of attribute width.



8
9
10
# File 'lib/jekyll/images/thumbnail.rb', line 8

def width
  @width
end

Instance Method Details

#destObject

Generates a destination from filename only if we’re downsizing



50
51
52
53
54
55
56
# File 'lib/jekyll/images/thumbnail.rb', line 50

def dest
  @dest ||= if thumbnail?
              filename.gsub(/#{extname}\z/, "_#{width}x#{height}#{extname}")
            else
              filename.gsub(/#{extname}\z/, "_optimized#{extname}")
            end
end

#extnameString

Return the same address, unless we’re mapping to something else

Returns:

  • (String)


69
70
71
72
73
74
75
76
77
# File 'lib/jekyll/images/thumbnail.rb', line 69

def extname
  @extname ||=
    begin
      ext = File.extname(filename)
      ext = nil if ext.empty?

      EXT_MAP[ext] || ext
    end
end

#interlaced?Boolean

Returns:

  • (Boolean)


124
125
126
# File 'lib/jekyll/images/thumbnail.rb', line 124

def interlaced?
  site.config.dig('images', 'interlaced')
end

#optimizeObject

Run optimizations



106
107
108
109
110
111
112
113
114
# File 'lib/jekyll/images/thumbnail.rb', line 106

def optimize
  before, after = Runner.run(dest, interlaced?)

  return unless before

  pct = ((after.to_f / before) * -100 + 100).round(2)

  Jekyll.logger.info 'Optimization:', "Reduced #{dest} from #{before} to #{after} bytes (%#{pct})"
end

#proportional_height(width) ⇒ Object

Finds a height that’s proportional to the width



40
41
42
# File 'lib/jekyll/images/thumbnail.rb', line 40

def proportional_height(width)
  @proportional_height ||= (image.height * (width / image.width.to_f)).round
end

#proportional_width(height) ⇒ Object

Find a width that’s proportional to height



45
46
47
# File 'lib/jekyll/images/thumbnail.rb', line 45

def proportional_width(height)
  @proportional_width ||= (image.width * (height / image.height.to_f)).round
end

#relative_pathObject



116
117
118
# File 'lib/jekyll/images/thumbnail.rb', line 116

def relative_path
  @relative_path ||= dest.sub(site.source, '').sub(%r{\A/}, '')
end

#static_fileObject



120
121
122
# File 'lib/jekyll/images/thumbnail.rb', line 120

def static_file
  @static_file ||= Jekyll::StaticFile.new(site, site.source, File.dirname(relative_path), File.basename(relative_path))
end

#thumbnailObject

XXX: We don’t memoize because we don’t need the thumbnail in memory after it has been written.



31
32
33
34
35
36
37
# File 'lib/jekyll/images/thumbnail.rb', line 31

def thumbnail
  Vips::Image.thumbnail filename,
                        width,
                        height: height,
                        auto_rotate: auto_rotate,
                        crop: crop
end

#thumbnail?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/jekyll/images/thumbnail.rb', line 62

def thumbnail?
  image.width > width
end

#urlObject



58
59
60
# File 'lib/jekyll/images/thumbnail.rb', line 58

def url
  static_file.url
end

#writeObject

Save the file into destination if needed and add to files to copy



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/jekyll/images/thumbnail.rb', line 86

def write
  return unless write?

  if thumbnail?
    Jekyll.logger.info 'Thumbnail:', "#{filename} => #{dest}"
    thumbnail.write_to_file(dest)
  else
    Jekyll.logger.info 'Copying:', "#{filename} => #{dest}"
    FileUtils.cp(filename, dest)
  end

  # Add it to the static files so Jekyll copies them.  Once they
  # are written they're copied when the site is loaded.
  site.static_files << static_file

  # The file was updated, so it exists and is newer than source
  !write?
end

#write?Boolean

Only write when the source is newer than the thumbnail

Returns:

  • (Boolean)


80
81
82
# File 'lib/jekyll/images/thumbnail.rb', line 80

def write?
  !File.exist?(dest) || File.mtime(filename) > File.mtime(dest)
end