carrierwave 1.3.2 → 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +235 -91
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -2
  4. data/lib/carrierwave/downloader/base.rb +101 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +68 -0
  6. data/lib/carrierwave/locale/en.yml +9 -6
  7. data/lib/carrierwave/mount.rb +53 -61
  8. data/lib/carrierwave/mounter.rb +167 -77
  9. data/lib/carrierwave/orm/activerecord.rb +23 -58
  10. data/lib/carrierwave/processing/mini_magick.rb +108 -123
  11. data/lib/carrierwave/processing/rmagick.rb +11 -15
  12. data/lib/carrierwave/processing/vips.rb +284 -0
  13. data/lib/carrierwave/processing.rb +1 -0
  14. data/lib/carrierwave/sanitized_file.rb +60 -66
  15. data/lib/carrierwave/storage/abstract.rb +5 -5
  16. data/lib/carrierwave/storage/file.rb +6 -5
  17. data/lib/carrierwave/storage/fog.rb +101 -62
  18. data/lib/carrierwave/storage.rb +1 -0
  19. data/lib/carrierwave/test/matchers.rb +11 -7
  20. data/lib/carrierwave/uploader/cache.rb +40 -24
  21. data/lib/carrierwave/uploader/callbacks.rb +1 -1
  22. data/lib/carrierwave/uploader/configuration.rb +38 -19
  23. data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
  24. data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
  25. data/lib/carrierwave/uploader/dimension.rb +66 -0
  26. data/lib/carrierwave/uploader/download.rb +2 -123
  27. data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
  28. data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
  29. data/lib/carrierwave/uploader/file_size.rb +2 -2
  30. data/lib/carrierwave/uploader/mountable.rb +6 -0
  31. data/lib/carrierwave/uploader/processing.rb +42 -7
  32. data/lib/carrierwave/uploader/proxy.rb +17 -4
  33. data/lib/carrierwave/uploader/serialization.rb +1 -1
  34. data/lib/carrierwave/uploader/store.rb +47 -7
  35. data/lib/carrierwave/uploader/url.rb +7 -4
  36. data/lib/carrierwave/uploader/versions.rb +157 -109
  37. data/lib/carrierwave/uploader.rb +10 -17
  38. data/lib/carrierwave/utilities/file_name.rb +47 -0
  39. data/lib/carrierwave/utilities/uri.rb +14 -11
  40. data/lib/carrierwave/utilities.rb +1 -0
  41. data/lib/carrierwave/validations/active_model.rb +7 -9
  42. data/lib/carrierwave/version.rb +1 -1
  43. data/lib/carrierwave.rb +13 -17
  44. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +3 -3
  45. data/lib/generators/uploader_generator.rb +3 -3
  46. metadata +103 -36
  47. data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
  48. data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
  49. data/lib/carrierwave/uploader/extension_blacklist.rb +0 -51
  50. data/lib/carrierwave/uploader/extension_whitelist.rb +0 -52
@@ -21,9 +21,11 @@ module CarrierWave
21
21
  # process :resize_to_fit => [200, 200]
22
22
  # end
23
23
  #
24
- # Or create your own helpers with the powerful manipulate! method. Check
25
- # out the ImageMagick docs at http://www.imagemagick.org/script/command-line-options.php for more
26
- # info
24
+ # Or create your own helpers with the powerful minimagick! method, which
25
+ # yields an ImageProcessing::Builder object. Check out the ImageProcessing
26
+ # docs at http://github.com/janko-m/image_processing and the list of all
27
+ # available ImageMagick options at
28
+ # http://www.imagemagick.org/script/command-line-options.php for more info.
27
29
  #
28
30
  # class MyUploader < CarrierWave::Uploader::Base
29
31
  # include CarrierWave::MiniMagick
@@ -31,23 +33,22 @@ module CarrierWave
31
33
  # process :radial_blur => 10
32
34
  #
33
35
  # def radial_blur(amount)
