home about

IE PNG Fix in Rails

August 2nd, 2007 cpetersen

Introduction

PNGs are great, having a full 8 bits of alpha transparency makes designing web pages so much easier. That being said, I'm sure anyone reading this post is well aware of the gross inadequacies of Internet Explorer 6 in terms to the rendering of PNGs. IE7 has remedied the situation, however, according to Google Analytics, 6% of the visitors to this website still use IE6, and the situation is much worse for our company website where a full 37% of visitors still use IE6. My point is, if we want to take advantage of the features PNGs give us, we have to account for the IE6 users. We are in luck, koivi.com has a write up on how we can use the Microsoft specific "DXImageTransform.Microsoft.AlphaImageLoader" css filter to load PNGs properly. The short version is, our image tags that used to look like:

<img width="128" height="128" src="example.png" alt="Example"/>
must now look like (for IE6 only):

<img  alt="Example" width="128" height="128" src="spacer.gif" style="filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='example.png', sizingMethod='scale');" />
To make this process easier, I've written a Ruby on Rails method that handles the details for us.

png_tag

png_tag is the little brother of image_tag. It is actually two functions, first a function (ie?) to determine if the client is using IE, and if so what version. The second function (png_tag) acts just like image_tag, except when passed a png it will automatically use the DXImageTransform.Microsoft.AlphaImageLoader filter if the client is using IE6. The first function is as follows:
1
2
3
4
5
6
  def ie?
    m = /MSIE\s+([0-9, \.]+)/.match(request.user_agent)
    unless m.nil?
      m[1].to_f
    end
  end
This is a pretty rudimentary check, it just looks for "MSIE" in the user agent, if it finds it it returns the number after it (the version) otherwise it returns nil. Now that we have the ability to see if our user is using IE, we can tackle the second function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  def png_tag(source, options = {})
    options.symbolize_keys!

    options[:src] = image_path(source)
    options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize

    if options[:size]
      options[:width], options[:height] = options[:size].split("x") if options[:size] =~ %r{^\d+x\d+$}
      options.delete(:size)
    end

    if ie? && ie? < 7 && options[:src] =~ /png$/i
      src = options[:src]
      options[:style] = "filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='#{src}', sizingMethod='scale');"
      options[:src] = image_path('spacer.gif')
    end

    "<img #{tag_options(options) if options} />"
  end
If this code looks a lot like the code for "image_tag" that because it basically is "image_tag" with the IE6 check. If we find IE6, we simply change out the image source for a spacer and add the filter code. Since it also checks that the source image is a PNG before adding the filter, you can use png_tag everywhere you would have used image_tag.

My Image Doesn't Show Up in IE?!?

I forgot to mention, there is one caveat. The "DXImageTransform.Microsoft.AlphaImageLoader" doesn't automatically size your "img" tag. So your image is showing up, it just happens to be 1 pixel by 1 pixel (the size of our spacer). This just means you have to specify to the size of your image ahead of time (a good practice anyway). In the case where the programmer doesn't specify the size, you could use this code to determine it at run time. I decided not to for my code, but keep that in mind if it is a feature you really need.

Using png_tag

I thought about bundling this as a plugin, but it just seemed like overkill. If you want to use png_tag, simply copy the two functions above into your application_helper.rb file, and copy the following spacer into your images directory: right click here to save the 1x1 pixel spacer image. I hope you find this code useful!

9 Responses to “IE PNG Fix in Rails”

  1. Bojan Mihela? Says:
    Hi, thanks for tip, but I doubt it will work with any kind of caching since first variant would be cached.
  2. Christopher L Petersen Says:
    Hi Bojan, I've been using this technique for over 3 years (I originally wrote this as a JSP tag library) and I haven't witnessed a caching problem. If anyone does see this problem, please post here, maybe we can help! I have seen one problem with this method, if the PNG file isn't available, IE6 blocks while trying to download it. So if you have a separate image server that happens to be down, your website will appear very slow to IE6 users... oh yeah and it won't have images either. Thanks for the comment, Chris
  3. philipp Says:
    Thanks for a very useful add-on. Have you also tried the Painless PNG rails plugin? Best regards Philipp
  4. Jon Says:
    I had to remove the $ from the line 12 in the second method if ie? && ie?
  5. Jim Says:
    Hi Christopher, Love your fix for pngs! I have used it for several months and recently tried to use it for pngs generated dynamically out of a controller action. The problem is that I don't know what the image size will be ahead of time. So the png_tag method won't work and Sam Stephenson's clever/fast size function won't work either. Nor does the Painless PNG Plugin (same reason). There is another way to use Microsoft's AlphaImageLoader that does not require prior knowledge of the image size. I added the code to use it into your method. In case you or someone else ever needs to do sizeless png transparency here's the enhanced method: def png_tag(source, options = {}) options.symbolize_keys! options[:src] = image_path(source) options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize if options[:size] options[:width], options[:height] = options[:size].split("x") if options[:size] =~ %r{^\d+x\d+$} options.delete(:size) end if ie? && ie? " else content_tag("span", image_tag(options[:src], :style=>"filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0)"), :style=>"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=#{options[:src]}, sizingMethod='crop');", :CONTENTEDITABLE=>true) end else "" end end
  6. Jim Says:
    Arf! The code didn't show up very well, did it? If someone wants it email me at jim {at jimjames {dot org or I'll post on my blog at blog.MyTripScrapbook.com Thanks again for doing the heavy lifting in the first place! Jim.
  7. Jim Says:
    ooops, wrong blog! Right one: blog.p.latyp.us Sorry about that.
  8. Christopher L Petersen Says:
    Hi Jim, Thanks for the post! I think that code will be useful for a lot of people. Chris
  9. Dotty Says:
    thank you! I'll try this out!

Leave a Reply