Friday, August 14, 2009

Using CSS3 box-shadow with IE

At YayArt, we show a lot of images. An important part of the presentation is subtle drop shadows.

My original solution to that problem was a Javascript hack that inserts a couple of 1 pixel wide divs at the edge of any element with a shadow class. However, although I have tweaked it several times, it has always been a bit slow. For instance, for the shop front page we're currently rendering more than 60 elements with shadows which takes a couple of seconds with Firefox and Firebug on my old trusty PIII 950 MHz laptop. I'll note that this is actually an order of magnitude faster than the (more general) code I based it on from somewhere else. The really tricky part was decent-speed IE 6 support because that actually required measuring the DOM elements.

However, Firefox 3.5 was released about a month ago, and one of the good news was support for the box-shadow CSS3 property. It's a relatively simple property for adding drop shadows. So given that Safari has it, and has had for some time, and most Firefox users will soon have it, it starts getting interesting. For reference, about half of our visitors are Firefox + Safari (+ Chrome which uses the same rendering engine as Safari).

If you search for box-shadow, some are already using it to add a bit of extra embellishment. However, on YayArt, it's an integrated part of the design. So I had to figure out how to make it work on all browsers.

I ended up with a three-way solution. For recent Firefox/Safari/... I use box-shadow. For non-box-shadow supporting browsers, I fallback to the old hack. And for Internet Explorer, I'm using a new hack based on the proprietary filter stuff in IE.

Here's a quick sketch. I put this in the general stylesheet:
.shadowed {
-moz-box-shadow: 2px 2px 3px #969696;
-webkit-box-shadow: 2px 2px 3px #969696;
}
This makes it work with browsers based on the Gecko and Webkit engines. Some people recommend adding a plain box-shadow line, but I need full control over which browsers are responding, and besides it seems to defeat the purpose of the browsers using the -engine- prefix. Here's a demo:

If your browser is recent enough, you can see a shadow here.

Now for the fallback, I need to discover in Javascript whether the browser is using the CSS:
function supportsBoxShadow() {
var s = document.body.style;
return s.WebkitBoxShadow !== undefined || s.MozBoxShadow !== undefined;
}
The assumption is that the symbols will be defined only if the browser actually draws the shadow. This seems to be about right, the only exception I've found so far is the Webkit port to GTK (there's a bug report open).

Final point is Internet Explorer. As it turns out, there is a DropShadow filter. It's not terrible useful for this purpose, however. There are no soft edges, and it shadows the content rather than the containing box (it simply redraws the content with an offset in another color). So for my purpose I need to set a background color to ensure it's drawing a rectangle and not a text shadow. You could probably hack the lack of blurring with another filter, but in the end I went with a simple three-one-pixel-wide-lines approach that looks like my old hack but is much faster (updated, replaced the filter with a more appropriate one as suggested in the comments):
.shadowed {
background-color: #fff;
zoom: 1;
filter: progid:DXImageTransform.Microsoft.Shadow(color='#969696', Direction=135, Strength=3);
}
This is served for IE only. The zoom: 1 is a hack to ensure that the element gets a layout (to work around the usual IE 6 bug).

So there you have it. Practical drop shadows. I will have to adjust the code a little bit as the other browsers get support, but I can live with that.

Friday, August 7, 2009

Emacs 23...

... is out and has been so for over a week. Why didn't somebody tell me?

In any case, I upgraded to Emacs 23 on my Debian unstable laptop. This means a new font. I put Emacs.font: Bitstream Vera Sans Mono 9 in my .Xresources file and am enjoying it so far. It's a bit muddy so I wasn't sure at first, but after having stared at it for a while the old font is just too skinny. Note that configuring the font in .emacs is still broken (sigh), in that it causes the window to jerk when you start Emacs.

I've been lobbying for changing the defaults over to make copy-paste between Emacs and other X applications (from this century) work properly. Apparently, (setq x-select-enable-clipboard t) is now better, but still not according to the spec. And it's not default yet.

I noticed some Tramp updates (for accessing remote files) in the NEWS and decided to give it a spin for the first time. Try C-x C-f someserver:myfile.txt and with a little patience, you're editing a remote file locally! Very neat.

Feeling adventurous I also tried IDO, an update of iswitchb that works with file find (so overrides C-x C-f). I'm turning it on with
(ido-mode 1)
(setq ido-enable-flex-matching t)
(setq ido-use-filename-at-point t)
replacing iswitchb and ffap settings. We'll see how it goes.

