Class: Kithe::VipsCliImageToJpeg

Inherits:
Object
  • Object
show all
Defined in:
app/derivative_transformers/kithe/vips_cli_image_to_jpeg.rb

Overview

Use the [vips](jcupitt.github.io/libvips/) command-line utility (via shell-out) to transform any image type to a JPG, with a specified maximum width (keeping aspect ratio).

Requires vips command line utilities ‘vips` and `vipsthumbnail` and to be installed on your system, eg `brew install vips`, or apt package `vips-tools`.

If thumbnail_mode:true is given, we ALSO apply some additional best practices for minimizing size when used as an image _in a browser_, such as removing color profile information. See eg:

* https://developers.google.com/speed/docs/insights/OptimizeImages
* http://libvips.blogspot.com/2013/11/tips-and-tricks-for-vipsthumbnail.html
* https://github.com/jcupitt/libvips/issues/775

It takes an open ‘File` object in, and returns an open TempFile object. It is built for use with kithe derivatives transformations, eg:

class Asset < KitheAsset
  define_derivative(thumb) do |original_file|
    Kithe::VipsCliImageToJpeg.new(max_width: 100, thumbnail_mode: true).call(original_file)
  end
end

We use the vips CLI because we know how, and it means we can avoid worrying about ruby memory leaks or the GIL. An alternative that uses vips ruby bindings would also be possible, and might work well, but this is what for us is tried and true.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_width: nil, jpeg_q: 85, thumbnail_mode: false) ⇒ VipsCliImageToJpeg

Returns a new instance of VipsCliImageToJpeg.



38
39
40
41
42
43
44
45
46
47
# File 'app/derivative_transformers/kithe/vips_cli_image_to_jpeg.rb', line 38

def initialize(max_width:nil, jpeg_q: 85, thumbnail_mode: false)
  @max_width = max_width
  @jpeg_q = jpeg_q
  @thumbnail_mode = !!thumbnail_mode

  if thumbnail_mode && max_width.nil?
    # https://github.com/libvips/libvips/issues/1179
    raise ArgumentError.new("thumbnail_mode currently requires a non-nil max_width")
  end
end

Instance Attribute Details

#jpeg_qObject (readonly)

Returns the value of attribute jpeg_q.



36
37
38
# File 'app/derivative_transformers/kithe/vips_cli_image_to_jpeg.rb', line 36

def jpeg_q
  @jpeg_q
end

#max_widthObject (readonly)

Returns the value of attribute max_width.



36
37
38
# File 'app/derivative_transformers/kithe/vips_cli_image_to_jpeg.rb', line 36

def max_width
  @max_width
end

Instance Method Details

#call(original_file) ⇒ Object

Will raise TTY::Command::ExitError if the external Vips command returns non-null.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'app/derivative_transformers/kithe/vips_cli_image_to_jpeg.rb', line 50

def call(original_file)
  tempfile = Tempfile.new(["kithe_vips_cli_image_to_jpeg", ".jpg"])

  vips_args = []

  # If we are resizing, we use `vipsthumbnail`, if we are not resizing,
  # `vips copy` works better.
  if max_width
    # Due to bug in vips, we need to provide a height constraint, we make
    # really huge one million pixels so it should not come into play, and
    # we're constraining proportionally by width.
    # https://github.com/jcupitt/libvips/issues/781
    vips_args.concat [vips_thumbnail_command, original_file.path]
    vips_args.concat maybe_profile_normalization_args
    vips_args.concat ["--size", "#{max_width}x65500"]
    vips_args.concat ["-o", "#{tempfile.path}#{vips_jpg_params}"]
  else
    # If we arne't making a thumbnail, we need to use `vips copy` instead of `vipsthumbnail`,
    # to avoid it changing height/width on us. There might be another way.
    #
    # Yes, this means we can't do thumbnail-mode normalizations.
    vips_args.concat [vips_command, "copy", original_file.path]
    vips_args.concat ["#{tempfile.path}#{vips_jpg_params}"]
  end

  TTY::Command.new(printer: :null).run(*vips_args)

  return tempfile
end