index.md (3247B)
1 --- 2 title: "Manual Cropping with Paperclip" 3 date: 2012-05-31T00:00:00+00:00 4 draft: false 5 canonical_url: https://www.viget.com/articles/manual-cropping-with-paperclip/ 6 --- 7 8 It's relatively straightforward to add basic manual (browser-based) 9 cropping support to your 10 [Paperclip](https://github.com/thoughtbot/paperclip) image attachments. 11 See [RJCrop](https://github.com/jschwindt/rjcrop) for one valid 12 approach. What's not so straightforward, though, is adding manual 13 cropping while preserving Paperclip's built-in thumbnailing 14 capabilities. Here's how. 15 16 Just so we're on the same page, when we're talking about "thumbnailing," 17 we're talking about the ability to set a size of `50x50#`, which means 18 "scale and crop the image into a 50 by 50 pixel square." If the original 19 image is 200x100, it would first be scaled down to 100x50, and then 25 20 pixels trimmed from both sides to arrive at the final dimensions. This 21 is not a native capability of ImageMagick, but rather the result of some 22 decently complex code in Paperclip. 23 24 Our goal is to allow a user to select a portion of an image and then 25 create a thumbnail of *just that selected portion*, ideally taking 26 advantage of Paperclip's existing cropping/scaling logic. 27 28 Any time you're dealing with custom Paperclip image processing, you're 29 talking about creating a custom 30 [Processor](https://github.com/thoughtbot/paperclip#post-processing). In 31 this case, we'll be subclassing the default 32 [Thumbnail](https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/thumbnail.rb) 33 processor and making a few small tweaks. We'll imagine you have a model 34 with the fields `crop_x`, `crop_y`, `crop_width`, and `crop_height`. How 35 those get set is left as an exercise for the reader (though I recommend 36 [JCrop](http://deepliquid.com/content/Jcrop.html)). Some code, then: 37 38 ```ruby 39 module Paperclip 40 class ManualCropper < Thumbnail 41 def initialize(file, options = {}, attachment = nil) 42 super 43 @current_geometry.width = target.crop_width 44 @current_geometry.height = target.crop_height 45 end 46 47 def target 48 @attachment.instance 49 end 50 51 def transformation_command 52 crop_command = [ 53 "-crop", 54 "#{target.crop_width}x" 55 "#{target.crop_height}+" 56 "#{target.crop_x}+" 57 "#{target.crop_y}", 58 "+repage" 59 ] 60 61 crop_command + super 62 end 63 end 64 end 65 ``` 66 67 In our `initialize` method, we call super, which sets a whole host of 68 instance variables, include `@current_geometry`, which is responsible 69 for creating the geometry string that will crop and scale our image. We 70 then set its `width` and `height` to be the dimensions of our cropped 71 image. 72 73 We also override the `transformation_command` method, prepending our 74 manual crop to the instructions provided by `@current_geometry`. The end 75 result is a geometry string which crops the image, repages it, then 76 scales the image and crops it a second time. Simple, but not certainly 77 not intuitive, at least not to me. 78 79 From here, you can include this cropper using the `:processors` 80 directive in your `has_attached_file` declaration, and you should be 81 good to go. This simple approach assumes that the crop dimensions will 82 always be set, so tweak accordingly if that's not the case.