3
votes

I have ran into a tricky problem with paperclip and custom processors where I'm loosing some of the styles.

I've got a picture which I crop with coordinates passed from the view. Cropping went well and original picture is saved but when running reprocess to make the other styles I get this error:

convert: geometry does not contain image `/var/folders/bg/w2mft0x51t933pzhgpvhm5800000gn/T/paperclip-reprocess20111114-3404-13lp780-0' @ warning/transform.c/CropImage/571.

And when I get this error the original image style is lost.

I tried toying around callbacks and paperclip stuff but with no success. I'm really stuck and can't find any info on the matter.

The example I followed is from http://railscasts.com/episodes/182-cropping-images

What is different from the example in the screencast is that I want to crop the original and then generate thumbnails from it with the standard paperclip styles.

Here is my code.

My processor:

module Paperclip

  class Cropper < Thumbnail
    def transformation_command
      if crop_command
        original_command = super
        if original_command.include?('-crop')
          original_command.delete_at(super.index('-crop') + 1)
          original_command.delete_at(super.index('-crop'))
        end
        crop_command + original_command
      else
        super
      end
    end

    def crop_command
      target = @attachment.instance
      if target.cropping?
        ["-crop", "#{target.crop_w.to_i}x#{target.crop_h.to_i}+#{target.crop_x.to_i}+#{target.crop_y.to_i}", "+repage"]
      end
    end
  end

end

My model:

class Item < ActiveRecord::Base

  belongs_to :category
  belongs_to :picture

  validates :picture_id,  :presence => true
  validates :category_id, :presence => true

  validates :title_url,   :presence => true, :uniqueness => { :case_sensitive => false }
  validates :title,       :presence => true, :uniqueness => { :case_sensitive => false }
  validates :information, :presence => true, :uniqueness => { :case_sensitive => false }

  validates :crop_x,      :presence => true, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0 }
  validates :crop_y,      :presence => true, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0 }
  validates :crop_w,      :presence => true, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0 }
  validates :crop_h,      :presence => true, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0 }

  Paperclip.interpolates :title_url do |attachment, style|
    attachment.instance.title_url
  end

  has_attached_file :image,
                    :styles => {
                      :small    => { :format => 'jpg', :quality => 100, :geometry => '100x100#' },
                      :medium   => { :format => 'jpg', :quality => 100, :geometry => '200x200#' },
                      :large    => { :format => 'jpg', :quality => 100, :geometry => '300x300#' },
                      :original => { :format => 'jpg', :quality => 100, :geometry => '', :processors => [:cropper] }
                    },
                    :path => ":rails_root/public/attachments/:class/:attachment/:id_partition/:style/:title_url.:extension",
                    :url  =>                   "/attachments/:class/:attachment/:id_partition/:style/:title_url.:extension"

  before_validation   :strip_attributes
  before_save         :image_assign,    :if => :cropping?
  after_create        :image_reprocess, :if => :cropping?

  def cropping?
    !crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?
  end

  def image_geometry(style = :original)
    @geometry ||= {}
    @geometry[style] ||= Paperclip::Geometry.from_file(avatar.path(style))
  end

  def calc(width,height)
    # original large side / smalled picture large side
    calc_width  = self.picture.width.to_f  / width
    calc_height = self.picture.height.to_f / height
    if calc_width >= calc_height
      return calc_width
    else
      return calc_height
    end
  end

  private

  def image_assign
    self.image = self.picture.image
  end

  def image_reprocess
    image.reprocess!
  end

  def strip_attributes
    # normalize attributes
    self.title.strip!
    self.title_url = Utility::friendly_url(self.title)
    self.information.strip!
  end

end
1
I'd take ":geometry => ''" out of :image => :styles => :original... Not sure if that would do anything but something must be causing the original not to save. hmmmdrhenner
Without geometry even a basic setup fails. It seems required when defining a style.YavorIvanov
I should clarify (if not clear above) that reprocess is breaking the original image. It seems fine when saving before the reprocess is called.YavorIvanov
For me it is before_validation that is causing issues with paperclip. I keep getting no method size for nil class >.<. Commenting out before_validation, everything runs fine for me.kikuchiyo

1 Answers

4
votes

Since I saw that at least one person other than me has bumped into problems I'm sharing my final solution to the problem of custom paperclip processor for cropping. There are some stuff like centering the picture cropping. I'm pretty sure this is far from perfect but it has worked reliably about a year now.

module Paperclip

  class CropperItem < Thumbnail
    def transformation_command
      if crop_command
        original_command = super
        if original_command.include?('-crop')
          original_command.delete_at(super.index('-crop') + 1)
          original_command.delete_at(super.index('-crop'))
        end
        if original_command.include?('-resize')
          crop_command('square') + original_command
        else
          crop_command + original_command
        end
      else
        super
      end
    end

    def crop_command(dimensions = nil)
      target = @attachment.instance
      if target.cropping?
        case dimensions
        when 'square'
          if target.crop_w > target.crop_h
            crop_w = target.crop_w.to_i
            crop_h = target.crop_w.to_i
            crop_x = target.crop_x.to_i
            crop_y = target.crop_y.to_i - ((target.crop_w.to_i-target.crop_h.to_i)/2).to_i
            crop_x = 0 if crop_x < 0
            crop_y = 0 if crop_y < 0
          elsif target.crop_w < target.crop_h
            crop_w = target.crop_h.to_i
            crop_h = target.crop_h.to_i
            crop_x = target.crop_x.to_i - ((target.crop_h.to_i-target.crop_w.to_i)/2).to_i
            crop_y = target.crop_y.to_i
            crop_x = 0 if crop_x < 0
            crop_y = 0 if crop_y < 0
          else
            crop_w = target.crop_w.to_i
            crop_h = target.crop_h.to_i
            crop_x = target.crop_x.to_i
            crop_y = target.crop_y.to_i
          end
          ["-crop", "#{crop_w}x#{crop_h}+#{crop_x}+#{crop_y}", "+repage"]
        else
          ["-crop", "#{target.crop_w.to_i}x#{target.crop_h.to_i}+#{target.crop_x.to_i}+#{target.crop_y.to_i}", "+repage"]
        end
      end
    end
  end

end

And I ended up using something like this in the model.

has_attached_file :photo,
                  :styles => {
                    :small        => { :format => 'jpg', :quality => 100, :geometry => '50x50^'   },
                    :thumb_small  => { :format => 'jpg', :quality => 100, :geometry => '110x110^' },
                    :thumb_medium => { :format => 'jpg', :quality => 100, :geometry => '150x150^' },
                    :medium       => { :format => 'jpg', :quality => 100, :geometry => '240x160^' },
                    :banner_small => { :format => 'jpg', :quality => 100, :geometry => '200x120#' },
                    :banner       => { :format => 'jpg', :quality => 100, :geometry => '300x250^' },
                    :focus_crop   => { :format => 'jpg', :quality => 100, :geometry => '400x400>' },
                    :focus_orig   => { :format => 'jpg', :quality => 100, :geometry => '' }
                  },
                  :convert_options => {
                    :thumb_small  => "-gravity Center -extent 50x50",
                    :thumb_small  => "-gravity Center -extent 110x110",
                    :thumb_medium => "-gravity Center -extent 150x150",
                    :medium       => "-gravity Center -extent 240x160",
                    :banner_small => "-gravity Center -extent 200x120",
                    :banner       => "-gravity Center -extent 300x250",
                    :focus_crop   => "-gravity Center"
                  },
                  :processors => [:cropper_item],
                  :path => PAPERCLIP_PATH,
                  :url  => PAPERCLIP_URL

I hope this helps someone.