views:

3536

answers:

3

Hey all, I'm currently trying to code a custom image cropping system similar to other ones on the internet where a user can select a cropping area and then have their image cropped accordingly. The application is in Rails and we're using Paperclip with Amazon S3 to store the files. I'm having a lot of trouble though getting RMagick to appropriately crop the file from S3. Here is the current code (which does not work):

   if params[:width].to_i > 0 and params[:height].to_i > 0 then
      photo = Photo.find(params[:id])
      image_data = Net::HTTP.get_response(URI.parse(photo.photo.url(:big))).body
      orig_img = Magick::ImageList.new
      orig_img.from_blob(image_data)

      args = [params[:x1].to_i, params[:y1].to_i, params[:width].to_i, params[:height].to_i]
      orig_img.crop!(*args)
      photo.update_attributes({:photo => orig_img.to_blob})

      photo.photo.reprocess!
      photo.save
    end

The main problem is that the cropped image is not uploaded back to S3 through paperclip, and thus not properly cropped. Has anyone attempted something like this with paperclip before? This may not even possible, but any help would be greatly appreciated.

A: 

You could define some attr_accessors on the model and set them in the controller action before creating the actual file. The below should work ..

class Poodle < ActiveRecord::Base
  has_attached_file :avatar, :styles => Proc.new{|a| a.instance.get_styles}
  attr_accessor :width, :height


  def get_styles(style = "medium")
    return {style.to_sym => [self.width, self.height].join("x") + ">" }
  end
 end
james
The problem with this is that it doesn't take into account the cropping start coordinates (x1, y1), just the width and height.
Eric Scrivner
+3  A: 

I've been able to solve this problem for the time being as follows:

  photo = Photo.find(params[:id])
  image_data = Net::HTTP.get_response(URI.parse(photo.photo.url(:big))).body
  orig_img = Magick::ImageList.new
  orig_img.from_blob(image_data)

  args = [params[:x1].to_i, params[:y1].to_i, params[:width].to_i, params[:height].to_i]
  orig_img.crop!(*args)

  tmp_img = Tempfile.new("image_data")
  orig_img.format = "png"
  tmp_img.write(orig_img.to_blob)
  photo.photo = tmp_img
  tmp_img.close

  photo.photo.reprocess!
  photo.save

The main concern here is the speed hit of creating a new temporary file, so probably we will have to create our own custom interface. Hopefully this can be a good starting point for people solving a similar issue.

EDIT: Just a quick note, Tempfile is part of the paperclip libraries.

Eric Scrivner
A: 

You may want to take a closer look at Paperclip processors.

I actually have the same problem in my own code, and have that on my todo list.

Daniel Schierbeck
Paperclip processors won't really work in a flexible way for this as I need parameters passed in the params hash to properly crop and there is no real way to pass parameters to processors in paperclip.
Eric Scrivner
You can set these parameters as attributes on the model, and access it in your processor by calling `attachment.instance` in your `#make` method.
Daniel Schierbeck