Sunday, December 2, 2007

PNG transparancy in IE5.5/6.0 with no size

You've seen the very handy rails plugins for handling png transparency in IE 5.5 and IE 6.0: by Christopher Petersen and by The Rails Way. Don't forget Sam Stephenson's fast/clever function to get a png's size.

They all fail if you are dynamically generating a png image with transparency on the fly. But we have a fix! First some background.

You'd probably be doing this as a controller action that renders directly. The final line of your action probably looks like this:

render :text => slide_image, :status => 200, :content_type => 'image/png', :layout => false

I never figured out if you needed to actually register the png mime type for rails to handle this or if it's already registered, I simply did it anyway. Put this in your environment.rb:

Mime::Type.register "image/png", :png

I use the excellent methods from Christopher Petersen. Assay Depot has several very useful plugins on their blog - Christopher is the software guru there. So I've built on his work. His, and the other techniques, use Microsoft's AlphaImageLoader as part of a filter transform applied to the image. This is an IE-only style feature and is ignored by all other browsers. For safety, the code sniffs the browser and doesn't use the technique for other browsers. The filter usually displays the transformed image (with the correct alpha transparency) and the original image next to it and uses this to size the transformed image. To prevent this looking silly, Christopher's technique replaces the original image with a 1x1 transparent gif sized with the original image's dimensions. But what if you don't know the original image's dimensions?

There's another way to use Microsoft's AlphaImageLoader that does not require an image file size. The trick is to actually supply the image but use another transform filter to make it transparent, too. It requires a layout be applied to the image - usually width or height or position:absolute - all of which would not work for generalized image placement. However you can use the CONTENTEDITABLE html attribute as well. This attribute was invented by Microsoft and although it's part of the HTML 5 draft spec, no other browser has ever provided support for it. It has a couple drawbacks or side effects:
  • the image becomes part of the tab sequence. Not a big deal for me since I'm using the image in a link anyway.
  • the Web user can "edit" the image. The only real editing you can do is delete or resize the image for that rendering only.
If these are an issue, you might want to substitute a CSS style of postion:absolute instead.

Below is Christopher's method pair updated with code to use this technique if no size is supplied. Enjoy! Many thanks to Assay Depot for the initial work!

def png_tag(source, options = {})
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+$}
if ie? && ie? < source ="~" src =" options[:src]" src="'#{src}'," sizingmethod="'crop');" src="">"
:style=>"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=#{options[:src]}, sizingMethod='crop');",
"<img src="" />"

# Called by png_tag above.
def ie?
m = /MSIE\s+([0-9, \.]+)/.match(request.user_agent)
unless m.nil?
Ugh. If you can't read that code snippet very well, download it from here. I really must get a real code formatter for this blog.

No comments: