shrinerb / shrine

File Attachment toolkit for Ruby applications
https://shrinerb.com
MIT License
3.18k stars 275 forks source link

Method of attaining original image dimensions inside of derivatives_processor #572

Closed darylpdavies closed 2 years ago

darylpdavies commented 2 years ago

I'm trying to create an elaborate watermarked image inside of derivatives_processor, to be attached as a :watermarked version.

In order for the watermark to be cropped correctly, I need to attain the original file dimensions of the image being processed.

This doesn't work (understandably):

Attacher.derivatives_processor do |original|
    image = ImageProcessing::Vips.source(original)
    puts image.width
end

This also doesn't work:

Attacher.derivatives_processor do |original|
    puts original.width
end

Is it possible to use the vips library to get the original image dimensions? Something like this (doesn't work):

Attacher.derivatives_processor do |original|
    image = Vips::Image.new_from_file(original)
    puts image.width
end

Is it also possible to pull data from the original file's model? The uploader is attached to a model called "Photo" - can I do something like:

if Attacher.object.setting == "green"
    watermark = "#{Rails.root}/green-photo-watermark.png"
end
janko commented 2 years ago

The original file yielded to the block is a regular File object, and Vips::Image.new_from_file requires a path, so the following should work:

Attacher.derivatives_processor do |original|
  image = Vips::Image.new_from_file(original.path)
  puts image.width
end

The block itself is evaluated in the context of the Shrine::Attacher instance, which has access to the model instance via #record, so the following should work:

Attacher.derivatives_processor do |original|
  if record.setting == "green"
    # ...
  end
end

In the future, please post usage questions on the discourse channel, GitHub issues should only be used for bug reports.

jrochkind commented 2 years ago

Technically i believe in some cases the original can be an IO object that is not a File, depending on how you have configured your attacher and flow, so to be perfectly safe for all possible configuraitions you might want to do:

Attacher.derivatives_processor do |original|
  Shrine.with_file(original) do |local_file|
    image = Vips::Image.new_from_file(local_file.path)
    puts image.width
  end
end

The Shrine.with_file method is super useful and good to know about!

jrochkind commented 2 years ago

But also note that in many cases Shrine has already extracted the width and height from the original before it calls the derivative processor. So if you are willing to use/trust that which already is stored in metadata, you might not have to process the file again with vips to find it's original dimension.

You could instead do... uh, maybe it would be.... since you're in the context of a Shrine::Attacher instance, it think it'd be off the file method?

Attacher.derivatives_processor do |original|
   puts file.metadata[:width], file.metadata[:height]
   #...
end

Shrine is so flexible, it can be hard to keep track of all of your possible ways to do it, and how to do them!

darylpdavies commented 2 years ago

Thank you guys @janko @jrochkind, really appreciate your help. I tested out all of your feedback, and this is what ended up working for me:

Attacher.derivatives_processor do |original|
  image = Vips::Image.new_from_file(original.path.to_s)
  puts image.width
end

I have another app running Shrine that relies heavily on the metadata, didn't realize you could pull these attributes in via file.metadata[:attribute] will be making use of this feature.

Shrine is such an incredible tool for Ruby, don't know what I would do without it. Thanks guys!