Class: Paperclip::Thumbnail

Inherits:
Processor show all
Defined in:
lib/paperclip/thumbnail.rb

Overview

Handles thumbnailing images that are uploaded. Now uses the image_processing gem internally, supporting both ImageMagick (via MiniMagick) and libvips backends.

Examples:

Basic usage (unchanged from before)

has_attached_file :avatar,
  styles: { medium: "300x300>", thumb: "100x100#" }

Using libvips backend for better performance

has_attached_file :avatar,
  styles: { medium: "300x300>", thumb: "100x100#" },
  backend: :vips

Per-style backend selection

has_attached_file :document,
  styles: {
    preview: { geometry: "800x800>", backend: :vips },
    thumb: { geometry: "100x100#", backend: :image_magick }
  }

Constant Summary collapse

ANIMATED_FORMATS =
%w(gif).freeze
MULTI_FRAME_FORMATS =

Creates a Thumbnail object set to work on the file given. It will attempt to transform the image into one defined by target_geometry which is a “WxH”-style string. format will be inferred from the file unless specified. Thumbnail creation will raise no errors unless whiny is true (which it is, by default. If convert_options is set, the options will be appended to the convert command upon image conversion

Options include:

+backend+ - image_magick or vips, fallbacks to Paperclip.options[:backend]
+geometry+ - the desired width and height of the thumbnail (required)
+file_geometry_parser+ - an object with a method named +from_file+ that takes an image file and produces its geometry and a +transformation_to+. Defaults to Paperclip::Geometry
+string_geometry_parser+ - an object with a method named +parse+ that takes a string and produces an object with +width+, +height+, and +to_s+ accessors. Defaults to Paperclip::Geometry
+source_file_options+ - flags passed to the +convert+ command that influence how the source file is read
+convert_options+ - flags passed to the +convert+ command that influence how the image is processed
+whiny+ - whether to raise an error when processing fails. Defaults to true
+format+ - the desired filename extension
+animated+ - whether to merge all the layers in the image. Defaults to true
+frame_index+ - the frame index of the source file to render as the thumbnail
%w(.mkv .avi .mp4 .mov .mpg .mpeg .gif .pdf).freeze
ALLOWED_IMAGEMAGICK_OPTIONS =

Like ActiveStorage we want to be careful on what options are allowed for ImageMagick 2 additional options added to Active Storage default list: set and profile github.com/advisories/GHSA-r4mg-4433-c7g3

%w(
  adaptive_blur adaptive_resize adaptive_sharpen adjoin affine alpha annotate antialias append
  attenuate authenticate auto_gamma auto_level auto_orient auto_threshold backdrop background
  bench bias bilateral_blur black_point_compensation black_threshold blend blue_primary
  blue_shift blur border bordercolor borderwidth brightness_contrast cache canny caption
  channel channel_fx charcoal chop clahe clamp clip clip_path clone clut coalesce colorize
  colormap color_matrix colors colorspace colourspace color_threshold combine combine_options
  comment compare complex compose composite compress connected_components contrast
  contrast_stretch convert convolve copy crop cycle deconstruct define delay delete density
  depth descend deskew despeckle direction displace dispose dissimilarity_threshold dissolve
  distort dither draw duplicate edge emboss encoding endian enhance equalize evaluate
  evaluate_sequence extent extract family features fft fill filter flatten flip floodfill
  flop font foreground format frame function fuzz fx gamma gaussian_blur geometry gravity
  grayscale green_primary hald_clut highlight_color hough_lines iconGeometry iconic identify
  ift illuminant immutable implode insert intensity intent interlace interline_spacing
  interpolate interpolative_resize interword_spacing kerning kmeans kuwahara label lat layers
  level level_colors limit limits linear_stretch linewidth liquid_rescale list log loop
  lowlight_color magnify map mattecolor median mean_shift metric mode modulate moments
  monitor monochrome morph morphology mosaic motion_blur name negate noise normalize opaque
  ordered_dither orient page paint pause perceptible ping pointsize polaroid poly posterize
  precision preview process profile quality quantize quiet radial_blur raise random_threshold
  range_threshold red_primary regard_warnings region remote render repage resample resize
  resize_to_fill resize_to_fit resize_to_limit resize_and_pad respect_parentheses reverse
  roll rotate sample sampling_factor scale scene screen seed segment selective_blur separate
  sepia_tone set shade shadow shared_memory sharpen shave shear sigmoidal_contrast silent
  similarity_threshold size sketch smush snaps solarize sort_pixels sparse_color splice
  spread statistic stegano stereo storage_type stretch strip stroke strokewidth style
  subimage_search swap swirl synchronize taint text_font threshold thumbnail tile_offset tint
  title transform transparent transparent_color transpose transverse treedepth trim type
  undercolor unique_colors units unsharp update valid_image view vignette virtual_pixel
  visual watermark wave wavelet_denoise weight white_balance white_point white_threshold
  window window_group
).freeze

Instance Attribute Summary collapse

Attributes inherited from Processor

#attachment, #file, #options

Instance Method Summary collapse

Methods inherited from Processor

#convert, #identify, make, #vips, #vips_image, #vipsheader

Constructor Details

#initialize(file, options = {}, attachment = nil) ⇒ Thumbnail

Returns a new instance of Thumbnail.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/paperclip/thumbnail.rb', line 93

def initialize(file, options = {}, attachment = nil)
  super

  geometry = options[:geometry].to_s
  @crop = geometry[-1, 1] == "#"
  @target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
  @whiny = options.fetch(:whiny, true)
  @format = options[:format]
  @animated = options.fetch(:animated, true)
  @auto_orient = options.fetch(:auto_orient, true)

  # Backward-compatible options
  @convert_options = options[:convert_options]
  @source_file_options = options[:source_file_options]

  # New options
  @backend = resolve_backend(options)

  @current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file, @backend)
  @current_geometry.auto_orient if @auto_orient && @current_geometry.respond_to?(:auto_orient)

  @current_format = File.extname(@file.path)
  @basename = File.basename(@file.path, @current_format)
  @frame_index = multi_frame_format? ? options.fetch(:frame_index, 0) : 0
