Undefined Method crop! Using Carrierwave with MiniMagick on rails 3.1.3
Asked Answered
L

3

17

I was having a heck of a time getting this to work, and still am. I'll get to the heart of it. I'm following Ryan Bates tutorial to make cropping work using Jcrop and Carrierwave. I've opted to use MiniMagick because even after reinstalling ImageMagick and RMagick on my machine I get an error that kills the rails server on my local machine. Anyway switching to MiniMagick fixed that for me. So everything is really nice up until this point. I have different sized images being produced, and they're being uploaded successfully. But once I try to crop I get this error:

undefined method `crop!' for #<MiniMagick::CommandBuilder:0x000001052e4608>

This is confusing the heck out of me because I'm using pretty much the exact same code as Bates:

def crop
if model.crop_x.present?
  resize_to_limit(700, 700)
  manipulate! do |img|
    x = model.crop_x.to_i
    y = model.crop_y.to_i
    w = model.crop_w.to_i
    h = model.crop_h.to_i
    img.crop!(x, y, w, h)
  end
 end
end

Anyway, it's that crop method that's failing. So I thought to myself, that's an ImageMagick command... So I looked at the ImageMagick doco, and I couldn't find the crop method with the bang, so I tried it without, and then the error turns to this:

No such file or directory - /var/folders/dF/dFNM2+Y7FVScn4+OxVHKOU+++TI/-Tmp-/mini_magick20111207-34409-1tnaa07.jpg

Anyway, something isn't making a ton of sense to me, any help would be appreciated! Thanks for reading!

Lanie answered 7/12, 2011 at 16:38 Comment(1)
An update! I got very very frustrated, and tried everything over again on a new machine. I went back to Rmagick and it worked this time. The key thing seemed to be with my setup. I'm not sure specifically what, but it had something to do with Ghostscript and/or MacPorts. Removing macports, reinstalling imagemagick, reinstalling rmagick, reinstalling ghostcript finally did it for me! Thanks!Lanie
K
15

Had the same problems, my solution was this method

def cropped_image(params)
    image = MiniMagick::Image.open(self.image.path)
    crop_params = "#{params[:w]}x#{params[:h]}+#{params[:x]}+#{params[:y]}"
    image.crop(crop_params)

    image
end

Just modify my method for your case.
The key is in which format pass variables to crop method, hope this helps you.

Kalfas answered 7/12, 2011 at 16:56 Comment(3)
That looks really promising, I've just tried to update my crop method to include this image = MiniMagick::Image.open(model.pdf_url) But it's spitting an error at me. Any suggestions on the key I should be using? The error I get is No such file or directory - /uploads/tmp/20111207-1235-34409-8280/backsideart_stevie.jpgLanie
well, i suppose it's an issue wiht minimagick [github.com/probablycorey/mini_magick/issues/54]. I have it before, but it's dissapear when im rewrite crop method as above. It's weird that you still encounter it.Kalfas
Just to add ... Here's what I did from Mikhail's code. The important thing apparent (as he has rightly pointed out) is (1) replace crop! with crop and also return the image at the end of the manipulate! block otherwise you'll get write errors.ruby def crop if model.crop_x.present? resize_to_limit(600, 600) manipulate! do |img| x = model.crop_x.to_i y = model.crop_y.to_i w = model.crop_w.to_i h = model.crop_h.to_i img.crop("#{w}x#{h}+#{x}+#{y}") img end end end Tetzel
I
48

In short:

img.crop("#{size}#{offset}") # Doesn't return an image...
img # ...so you'll need to call it yourself

Here's a better explanation of why this happened as opposed to a cut/paste style solution.

RMagick and MiniMagick aren't interchangeable. RMagick has a very Ruby-like DSL and as such employs methods that take multiple arguments:

rmagick_image.crop(x_offset, y_offset, width, height) # Returns an image object
rmagick_image.crop!(x_offset, y_offset, width, height) # Edits object in place

MiniMagick instead dynamically generates methods by iterating through a list of MOGRIFY_COMMANDS that match up with numerous dash-prefixed options specified in ImageMagick's mogrify documentation. Each of those methods pass their arguments directly to mogrify and none return an image object:

minimagick_image.crop('100x200') # Translates to `mogrify -crop 100x200 image.ext`
minimagick_image.polaroid('12')  # Executes `mogrify -polaroid 12 image.ext`

In kind, RMagick has crop! and MiniMagick doesn't.

According to the ImageMagick docs, mogrify -crop takes an argument geometry. The geometry argument is explained here. You'll notice that all of those arguments are strings, so instead of crop(100,200) you would use crop('100x200') or crop('100%). It's not very Ruby-like, but that's part of what makes MiniMagick so lightweight.

With that knowledge, we can deduce how to crop with MiniMagick. mogrify -crop can take a geometry as a string widthxheight+xoffset+yoffset, so we just need to build a similar string.

Given w,h,x, and y you could use whichever of the following you find most readable:

# Concatenating plus signs with plus signs is atrociously confusing.
# Recommended only if you want to drive your future self insane.
mogrify_arg = w + 'x' + h + '+' + x + '+' + y

# Readable but inefficient
mogrify_arg = [ w, 'x', h, '+', x, '+', y ].join('')

# Questionable readability
mogrify_arg = "#{w}x#{h}+#{x}+#{y}"

# Slick, performant, but potentially risky: `<<` modifies the receiving object in place
# `w` is actually changing here to  "WxH+X+Y"...
mogrify_arg = w << 'x' << h << '+' << x << '+' << y

# A lovely, self-documenting version
size = w << 'x' << h
offset = '+' << x '+' << y
mogrify_arg = "#{size}#{offset}"

Here's a complete example:

def crop
  if model.crop_x.present?
    resize_to_limit(700, 700)

    manipulate! do |img|
      x = model.crop_x
      y = model.crop_y
      w = model.crop_w
      h = model.crop_h

      size = w << 'x' << h
      offset = '+' << x << '+' << y

      img.crop("#{size}#{offset}") # Doesn't return an image...
      img # ...so you'll need to call it yourself
    end

   end
  end
Infante answered 1/4, 2012 at 2:25 Comment(1)
@faraz wonderful explanation of something I was expecting to happen as I was looking at an rMagick example under consideration for a MiniMagick example. Only issu I had was with the size and offset which generated cannot convert String into Integer errors from Rails.Expression
K
15

Had the same problems, my solution was this method

def cropped_image(params)
    image = MiniMagick::Image.open(self.image.path)
    crop_params = "#{params[:w]}x#{params[:h]}+#{params[:x]}+#{params[:y]}"
    image.crop(crop_params)

    image
end

Just modify my method for your case.
The key is in which format pass variables to crop method, hope this helps you.

Kalfas answered 7/12, 2011 at 16:56 Comment(3)
That looks really promising, I've just tried to update my crop method to include this image = MiniMagick::Image.open(model.pdf_url) But it's spitting an error at me. Any suggestions on the key I should be using? The error I get is No such file or directory - /uploads/tmp/20111207-1235-34409-8280/backsideart_stevie.jpgLanie
well, i suppose it's an issue wiht minimagick [github.com/probablycorey/mini_magick/issues/54]. I have it before, but it's dissapear when im rewrite crop method as above. It's weird that you still encounter it.Kalfas
Just to add ... Here's what I did from Mikhail's code. The important thing apparent (as he has rightly pointed out) is (1) replace crop! with crop and also return the image at the end of the manipulate! block otherwise you'll get write errors.ruby def crop if model.crop_x.present? resize_to_limit(600, 600) manipulate! do |img| x = model.crop_x.to_i y = model.crop_y.to_i w = model.crop_w.to_i h = model.crop_h.to_i img.crop("#{w}x#{h}+#{x}+#{y}") img end end end Tetzel
W
2

I was able to get this to work by adding the X and Y parameters to the crop command as indicated by @mikhail-nikalyukin

def crop
  manipulate! do |img|
    img.crop "750x600+0+0"
    img.strip

    img = yield(img) if block_given?
    img
  end
end
Wiseman answered 23/1, 2012 at 22:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.