34
- # manipulate! do |img|
35
- # img.radial_blur(amount)
36
- # img = yield(img) if block_given?
37
- # img
36
+ # minimagick! do |builder|
37
+ # builder.radial_blur(amount)
38
+ # builder = yield(builder) if block_given?
39
+ # builder
38
40
  # end
39
41
  # end
40
42
  # end
41
43
  #
42
44
  # === Note
43
45
  #
44
- # MiniMagick is a mini replacement for RMagick that uses the command line
45
- # tool "mogrify" for image manipulation.
46
+ # The ImageProcessing gem uses MiniMagick, a mini replacement for RMagick
47
+ # that uses ImageMagick command-line tools, to build a "convert" command that
48
+ # performs the processing.
46
49
  #
47
50
  # You can find more information here:
48
51
  #
49
- # http://mini_magick.rubyforge.org/
50
- # and
51
52
  # https://github.com/minimagick/minimagick/
52
53
  #
53
54
  #
@@ -55,19 +56,7 @@ module CarrierWave
55
56
  extend ActiveSupport::Concern
56
57
 
57
58
  included do
58
- begin
59
- require "mini_magick"
60
- rescue LoadError => e
61
- e.message << " (You may need to install the mini_magick gem)"
62
- raise e
63
- end
64
-
65
- prepend Module.new {
66
- def initialize(*)
67
- super
68
- @format = nil
69
- end
70
- }
59
+ require "image_processing/mini_magick"
71
60
  end
72
61
 
73
62
  module ClassMethods
@@ -99,7 +88,7 @@ module CarrierWave
99
88
  #
100
89
  # === Parameters
101
90
  #
102
- # [format (#to_s)] an abreviation of the format
91
+ # [format (#to_s)] an abbreviation of the format
103
92
  #
104
93
  # === Yields
105
94
  #
@@ -109,12 +98,11 @@ module CarrierWave
109
98
  #
110
99
  # image.convert(:png)
111
100
  #
112
- def convert(format, page=nil)
113
- @format = format
114
- @page = page
115
- manipulate! do |img|
116
- img = yield(img) if block_given?
117
- img
101
+ def convert(format, page=nil, &block)
102
+ minimagick!(block) do |builder|
103
+ builder = builder.convert(format)
104
+ builder = builder.loader(page: page) if page
105
+ builder
118
106
  end
119
107
  end
120
108
 
@@ -128,21 +116,18 @@ module CarrierWave
128
116
  #
129
117
  # [width (Integer)] the width to scale the image to
130
118
  # [height (Integer)] the height to scale the image to
119
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
131
120
  #
132
121
  # === Yields
133
122
  #
134
123
  # [MiniMagick::Image] additional manipulations to perform
135
124
  #
136
- def resize_to_limit(width, height, combine_options: {})
137
- width = dimension_from width
138
- height = dimension_from height
139
- manipulate! do |img|
140
- img.combine_options do |cmd|
141
- cmd.resize "#{width}x#{height}>"
142
- append_combine_options cmd, combine_options
143
- end
144
- img = yield(img) if block_given?
145
- img
125
+ def resize_to_limit(width, height, combine_options: {}, &block)
126
+ width, height = resolve_dimensions(width, height)
127
+
128
+ minimagick!(block) do |builder|
129
+ builder.resize_to_limit(width, height)
130
+ .apply(combine_options)
146
131
  end
147
132
  end
148
133
 
@@ -155,21 +140,18 @@ module CarrierWave
155
140
  #
156
141
  # [width (Integer)] the width to scale the image to
157
142
  # [height (Integer)] the height to scale the image to
143
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
158
144
  #
159
145
  # === Yields
160
146
  #
161
147
  # [MiniMagick::Image] additional manipulations to perform
162
148
  #