Finally, Anders mentioned a new Javascript mode, js2-mode, which I'm definitely going to try out.

Saturday, July 4, 2009

Using X11 over high-latency ADSL and an AltGr NX solution

X11 has been bad at high-latency links for a long time. Supposedly, the Xlib replacement XCB would fix some of that, but so far no-one seems to have picked it up. I found some work on GTK+ in 2006 but nothing since then. Sigh.

So there's this other proprietary solution, Nomachine NX with a free companion FreeNX. I decided to give it a try today, and after spending some time contemplating how to get it installed on Debian, it's not in the main repository because the basic architecture is broken (it's including it's own copy of X and SSH and some other stuff), I finally realized that the easiest setup was probably just downloading and installing the .debs on Nomachine's web site.

I was hoping to get ssh somehost then emacs & working, but it's a bit weirder than that. You have to start up a graphical connection thing, in which you can fortunately select a console. This gives you a remote xterm. The rest is simple.

I must say I'm very impressed with the performance. Of course, I'm only using Emacs but it works really well. It's like being connected through a local network.

Of course, once I started coding, the first time I needed a curly brace, which happens soon enough with Javascript, it was bam, back to start. AltGr sends an arrow-key left. After some digging and hair-pulling, it turns out I needed to run setxkbmap [layout] where layout is "dk" or "de" or similar. Except setxkbmap wasn't installed. So I installed it. Then it was missing it's data. I installed that too. Now everything is fine.

If someone would now just port this kind of thing to the default X.org setup, then I would be a happy man. No more console Emacs sessions.

Friday, May 1, 2009

Safe truncation of HTML

Another recipe, this time for solving the problem of truncating a piece of HTML, i.e. turning "<p>Blah blah blah</p>" into "<p>Blah ...</p>". Google didn't really turn anything useful up, except for a suggestion of using a full-blown HTML parser and then simplifying the result, so I thought I would post the snippet here for Google to pick up.

The code never splits a valid tag or character entity. It should be able to cope with invalid HTML too, but note that it won't sanitize it. So for instance, if there's an unbalanced <a> in the source string, it won't fix it. Character entities are dealt with by counting them as one character.

The basic idea in the snippet is that we just skip through the string unless we encounter an opening tag. If so, we see if we can find the corresponding end tag and save it for later. When we got enough non-HTML characters, a ... is put in and any saved but not yet used end tags are added to the output.

Here's the code in Python (it's easily turned into a Django filter), I aimed for readability rather than ultra-regexp ninja tricks:

import re

tag_end_re = re.compile(r'(\w+)[^>]*>')
entity_end_re = re.compile(r'(\w+;)')

@register.filter
def truncatehtml(string, length, ellipsis='...'):
"""Truncate HTML string, preserving tag structure and character entities."""
output_length = 0
i = 0
pending_close_tags = {}

while output_length < length and i < len(string):
c = string[i]
if c == '<':
# probably some kind of tag
if i in pending_close_tags:
# just pop and skip if it's closing tag we already knew about
i += len(pending_close_tags.pop(i))
else:
# else maybe add tag

i += 1
match = tag_end_re.match(string[i:])
if match:
tag = match.groups()[0]
i += match.end()

# save the end tag for possible later use if there is one
match = re.search(r'(</' + tag + '[^>]*>)', string[i:], re.IGNORECASE)
if match:
pending_close_tags[i + match.start()] = match.groups()[0]
else:
output_length += 1 # some kind of garbage, but count it in

elif c == '&':
# possible character entity, we need to skip it
i += 1
match = entity_end_re.match(string[i:])
if match:
i += match.end()

# this is either a weird character or just '&', both count as 1
output_length += 1
else:
# plain old characters
skip_to = string.find('<', i, i + length)
if skip_to == -1:
skip_to = string.find('&', i, i + length)
if skip_to == -1:
skip_to = i + length

# clamp
delta = min(skip_to - i,
length - output_length,
len(string) - i)

output_length += delta
i += delta

output = [string[:i]]
if output_length == length:
output.append(ellipsis)

for k in sorted(pending_close_tags.keys()):
output.append(pending_close_tags[k])

return "".join(output)

Friday, April 17, 2009

Test if uploaded file is JPEG, PNG or TIFF

I've been looking at some of uploads that went wrong on YayArt lately, and it turns out that people sometimes submit images with the wrong extension, e.g. "someimage.png" when it's really a JPEG. This confuses the image backend we're using to process large images, VIPS, so it reports back an error.