end

Instance Attribute Details

#animatedObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def animated
  @animated
end

#auto_orientObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def auto_orient
  @auto_orient
end

#backendObject

New attributes



33
34
35
# File 'lib/paperclip/thumbnail.rb', line 33

def backend
  @backend
end

#convert_optionsObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def convert_options
  @convert_options
end

#current_geometryObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def current_geometry
  @current_geometry
end

#formatObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def format
  @format
end

#frame_indexObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def frame_index
  @frame_index
end

#source_file_optionsObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def source_file_options
  @source_file_options
end

#target_geometryObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def target_geometry
  @target_geometry
end

#whinyObject

Backward-compatible attributes (same as original Thumbnail)



28
29
30
# File 'lib/paperclip/thumbnail.rb', line 28

def whiny
  @whiny
end

Instance Method Details

#convert_options?Boolean

Returns true if the image is meant to make use of additional convert options. Backwards-compatible method from original Thumbnail.

Returns:

  • (Boolean)


126
127
128
# File 'lib/paperclip/thumbnail.rb', line 126

def convert_options?
  @convert_options.present?
end

#crop?Boolean

Returns true if the target_geometry is meant to crop.

Returns:

  • (Boolean)


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

def crop?
  @crop
end

#makeObject



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/paperclip/thumbnail.rb', line 130

def make
  source_path = File.expand_path(@file.path)
  extension = @format ? ".#{@format}" : @current_format
  filename = [@basename, extension].join
  destination = nil

  begin
    destination = TempfileFactory.new.generate(filename)
    pipeline = build_pipeline(source_path)
    pipeline.call(destination: destination.path)
    destination
  rescue LoadError => e
    destination&.close! if destination.respond_to?(:close!)
    raise Paperclip::Errors::CommandNotFoundError.new("Could not run the command for #{backend}. Please install dependencies.")
  rescue StandardError => e
    destination&.close! if destination.respond_to?(:close!)
    if defined?(::Vips::Error) && e.is_a?(::Vips::Error)
      handle_error(e, "libvips")
    elsif defined?(::MiniMagick::Error) && (e.is_a?(::MiniMagick::Error) || e.is_a?(::MiniMagick::Invalid))
      handle_error(e, "ImageMagick")
    elsif defined?(::ImageProcessing::Error) && e.is_a?(::ImageProcessing::Error)
      handle_error(e, "ImageProcessing")
    else
      raise e
    end
  end
end

#transformation_commandObject

Deprecated.

This method is deprecated and does not reflect actual processing.

Returns the command ImageMagick’s convert needs to transform the image into the thumbnail. Provided for backwards compatibility.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/paperclip/thumbnail.rb', line 161

def transformation_command
  if backend == :vips
    Paperclip.log("Warning: transformation_command called but using vips backend")
  end

  scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
  trans = []
  trans << "-coalesce" if animated?
  trans << "-auto-orient" if auto_orient
  trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
  trans << "-crop" << %["#{crop}"] << "+repage" if crop
  trans << '-layers "optimize"' if animated?
  trans
end