163
- def resize_to_fit(width, height, combine_options: {})
164
- width = dimension_from width
165
- height = dimension_from height
166
- manipulate! do |img|
167
- img.combine_options do |cmd|
168
- cmd.resize "#{width}x#{height}"
169
- append_combine_options cmd, combine_options
170
- end
171
- img = yield(img) if block_given?
172
- img
149
+ def resize_to_fit(width, height, combine_options: {}, &block)
150
+ width, height = resolve_dimensions(width, height)
151
+
152
+ minimagick!(block) do |builder|
153
+ builder.resize_to_fit(width, height)
154
+ .apply(combine_options)
173
155
  end
174
156
  end
175
157
 
@@ -183,37 +165,18 @@ module CarrierWave
183
165
  # [width (Integer)] the width to scale the image to
184
166
  # [height (Integer)] the height to scale the image to
185
167
  # [gravity (String)] the current gravity suggestion (default: 'Center'; options: 'NorthWest', 'North', 'NorthEast', 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast')
168
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
186
169
  #
187
170
  # === Yields
188
171
  #
189
172
  # [MiniMagick::Image] additional manipulations to perform
190
173
  #
191
- def resize_to_fill(width, height, gravity = 'Center', combine_options: {})
192
- width = dimension_from width
193
- height = dimension_from height
194
- manipulate! do |img|
195
- cols, rows = img[:dimensions]
196
- img.combine_options do |cmd|
197
- if width != cols || height != rows
198
- scale_x = width/cols.to_f
199
- scale_y = height/rows.to_f
200
- if scale_x >= scale_y
201
- cols = (scale_x * (cols + 0.5)).round
202
- rows = (scale_x * (rows + 0.5)).round
203
- cmd.resize "#{cols}"
204
- else
205
- cols = (scale_y * (cols + 0.5)).round
206
- rows = (scale_y * (rows + 0.5)).round
207
- cmd.resize "x#{rows}"
208
- end
209
- end
210
- cmd.gravity gravity
211
- cmd.background "rgba(255,255,255,0.0)"
212
- cmd.extent "#{width}x#{height}" if cols != width || rows != height
213
- append_combine_options cmd, combine_options
214
- end
215
- img = yield(img) if block_given?
216
- img
174
+ def resize_to_fill(width, height, gravity = 'Center', combine_options: {}, &block)
175
+ width, height = resolve_dimensions(width, height)
176
+
177
+ minimagick!(block) do |builder|
178
+ builder.resize_to_fill(width, height, gravity: gravity)
179
+ .apply(combine_options)
217
180
  end
218
181
  end
219
182
 
@@ -232,28 +195,18 @@ module CarrierWave
232
195
  # [height (Integer)] the height to scale the image to
233
196
  # [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
234
197
  # [gravity (String)] how to position the image
198
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
235
199
  #
236
200
  # === Yields
237
201
  #
238
202
  # [MiniMagick::Image] additional manipulations to perform
239
203
  #
240
- def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {})
241
- width = dimension_from width
242
- height = dimension_from height
243
- manipulate! do |img|
244
- img.combine_options do |cmd|
245
- cmd.thumbnail "#{width}x#{height}>"
246
- if background == :transparent
247
- cmd.background "rgba(255, 255, 255, 0.0)"
248
- else
249
- cmd.background background
250
- end
251
- cmd.gravity gravity
252
- cmd.extent "#{width}x#{height}"
253
- append_combine_options cmd, combine_options
254
- end
255
- img = yield(img) if block_given?
256
- img
204
+ def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {}, &block)
205
+ width, height = resolve_dimensions(width, height)
206
+
207
+ minimagick!(block) do |builder|
208
+ builder.resize_and_pad(width, height, background: background, gravity: gravity)
209
+ .apply(combine_options)
257
210
  end
258
211
  end
259
212
 
@@ -284,6 +237,9 @@ module CarrierWave
284
237
  # and then pass each of its frames to the supplied block. It will then
285
238
  # save the image to disk.
286
239
  #
240
+ # NOTE: This method exists mostly for backwards compatibility, you should
241
+ # probably use #minimagick!.
242
+ #
287
243
  # === Gotcha
288
244
  #