I did a bit of googling, and it seems the easiest way out is to simply check the first few bytes of the file for magic numbers. So here's a bit of Python code for checking for whether the file data belongs to a JPEG, PNG or TIFF image:
def is_jpg(data):
return data[:2] == '\xff\xd8'

def is_png(data):
return data[:8] == '\x89PNG\x0d\x0a\x1a\x0a'

def is_tiff(data):
return data[:4] == 'MM\x00\x2a' or data[:4] == 'II\x2a\x00'
If the file is already on disk, you can grab the first few bytes with
f = open("somefile.jpg", 'r')
data = f.read(11)
if is_jpeg(data):
ext = ".jpg"
elif is_png(data):
ext = ".jpg"
Of course this won't test that the whole file is valid. But it's easier to do that afterwards with an image library once the extension is correct.

The magic numbers are documented in the specifications for the formats. You can also find some help for other formats in the source code of the file command on Unix systems.

Update: I'm liking this so much that I ended up putting it in a separate file and making a convenience function for getting an extension like '.jpg'. Grab the Python file here. I also added support for GIF. Here's another easy reference for magic file numbers.

Second update: I've updated the code, there was a bug detecting JPEGs from certain digital cameras that put Exif data in the first segment. Suffice to check the two first bytes of the JPEG, then the problem does not occur.

Wednesday, April 8, 2009

Good art and bad art

There's a couple of things I've learned in the process with YayArt that I'd like to share. First, the key question: what is good art?

If you're like me, you'll probably think that this is impossible to say, or at least so difficult to answer that you need to have studied art for many years, be part of the art elite, to answer in a qualified way.

That's right. But in my opinion also wrong.

One approach to definition of good art is the Aristotelian approach. We try to pinpoint the common traits of the subject. This is a slippery path, but we can still do some.

First, art is a result of craftmanship which is performed with the purpose of engaging you in some way, by moving you, touching your feelings. It must be more than just an everyday thing.

If you're part of the art elite, you would add to this that good new art must bring something fresh to the table every time. It must be innovative. The past is important, to the point that new works that in the past would have been considered good art seize to be interesting.

If you're not part of the art elite but like me, you're probably more interested in the looks than prior art. It must be pleasing to the eye, or at least pleasingly unpleasant. The small details that are hard to define, but nevertheless obvious, must be right.


Good or bad art?

There's a conflict between these opposing views, and I believe this conflict leads directly to museums and obscure works like the infamous diamond skull by Damien Hurst, which most people are likely to agree is pretty ugly.

Why do most people hardly ever go to museums for inspiration? Is it because they're not susceptible to art? They lack the gene that enables them to experience art? This seems like a dubious explanation. Witness the commercial success of photography and Hollywood. Pictures work. And good art is, by definition, capable of engaging people.


Good or bad art?

The explanation is probably rather that going to a museum is cumbersome, and when you finally get there you might only see art that is definitely different, but doesn't engage you. It doesn't speak to you. Art that doesn't engage you is bad art, by definition - for you.

With YayArt we're trying to make it less cumbersome to see the art. And we are trying to replace the elitist notion of good art as being innovative art by something else. It's not that having an art elite is bad. It's just that we think there's a large proportion of people who could enjoy and benefit from art if the current art market would serve them. Or a new art market emerged.

The definition of good art we're using is a platonic definition, i.e. it works by examples. For instance, to define a chair to native from the jungle we wouldn't talk about furniture and four legs but instead point at the six chairs around the dining table and say, now do you understand?

At YayArt we show you a piece of art and ask, is it good or bad?


Good or bad art?

This is not abstract, so it is easy for everyone, not just art pros, to answer. What we're implicitly asking are questions like, does it move you? Do you like it? Everybody can answer that.

And when you think about it, this is really what good art is about.

Thursday, March 26, 2009

Ketil Bjørnstad

I finished reading To Music by Ketil Bjørnstad the other day.

It's mixing a realistic presentation of an young aspiring pianist and his chaotic life before his carrier starts with a sneaky, subtle weirdiness, a bit like Twin Peaks; very unnerving but at the same time funny. Of course, once I started on it, it was impossible to put down. For any interested in their general health condition, I recommend staying away. Seriously.

It makes me wonder what I look for in a book when I go to the library. My previous book was by Alistair MacLean, which today requires an humorous attitude towards his anachronistic a-man-is-man world views to read, at least for me.