Converting Image Format When Thumbnailing

Recently I built a screen where the user can upload multiple images with SWFUpload and attachment_fu. In this case the user is an award-winning photographer and he uploads images in a wide variety of formats. So far, so good.

The catch is his wanting the thumbnail the site generates for each image to be a JPEG. So if he uploads a CR2, the site should generate a JPEG thumbnail (while leaving the original intact).

This is so that every browser can display thumbnails of his images regardless of their original formats. Sensible.

It sounds simple but, as I often find with attachment_fu, getting it working was harder than I anticipated. There may well be a more elegant solution than the code below. I’m sure you could generalise it to other formats and other image processors, pluginise it, and so on. However my code does have the virtue of working.

Assumptions

The Code

Let me know if you improve this.

class Asset < ActiveRecord::Base 
  has_attachment :storage    => :s3,
                 :thumbnails => {:thumb => '100x'}

  # All the image formats that should be thumbnailed
  # and converted to JPEG.  Must contain jpg and jpeg.
  @@converts = %w( gif jpg jpeg png tif tiff bmp cr2 crw )
  @@converts << %w( eps pcd pdf pef pict psd raf )
  @@converts.flatten!

  before_thumbnail_saved do |record, thumbnail|
    thumbnail.content_type = 'image/jpeg' if convertable? thumbnail.filename
  end

  def resize_image(img, size)
    img.format = 'JPEG' if convertable? thumbnail_name_for(thumbnail)
    super
  end

  def convertable?(name)
    self.class.convertable? name
  end

  def self.convertable?(name)
    ext = File.extname(name)[1..-1]
    @@converts.include?(ext) && rmagick_can_read_extension?(ext)
  end

  def self.rmagick_can_read_extension?(extension)
    # Convert extensions to ones that RMagick recognises.
    ext = case extension
          when 'tif': 'tiff'
          else extension
          end
    rmagick_can_read_format? ext.upcase
  end

  def self.rmagick_can_read_format?(format)
    code = Magick.formats[format]
    # Determine whether RMagick knows how to read files in this format.
    # TODO: test native blob support?  p.416 Ruby Cookbook.
    code && code[1] == ?r
  end

  def image?
    super || convertable?(filename)
  end

  def thumbnail_name_for(thumbnail = nil)
    name = super
    name.sub!(/\.\w+$/, '.jpg') if name.match(/_thumb/) && convertable?(name)
    name
  end
end
Andy Stewart, 15 January 2008

Posted in Rails


Have your say

You can use Markdown in your comments. If you want to post code, do this:

<pre><code class="ruby|javascript|css|html">your code here</code></pre>

Thanks!