289
245
  # This method assumes that the object responds to +current_path+.
@@ -303,46 +259,75 @@ module CarrierWave
303
259
  cache_stored_file! if !cached?
304
260
  image = ::MiniMagick::Image.open(current_path)
305
261
 
306
- begin
307
- image.format(@format.to_s.downcase, @page) if @format
308
- image = yield(image)
309
- image.write(current_path)
262
+ image = yield(image)
263
+ FileUtils.mv image.path, current_path
310
264
 
311
- if @format
312
- move_to = current_path.chomp(File.extname(current_path)) + ".#{@format}"
313
- file.content_type = ::MIME::Types.type_for(move_to).first.to_s
314
- file.move_to(move_to, permissions, directory_permissions)
315
- end
316
-
317
- image.run_command("identify", current_path)
318
- ensure
319
- image.destroy!
320
- end
265
+ image.run_command("identify", current_path)
321
266
  rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
322
- message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e)
267
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
268
+ message = I18n.translate(:"errors.messages.processing_error")
323
269
  raise CarrierWave::ProcessingError, message
270
+ ensure
271
+ image.destroy! if image
324
272
  end
325
273
 
326
- private
274
+ # Process the image with MiniMagick, using the ImageProcessing gem. This
275
+ # method will build a "convert" ImageMagick command and execute it on the
276
+ # current image.
277
+ #
278
+ # === Gotcha
279
+ #
280
+ # This method assumes that the object responds to +current_path+.
281
+ # Any class that this module is mixed into must have a +current_path+ method.
282
+ # CarrierWave::Uploader does, so you won't need to worry about this in
283
+ # most cases.
284
+ #
285
+ # === Yields
286
+ #
287
+ # [ImageProcessing::Builder] use it to define processing to be performed
288
+ #
289
+ # === Raises
290
+ #
291
+ # [CarrierWave::ProcessingError] if processing failed.
292
+ def minimagick!(block = nil)
293
+ builder = ImageProcessing::MiniMagick.source(current_path)
294
+ builder = yield(builder)
295
+
296
+ result = builder.call
297
+ result.close
327
298
 
328
- def append_combine_options(cmd, combine_options)
329
- combine_options.each do |method, options|
330
- if options.nil?
331
- cmd.send(method)
332
- else
333
- cmd.send(method, options)
334
- end
335
- end
299
+ # backwards compatibility (we want to eventually move away from MiniMagick::Image)
300
+ if block
301
+ image = ::MiniMagick::Image.new(result.path, result)
302
+ image = block.call(image)
303
+ result = image.instance_variable_get(:@tempfile)
336
304
  end
337
305
 
338
- def dimension_from(value)
339
- return value unless value.instance_of?(Proc)
340
- value.arity >= 1 ? value.call(self) : value.call
306
+ FileUtils.mv result.path, current_path
307
+
308
+ if File.extname(result.path) != File.extname(current_path)
309
+ move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
310
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
311
+ file.move_to(move_to, permissions, directory_permissions)
341
312
  end
313
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
314
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
315
+ message = I18n.translate(:"errors.messages.processing_error")
316
+ raise CarrierWave::ProcessingError, message
317
+ end
318
+
319
+ private
342
320
 
343
- def mini_magick_image
344
- ::MiniMagick::Image.read(read)
321
+ def resolve_dimensions(*dimensions)
322
+ dimensions.map do |value|
323
+ next value unless value.instance_of?(Proc)
324
+ value.arity >= 1 ? value.call(self) : value.call
345
325
  end
326
+ end
327
+
328
+ def mini_magick_image
329
+ ::MiniMagick::Image.read(read)
330
+ end
346
331
 
347
332
  end # MiniMagick
348
333
  end # CarrierWave
@@ -62,10 +62,12 @@ module CarrierWave
62
62
  begin
63
63
  require "rmagick"
64
64
  rescue LoadError
