My blog recently got swamped with comment spam. I had hoped that a simple "click this to prove you are not a spammer" checkbox would provide adequate protection against automated comment bots, but that didn't accomplish very much. I managed to find a relatively good Ruby captcha library built with GD that will hopefully get the job done. Captcha systems are simplistic turing tests that automatically generate images with random garbled text. A user proves that they are not a bot by typing the text from the image into a text box. Integrating Captcha support was relatively easy:
captcha = CAPTCHA::Web.from_configuration($CAPTCHA_CONFIG)
captcha.clean; captcha.image
$cgi.div("id" => "commentForm") {
$cgi.img(captcha.image_uri + captcha.file_name) +
$cgi.hidden("digest", captcha.digest) +
$cgi.p {
"Text in image:" + $cgi.br + $cgi.text_field("key", "")
} +
$cgi.submit("Send Comment")
}
I also spent some time fixing up the recent activity links on my home page. I added support for recent Trac actions by parsing items from the Trac Timeline RSS feed. Right now, it only shows items that relate to Mage, but I may expand it in the future if I start working with other Cixar project repositories on a regular basis. I was having trouble with Trac's RSS feed at first, because it wasn't including an author tag in any of the items. I eventually discovered that the author tag only gets included if the associated user specifes an e-mail address on the Trac settings page. I still haven't found a way to use URL parameters to filter for a specific user, so I do it with an XPath expression in my Ruby script.
Tags: programming, ruby, blog, cgi
Posted on 2006-08-08
In the dynamic world of technology journalism, there are very few universal truisms. One of the most important things I have learned is that screenshots almost always improve the quality of an article. In written works about software, particularly in reviews or tutorials, screenshots fulfill a number of crucial functions and imbue content with elegance and authority that would otherwise be absent. Failure to include a screenshot in a news article about a new program, for instance, elicits criticism and rebuke from irritated readers. They don't just appreciate screenshots, they expect screenshots.
The screenshot rule isn't entirely applicable to documentation. Although screenshots are still important in documentation, it is a context in which they should be used sparingly. There are many reasons for this, but I think the most important one is that screenshots are more likely to be superfluous in task-oriented reference material that is meant to be used alongside the actual program. In many cases, screenshots in the GNOME documentation are used to help the user confirm that they are reading about the right program.
Although I routinely use screenshots to break up large blocks of text in articles in order to increase readability, screenshots in documentation actually have the opposite effect. As the GNOME documentation style guide points out, breaking up documentation text with screenshots inconveniences readers by making them do additional scrolling. There are also some pragmatic reasons. Each screenshot included in the documentation has to be replicated by translators in each of the 42 languages supported by GNOME (Update: the GDP is evaluating ways to automate this process). Screenshots also have to be updated constantly as continued development alters the appearance of applications.
One of the points mentioned in the style guide has been of particular concern to the GDP lately. Usability studies have shown that users confuse screenshots in documentation with real windows. Although this may seem far fetched, it is apparently a very real concern, and one that we are actively trying to address. Joachim suggests fading the edges of screenshots to make the distinction between windows and images more apparent to users. I think this is a great idea, but as Joachim points out in a comment on bug #348495, there is no way to ensure that the fading effect will be consistent in all screenshots. Additionally, opening up the GIMP and doing that sort of operation for every image could become very time consuming.
For the sake of simplicity and consistency, I think we need to automate the process. In order to facilitate discussion on the possibility of leveraging automated image effects, I have developed a simple proof-of-concept utility that automatically blurs the edges of PNG images and applies a drop shadow.
The New Utility
Written in Python, my new utility leverages Cairo, PIL, and GTK. I initially wanted to do all of the image processing with ImageMagick, but the ImageMagick bindings for Python don't appear to be maintained or widely distributed. When I discovered the binding deficiency, I thought about using Ruby instead of Python, but the utility really needs to be built with a language that is used and understood by other GNOME developers. I also want to avoid foisting Ruby/GNOME dependency headaches on other documentation writers. It would have been nice to be able to avoid using PIL, but Cairo doesn't seem to have a working guassian blur filter yet, and I need that in order to produce a drop shadow. There is actually a value in Cairo::Filter called FILTER_GAUSSIAN, but apparently it doesn't do anything yet.
Using PIL to render the drop shadow is problematic because there is no simple way to convert Cairo surfaces into PIL image instances with version 1.0.2 of Cairo. Right now, the only way to do it is to save the Cairo surface to disk and load it back into a PIL image instance. Since the Python Cairo bindings don't use Python's file interface conventions, I can't just save to a StringIO instance like I do when I want to get a PIL image into a GDK pixbuf. There are some nasty hacks that involve wrapping a Cairo surface around a binary array, but that particular trick causes some peculiar problems and distorts the images in some cases. Fortunately, a new surface method called to_rgba() is already in CVS, and possibly even in the next major version of Cairo. For now, I have the script automatically save the image and load it back in again, but hopefully I'll be able to eliminate that step in the future.
My working prototype isn't particularly elegant, but it is effective. I spent an extra hour adding a user interface that allows the user to customize the parameters of the operation. Users can drag sliders to change several values:
- border - specifies the size of the faded regions
- filled - specifies how far from the edges the fades should end
- offset - specifies the drop shadow offset
The UI is currently very kludgy, but it will make it easy to compare various configurations and determine exactly which values should be used by default for documentation screenshots. If there is sufficient interest, I plan to eventually produce a complete, streamlined screenshot utility specifically for use by documentation writers. It will include support for screen region selection and it will make it possible to capture multiple images and then choose which ones to save. (Update: It looks like other members of the GDP team are also interested in automation and have some very cool ideas of their own. I particularly like the idea of using DogTail for automatically grabbing the images.)
The current implementation of my utility can be run as a stand-alone script or loaded into other Python scripts as a module. To execute it in stand-alone mode, simply run it from the command line and provide the path to a png file as a parameter:
python edge_fader.py screenshot.png
The following example shows how to use the edge fader as a module:
import edge_fader
img = edge_fader.fade_edges("original.png",
border = 30, filled = 2, offset = 6, show_shadow = True)
edge_fader.save_to_disk(img, "output.png")
View the source code here, and download it here. In order to run it, you will need PIL, and Python bindings for Cairo and GTK. An example of the program's output can be found here, and this is a screenshot of the program itself. Comments and criticism are greatly appreciated.
Tags: programming, screenshots, python, gnome, cairo, gdp
Posted on 2006-08-02