This is as days pass by, by Stuart Langridge

And this is More on favatars, and the urlparse module, written , and concerning Web, Howtos

Bit more detail on how I implemented the favatars stuff, perhaps. The template that defines how a comment is output looks like this:

<div id="au$cmt_time" class="onecomment">
<img class="favatar" alt=""
src="http://www.kryogenix.org/favatars/$cmt_link">
<h4>$cmt_author_and_link</h4>
$cmt_description
[<a href="#au$cmt_time">#</a>]
</div>
The key bit in there is the <img class="favatar" alt="" src="http://www.kryogenix.org/favatars/$cmt_link">. http://www.kryogenix.org/favatars/*SOMETHING* goes to a Python CGI. You call it with a full URL, so http://www.kryogenix.org/favatars/http://simon.incutio.com/ will display either the favicon from Simon’s site or my “no icon” icon. This is made very easy by Python’s excellent urlparse module. You see, we need to get the favicon from the root of the server. To do this, just use urlparse.urljoin to join whatever URL you like with “/favicon.ico“. The key thing there is the / at the beginning. Watch:

>>> import urlparse
>>> urlparse.urljoin('http://www.kryogenix.org/','/favicon.ico')
'http://www.kryogenix.org/favicon.ico'
>>> urlparse.urljoin('http://www.kryogenix.org/days/','/favicon.ico')
'http://www.kryogenix.org/favicon.ico'
>>> urlparse.urljoin('http://www.kryogenix.org/days/something','/favicon.ico')
'http://www.kryogenix.org/favicon.ico'
>>> urlparse.urljoin('something that is not a URL','/favicon.ico')
'/favicon.ico'
>>> urlparse.urljoin('','/favicon.ico')
'/favicon.ico'
So, if you join the URL the punter left with “/favicon.ico“, then you get the URL for favicon.ico at the root of their domain. If they fill in something that isn’t a proper URL, like an email address, a mailto: with email address, blankness, or some bullshit, then you just get back “/favicon.ico“. The script checks, and, if the joined URL is “/favicon.ico“, it shows the “no icon” icon. If it isn’t, then it fetches the URL, transforms it to a 40×40 PNG, and streams that PNG for display. All nice and easy, thanks to the Python urlparse, urllib, StringIO, and Image libraries. The code also caches icons so that it works faster. You can read the code for favatar.cgi if you would like more details; it won’t work directly for you without some tweaking. You’ll also want a line similar to this in your .htaccess file:
RewriteRule   ^favatars/(.*)            /pyblosxom/favatar.cgi?url=$1

Comments

N/A

That’s pretty cool, but two improvements I’d recommend are making the image a link (but I don’t know what a gravatar is so maybe they’re meant to fulfill some other purpose) and also supporting icons that are given as a in the poster’s URL. I don’t know if it is an MSism but many things (including Firefox) support it, it isn’t too hard to add, and it solves the problem of people in mrben’s situation (assuming he has his own pages on the shared host that he would be linking to and it isn’t a shared page causing the forced sharing of icons). Here’s a function to get the most likely URL of an icon:

def getIcoUrl(url):

"""Return the URL of the favourite icon associated with input.

Returns None if input isn't a valid address."""

try:

fp = urllib.urlopen(url)

except IOError, e:

if e[1] == "unknown url type":

# The given URL isn't a web address so adding

# /favicon.ico won't work. Might as well

# jump straight to giving up

return None

else:

if fp.headers['content-type'].startswith('text/html') or fp.headers['content-type'].startswith('application/xhtml+xml'):

data = fp.read()

fp.close()

# Try to find any links to shortcut icons

ICONREGEX = "<link rel="shortcut icon" (?:.*)href=(?:['|"])(.[^'|"|\s]{0,})(?:['|"])(?:.*)>"

results = re.search(ICONREGEX, data, re.I)

if results != None:

# Found the requested URL of the favourite icon

icourl = results.groups()[0]

# the base url is already known to be valid, so we

# don't need to check that the join works.

# If the icon's url is invalid, we'll let

# loadResizeImage handle the inability to retrieve it

return urlparse.urljoin(url, icourl)

else:

fp.close()

# Return the default of url/favicon.ico

return urlparse.urljoin(url, '/favicon.ico')

I put that in code and pre blocks in the hope that it would not try to parse it, but the preview doesn’t look like it is going to work. It isn’t very hard to write yourself, but hopefully you can guess what it looks like untextiled (the numbered lists used to be comments) or get at the source somehow.

If you want to use this function you’ll have to rework the file naming since you probably don’t want to call this function for every request just to see what the cached image is/should be called, but that shouldn’t be too hard; saving the images as the user inputted URL instead of the image’s URL would eliminate the space saving feature of http://website/foobar always pointing to the file http—-website-favicon.ico for any value of foobar, but if you add in some code to normalise an URL (like always remove trailing slashes) it shouldn’t prove too much of a problem since most people would enter the root of their namespace. Or you could go all out and cache the results of this function in some sort of mapping file. – Testing:

>>>print getIcoUrl(‘http://www.python.org/doc/2.3/modindex.html‘)

‘http://www.python.org/doc/2.3/icons/pyfav.gif’

>>> print getIcoUrl(‘http://www.google.com‘)

‘http://www.google.com/favicon.ico’ – Transparency: If the icon is a gif (all I tested with), you seem to be able to get transparency working fine by doing i.save(thumb,‘PNG‘,transparency=i.info[‘transparency‘]) in loadResizeImage() but ICO files don’t get a ‘transparency’ key in their info dictionary (so you’d want to add a has_key() check before doing that form of save).

I have no idea really how transparency works in PIL or file formats in general, so I may be missing something obvious, but getcolors() only shows 4 colors for one of the icons with transparancy problems on your comments (simon.incutio.com/favicon.ico) and it looks like it uses 4 colors so one isn’t set aside just for transparency and converting to an RGBA image lists each of the 4 colors with a 4th value of 255 (fully opaque, I assume, but all the same value at any rate). Doing the same with the python.org gif gives you an extra color and lists one of the colors as having a 4th channel of value 0 (vs. 255 for the others) if converted to RGBA. So ICO files don’t support the same way of getting at the transparency info of other files, and I suspect it doesn’t support transparency on them at all.

N/A

Opps, I messed up the formatting a bit. The else: fp.close() should be indented one block.

gilmae

Why do you convert it to a PNG? Just leaving it as an .ico will solve your transparency problem.

sil

gilmae: I’m not sure that all browsers support ICO files. Mozilla seems to, in my testing, but I wasn’t sure about Konqueror, Safari, or Opera. If someone can confirm that browsers have as good support for ICO files as they do for PNG then I’m happy.

sil

gilmae: also, the Python Imaging Library can’t save ICO files, only read them, it seems.

gilame

On Windows, IE 5.0+ and Opera 7 do. Netscape 4.08 doesn’t. I was too lazy to lean over to the Macintosh on my desk and try it there.
Saving: Fair enough, that is a bit of a deal breaker.

t whalen

My favicon.ico is actually a 301 HTTP redirect to favicon.png:

RewriteRule ^favicon.ico$ http://introvert.net/favicon.png [R=301]

I’m pretty sure Python’s urllib will handle this redirect transparently.

Senji

Do you cache the icons?

Senji

Oops, how did I doublepost that? :)

sil

Senji: yes. See the code for details.

N/A

gilmae: even if PIL did support writing to ICO files, it (seems to) lose the transparency when loading the ICO, not when converting to another format.

gilmae

N/A: PIL loses the transparency for you. If you simply request the ICO files and save them to disk without the PIL step, the ICO file retains its transparency. I forget how sil is caching the favatars, but I am assuming it is to disk. And sil’s not much image quality benefit out of using PIL, the quality of my icon is equally as crap as it is when I use width and height attributes to blow it up.

sil

gilmae: but, again, that has two deficits. The first, and least serious, is that browsers’ resize-an-image algorithms tend to be worse than PIL’s. (This is not very serious because all the images look a bit ddgy when resized up.) The second is that, again, I am sure that not all browsers support ICO as a native format. If someone can confirm to me that that’s not the case then I’ll just start caching the icon directly and not PILing it on the way through.

name

Real,

This website belongs to Stuart Langridge. Contact details are available. Don't eat yellow snow. Valid HTML5, at least in theory, except for the bits that aren't because I'm that futuristic that I'm ahead of the spec, oh yes. HTML5 help from Bruce Lawson, among others. Fonts from the superb FontSquirrel. End.