65
- require "RMagick"
66
- rescue LoadError => e
67
- e.message << " (You may need to install the rmagick gem)"
68
- raise e
65
+ begin
66
+ require "RMagick"
67
+ rescue LoadError => e
68
+ e.message << " (You may need to install the rmagick gem)"
69
+ raise e
70
+ end
69
71
  end
70
72
 
71
73
  prepend Module.new {
@@ -109,7 +111,7 @@ module CarrierWave
109
111
  #
110
112
  # === Parameters
111
113
  #
112
- # [format (#to_s)] an abreviation of the format
114
+ # [format (#to_s)] an abbreviation of the format
113
115
  #
114
116
  # === Yields
115
117
  #
@@ -228,13 +230,7 @@ module CarrierWave
228
230
  height = dimension_from height
229
231
  manipulate! do |img|
230
232
  img.resize_to_fit!(width, height)
231
- new_img = ::Magick::Image.new(width, height) { self.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
232
- if background == :transparent
233
- filled = new_img.matte_floodfill(1, 1)
234
- else
235
- filled = new_img.color_floodfill(1, 1, ::Magick::Pixel.from_color(background))
236
- end
237
- destroy_image(new_img)
233
+ filled = ::Magick::Image.new(width, height) { |image| image.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
238
234
  filled.composite!(img, gravity, ::Magick::OverCompositeOp)
239
235
  destroy_image(img)
240
236
  filled = yield(filled) if block_given?
@@ -363,15 +359,15 @@ module CarrierWave
363
359
  if options[:format] || @format
364
360
  frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
365
361
  move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
366
- file.content_type = ::MIME::Types.type_for(move_to).first.to_s
362
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
367
363
  file.move_to(move_to, permissions, directory_permissions)
368
364
  else
369
365
  frames.write(current_path, &write_block)
370
366
  end
371
367
 
372
368
  destroy_image(frames)
373
- rescue ::Magick::ImageMagickError => e
374
- raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e)
369
+ rescue ::Magick::ImageMagickError
370
+ raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.processing_error")
375
371
  end
376
372
 
377
373
  private
@@ -0,0 +1,284 @@
1
+ module CarrierWave
2
+
3
+ ##
4
+ # This module simplifies manipulation with vips by providing a set
5
+ # of convenient helper methods. If you want to use them, you'll need to
6
+ # require this file:
7
+ #
8
+ # require 'carrierwave/processing/vips'
9
+ #
10
+ # And then include it in your uploader:
11
+ #
12
+ # class MyUploader < CarrierWave::Uploader::Base
13
+ # include CarrierWave::Vips
14
+ # end
15
+ #
16
+ # You can now use the provided helpers:
17
+ #
18
+ # class MyUploader < CarrierWave::Uploader::Base
19
+ # include CarrierWave::Vips
20
+ #
21
+ # process :resize_to_fit => [200, 200]
22
+ # end
23
+ #
24
+ # Or create your own helpers with the powerful vips! method, which
25
+ # yields an ImageProcessing::Builder object. Check out the ImageProcessing
26
+ # docs at http://github.com/janko-m/image_processing and the list of all
27
+ # available Vips options at
28
+ # https://libvips.github.io/libvips/API/current/using-cli.html for more info.
29
+ #
30
+ # class MyUploader < CarrierWave::Uploader::Base
31
+ # include CarrierWave::Vips
32
+ #
33
+ # process :radial_blur => 10
34
+ #
35
+ # def radial_blur(amount)
36
+ # vips! do |builder|
37
+ # builder.radial_blur(amount)
38
+ # builder = yield(builder) if block_given?
39
+ # builder
40
+ # end
41
+ # end
42
+ # end
43
+ #
44
+ # === Note
45
+ #
46
+ # The ImageProcessing gem uses ruby-vips, a binding for the vips image
47
+ # library. You can find more information here:
48
+ #
49
+ # https://github.com/libvips/ruby-vips
50
+ #
51
+ #
52
+ module Vips
53
+ extend ActiveSupport::Concern
54
+
55
+ included do
56
+ require "image_processing/vips"
57
+ # We need to disable caching since we're editing images in place.
58
+ ::Vips.cache_set_max(0)
59
+ end
60
+
61
+ module ClassMethods
62
+ def convert(format)
63
+ process :convert => format
64
+ end
65
+
66
+ def resize_to_limit(width, height)
67
+ process :resize_to_limit => [width, height]
68
+ end
69
+
70
+ def resize_to_fit(width, height)
71
+ process :resize_to_fit => [width, height]
72
+ end
73
+
74
+ def resize_to_fill(width, height, gravity='centre')
75
+ process :resize_to_fill => [width, height, gravity]
76
+ end
77
+
78
+ def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil)
79
+ process :resize_and_pad => [width, height, background, gravity, alpha]
80
+ end
81
+ end
82
+
83
+ ##
84
+ # Changes the image encoding format to the given format
85
+ #
86
+ # See https://libvips.github.io/libvips/API/current/using-cli.html#using-command-line-conversion
87
+ #
88
+ # === Parameters
89
+ #
90
+ # [format (#to_s)] an abbreviation of the format
91
+ #
92
+ # === Yields
93
+ #
94
+ # [Vips::Image] additional manipulations to perform
95
+ #
96
+ # === Examples
97
+ #
98
+ # image.convert(:png)
99
+ #
100
+ def convert(format, page=nil)
101
+ vips! do |builder|
102
+ builder = builder.convert(format)
103
+ builder = builder.loader(page: page) if page
104
+ builder
105
+ end
106
+ end
107
+
108
+ ##
109
+ # Resize the image to fit within the specified dimensions while retaining
110
+ # the original aspect ratio. Will only resize the image if it is larger than the
111
+ # specified dimensions. The resulting image may be shorter or narrower than specified
112
+ # in the smaller dimension but will not be larger than the specified values.
113
+ #
114
+ # === Parameters
115
+ #
116
+ # [width (Integer)] the width to scale the image to
117
+ # [height (Integer)] the height to scale the image to
118
+ # [combine_options (Hash)] additional Vips options to apply before resizing
119
+ #
120
+ # === Yields
121
+ #
122
+ # [Vips::Image] additional manipulations to perform
123
+ #
124
+ def resize_to_limit(width, height, combine_options: {})
125
+ width, height = resolve_dimensions(width, height)
126
+
127
+ vips! do |builder|
128
+ builder.resize_to_limit(width, height)
129
+ .apply(combine_options)
130
+ end
131
+ end
132
+
133
+ ##
134
+ # Resize the image to fit within the specified dimensions while retaining
135
+ # the original aspect ratio. The image may be shorter or narrower than
136
+ # specified in the smaller dimension but will not be larger than the specified values.
137
+ #
138
+ # === Parameters
139
+ #
140
+ # [width (Integer)] the width to scale the image to
141
+ # [height (Integer)] the height to scale the image to
142
+ # [combine_options (Hash)] additional Vips options to apply before resizing
143
+ #
144
+ # === Yields
145
+ #
146
+ # [Vips::Image] additional manipulations to perform
147
+ #
148
+ def resize_to_fit(width, height, combine_options: {})
149
+ width, height = resolve_dimensions(width, height)
150
+
151
+ vips! do |builder|
152
+ builder.resize_to_fit(width, height)
153
+ .apply(combine_options)
154
+ end
155
+ end
156
+
157
+ ##
158
+ # Resize the image to fit within the specified dimensions while retaining
159
+ # the aspect ratio of the original image. If necessary, crop the image in the
160
+ # larger dimension.
161
+ #
162
+ # === Parameters
163
+ #
164
+ # [width (Integer)] the width to scale the image to
165
+ # [height (Integer)] the height to scale the image to
166
+ # [combine_options (Hash)] additional vips options to apply before resizing
167
+ #
168
+ # === Yields
169
+ #
170
+ # [Vips::Image] additional manipulations to perform
171
+ #
172
+ def resize_to_fill(width, height, _gravity = nil, combine_options: {})
173
+ width, height = resolve_dimensions(width, height)
174
+
175
+ vips! do |builder|
176
+ builder.resize_to_fill(width, height).apply(combine_options)
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Resize the image to fit within the specified dimensions while retaining
182
+ # the original aspect ratio. If necessary, will pad the remaining area
183
+ # with the given color, which defaults to transparent (for gif and png,
184
+ # white for jpeg).
185
+ #
186
+ # See https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsCompassDirection
187
+ # for gravity options.
188
+ #
189
+ # === Parameters
190
+ #
191
+ # [width (Integer)] the width to scale the image to
192
+ # [height (Integer)] the height to scale the image to
193
+ # [background (List, nil)] the color of the background as a RGB, like [0, 255, 255], nil indicates transparent
194
+ # [gravity (String)] how to position the image
195
+ # [alpha (Boolean, nil)] pad the image with the alpha channel if supported
196
+ # [combine_options (Hash)] additional vips options to apply before resizing
197
+ #
198
+ # === Yields
199
+ #
200
+ # [Vips::Image] additional manipulations to perform
201
+ #
202
+ def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil, combine_options: {})
203
+ width, height = resolve_dimensions(width, height)
204
+
205
+ vips! do |builder|
206
+ builder.resize_and_pad(width, height, background: background, gravity: gravity, alpha: alpha)
207
+ .apply(combine_options)
208
+ end
209
+ end
210
+
211
+ ##
212
+ # Returns the width of the image in pixels.
213
+ #
214
+ # === Returns
215
+ #
216
+ # [Integer] the image's width in pixels
217
+ #
218
+ def width
219
+ vips_image.width
220
+ end
221
+
222
+ ##
223
+ # Returns the height of the image in pixels.
224
+ #
225
+ # === Returns
226
+ #
227
+ # [Integer] the image's height in pixels
228
+ #
229
+ def height
230
+ vips_image.height
231
+ end
232
+
233
+ # Process the image with vip, using the ImageProcessing gem. This
234
+ # method will build a "convert" vips command and execute it on the
235
+ # current image.
236
+ #
237
+ # === Gotcha
238
+ #
239
+ # This method assumes that the object responds to +current_path+.
240
+ # Any class that this module is mixed into must have a +current_path+ method.
241
+ # CarrierWave::Uploader does, so you won't need to worry about this in
242
+ # most cases.
243
+ #
244
+ # === Yields
245
+ #
246
+ # [ImageProcessing::Builder] use it to define processing to be performed
247
+ #
248
+ # === Raises
249
+ #
250
+ # [CarrierWave::ProcessingError] if processing failed.
251
+ def vips!
252
+ builder = ImageProcessing::Vips.source(current_path)
253
+ builder = yield(builder)
254
+
255
+ result = builder.call
256
+ result.close
257
+
258
+ FileUtils.mv result.path, current_path
259
+
260
+ if File.extname(result.path) != File.extname(current_path)
261
+ move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
262
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
263
+ file.move_to(move_to, permissions, directory_permissions)
264
+ end
265
+ rescue ::Vips::Error
266
+ message = I18n.translate(:"errors.messages.processing_error")
267
+ raise CarrierWave::ProcessingError, message
268
+ end
269
+
270
+ private
271
+
272
+ def resolve_dimensions(*dimensions)
273
+ dimensions.map do |value|
274
+ next value unless value.instance_of?(Proc)
275
+ value.arity >= 1 ? value.call(self) : value.call
276
+ end
277
+ end
278
+
279
+ def vips_image
280
+ ::Vips::Image.new_from_buffer(read, "")
281
+ end
282
+
283
+ end # Vips
284
+ end # CarrierWave
@@ -1,2 +1,3 @@
1
1
  require "carrierwave/processing/rmagick"
2
2
  require "carrierwave/processing/mini_magick"
3
+ require "carrierwave/processing/vips"