Полезная информация

Chapter 12



Imagemaps have been a commonly found CGI feature on the World Wide Web for years now-and with good reason. They allow Web designers to create a powerful and attractive hyperlinking user interface. Unfortunately, the very success of imagemaps has lead to stagnation in their development as CGIs. This chapter shows that this does not have to be the case.

Imagemaps-Myth, Metaphor, and Meaning

One of my earliest memories of the World Wide Web is of an imagemap a friend of mine put together. This was back in late 1993 when I really didn't see the point behind the Web, and aich-tee-tee-pee (HTTP) meant nothing to me. By virtue of adequate hardware and abundant time, graduate students at the University of Western Ontario Astronomy Department started experimenting with CGI programming-hey, this was recreation! So much nicer than coding Runge-Kutta solvers and Simplex Minimization routines.

My friend Marc grabbed a GIF image of a bunch of Matt Groening cartoon characters-Akbar and Jeff, Binky the Rabbit, and so on-and if you clicked on a character, the system would go to a shell, finger the "appropriate" person in the department, and return the finger status to the Web. Marc was kind enough to revive this many-year-old page for the sake of this book (see Figure 12.1). Thanks, Marc!

Figure 12.1: The "Web finger" imagemap.

In its day, this was state-of-the-art CGI programming. (Please forgive us, but forms weren't widely used back then.) I was mystified by it and mentioned this "Web finger" to a friend of mine at the University of Toronto. He was so impressed that he posted its URL to Usenet and as a result, the Astro department server stats shot through the roof for the next few weeks. (This UofT friend later went on to become a part of Damien Doligez's Netscape SSL-cracking team-I knew Netscape was cracked two months before Netscape did!)

This small story took place three years ago. Now, I want to ask you a question: When was the last time you saw an imagemap that did something remotely as complicated as finger someone? That's my point-it doesn't happen! Imagemap programming has come to the doldrums of click-on-a-shape-and-we'll-put-you-on-a-new-page period.

But why is that? My guess is because imagemaps have become so commonplace and so uniform and so standardized that Web developers just don't think of all the other possibilities that imagemaps can hold. In my own small way, I hope to change that view here.

You can see many of the principles I discuss in this chapter in action by going to the following URL:

Anatomy of an Image-Pixels and Coordinates

Imagemaps allow locations on images to be determined and dealt with through the CGI. So, before we talk about imagemaps, it seems wise to learn something about computer image measurement systems.

Just about any image you'll find on the World Wide Web will be described by pixels and coordinates. Pixels, a distorted contraction of "Picture Elements," are colored squares of light that appear on your monitor. If ever you hear that a monitor's video mode is 1024¥768¥256, then that means the monitor is displaying 1024 pixels in the horizontal, 768 in the vertical, and that each pixel can assume one of 256 colors. Occasionally, you might hear that last designation referred to as some number of bits. 8-bit color is the same as 256 colors, while 16-bit color is 65,536 colors, and 24-bit color is the same as 16,777,216 colors. The connection between "bit color" and "colors" is that 2 raised to the power of the bit number equals the number of colors displayable.

Have you ever wondered why your video card might not be capable of certain video modes? Consider this: A full screen at 1024¥768¥24-bit resolution will require 18,874,368 bits of memory on your video card. This is equal to 2,359,296 bytes. If you have only 1 megabyte of video memory on your video card, you simply don't have enough memory for your video card to "remember" what it's supposed to display on your screen.

Each pixel can be referenced by an ordered pair coordinate (see Figure 12.2). The top-leftmost pixel on the screen is pixel (x=0,y=0). The bottom rightmost pixel is pixel (x=xmax-1,y=ymax-1), where xmax and ymax are the numbers stated when describing the video mode.

Figure 12.2: A friendly diagram showing how pixels are mapped onto a rectangular image..

In mathematics, x is the variable traditionally used to measure horizontal displacement from some reference point, while y is used to measure vertical displacement. With computer images, the same holds true. In mathematics, x increases towards the right, and y increases going up the page. In computer images, x increases in the same direction, but y increases going down. Watch out for this!

A completely analogous system is used to describe any image, only xmax and ymax don't have any predefined restrictions placed upon them. Also, the top-left (0,0) corner is defined relative to the image and not the screen.

As with so many other areas in computer science, the index of pixels within an image starts at zero, not one. So, if you have an image that is 100 pixels wide, its x coordinate will run between 0 and 99; likewise for its y coordinate. Be aware of this when planning out your map files and any programs that might process them.

HTML, ISMAP, and QUERY_STRING - Passing Imagemap Information to a CGI Program

Passing Imagemap Information to a

This far into your CGI programming experience, I'm sure you've encountered the GET and POST methods of passing information from the Web browser to the Web server and CGI program. These two techniques take name/value pairs created within a form and encode this information in a standardized fashion. The CGI program on the server is then responsible for decoding the provided string and reclaiming the name/value pairs.

The preceding is all true, but it's a very form-centric way of looking at things. The GET and POST methods aren't the most basic way to look at the problem of getting information from browser to server. Underlying these methods are STDIN and QUERY_STRING: The GET method places its encoded name/value string and places it in the QUERY_STRING, while POST places its encoded string within STDIN. However, GET doesn't have a monopoly on the use of QUERY_STRING. Because imagemaps, too, rely on QUERY_STRING, I'll restrict my discussion to it from now on.

Consider the following mock URL:


It obviously invokes a CGI program. This fairly straightforward URL can be made more complex. If we were to append a ? to the end of the line, we could continue onward with more text:


In a URL, ? is a reserved character. Anything following it becomes the QUERY_STRING. The QUERY_STRING can be read by a CGI program and used within that program to do whatever a program might do with string data. For instance, it could be parsed and used as data within mathematical calculations:


In a rare show of short-sightedness, HTML has imposed a limit of 1,024 characters on the length of a URL. This includes its QUERY_STRING and its PATH_INFO. Even then, there is no guarantee that any given Web browser can handle a URL of that length. For this reason, many forms that have to pass halfway decent amounts of information to a CGI program will use POST rather than GET, even though GET is the "preferred" method.

QUERY_STRING information is extracted by a CGI program in a number of different ways. In UNIX, the most common is through environment variables. Within a Borne shell script, you can reference it as in this example:

echo Content-type: text/html
echo ""
echo Your query string was:

In Perl, this same mini-program would be written as the following:

print "Content-type: text/html\n\n";
print "Your query string was: $ENV{QUERY_STRING}\n";
exit 0;

The QUERY_STRING is the heart and soul of imagemaps. A Web browser will generate a QUERY_STRING appropriate to the x,y value of a mouse-click upon an image when the following HTML is used:


Note that it takes a combination of both the ISMAP attribute within the <IMG> tag and the presence of an <A HREF> statement for x,y QUERY_STRING information to be produced.

To use a slightly less generic example, let's say I had an image file called mercator.gif that had dimensions of 600¥300 pixels. I place my mouse pointer somewhere around Toronto, which might be located at 200,90. I want this information to be processed by a CGI program called imagemap.cgi. Some possible HTML that could be written to accomplish all this is

<A HREF=http://www.anyfirm.com/cgi-bin/imagemap.cgi><IMG SRC=http://Âwww.anyfirm.com/pics/mercator.gif ISMAP></A>

If I were to then click on Toronto, the URL the browser would try to invoke would be


Flatland Revisited-An Introduction to the Standard Imagemap System

To begin, I'll start by telling you that you won't be seeing any imagemap-handling source code any time soon. I hate to say this as it gives me great joy to both read and write the stuff. Conventional imagemap source code has become so standardized that there's no point in my going into it immediately. However, I will write my own nonconventional imagemap handler later on in this chapter to show you that you don't have to rely on the standard software if it doesn't fit your purposes. Instead, right now I'll go into the theory of how imagemaps work.

In the 19th century, a Shakespearean scholar named Edwin Abbott wrote a book called Flatland. It was a satirical description of the lives of two-dimensional creatures inhabiting a plane. The social status of a flatlander was almost entirely based on their geometry. I'm reminded of flatland when it comes to standard imagemaps, as they are a description of what to do when a particular two-dimensional geometry is encountered.

Imagemap.c-The Standard Imagemap Handler

The National Center for Supercomputing Applications (ncSA) released the first HTTP server that really caught on as well as the first Web browser, called Mosaic. It has also supported quite a lot of other Web-related projects, including the creation and distribution of imagemap.c, the standard imagemap handler. This C source code comes with the ncSA HTTPd distribution and will be automatically compiled with the HTTPd and installed in the /cgi-bin/ directory of the Web server. However, if this isn't the case with your installation or if you wish to get a newer version of the code, you may find it through the following URL:


In fact, this link will tell you just about everything you need to know about the standard imagemap system.

The ncSA and Apache World Wide Web servers have become the "modern standard" in the field. In the beginning, though, CERN was King. This is quite understandable-Tim Berners-Lee of CERN (Conseil Europeen pour la Recherche Nucleaire) was the driving force behind HTML and HTTP.
CERN proposed its own imagemap-handling system that was quite popular for a while. In case you ever run across it, you can find a discussion at
The main difference between the CERN and ncSA imagemap-handling systems is that the .map files aren't quite the same. However, there is a 1:1 correspondence between the two map file types. Conceptually, the two schema are virtually identical.

I'll start here where I left off in the previous section. Through HTML, an x,y coordinate pair corresponding to a mouse-click on an image can be generated and sent to a CGI program. This program is then responsible for obtaining this information from the QUERY_STRING string environment variable. So, the question now becomes what to do with this coordinate pair. The ncSA standard imagemap system offers one possible solution.

Consider the following URL:

<A HREF=http://www.anyfirm.com/cgi-bin/imagemap.cgi/pathinfo/mapfile.map><IMG ÂSRC=imageURL ISMAP></A>

This HTML is almost identical to the HTML in the previous section, except that now imagemap.cgi has more information following it. This information tells imagemap.cgi to look into a .map file; .map files contain a geometrical description of the image in question and tell imagemap.cgi what to do when it finds a mouse-click lying within a given shape. The next section is an in-depth examination of .map files.

Note that this line will vary depending on whether or not your imagemap executable is named imagemap.cgi or simply imagemap, whether or not it is located within the /cgi-bin/ directory, and what sort of pathinfo you supply it with. If the imagemap executable file is not kept in the server's specified /cgi-bin/ directory, then it must be named imagemap.cgi. Within the /cgi-bin/ directory, it may or may not possess this extension.

pathinfo is a strange fixture of CGI programming at best, but it becomes even stranger in the context of the standard imagemap handler. A path string can be appended after any URL that terminates in a file. The CGI programmer can find this string in the PATH_INFO environment variable. In the case of the standard imagemap handler, one of two things can be done with this string:

It can be used to directly reference the .map file associated with the image that has been ISMAPed.
It can be used as an alias by the imagemap program to reference the imagemap.conf file.

The HTML code given earlier shows the usage when PATH_INFO is used to directly reference a .map file. In this context, imagemap.cgi will try to access the .map file that could be referenced by the URL:


Note that pathinfo could have many different directory levels included within.

Some servers are configured to recognize .map files as being specifically associated with imagemaps. If that is the case, the server will prevent you from accessing a .map file directly through its URL as I pointed out earlier.

If pathinfo is used as an alias, imagemap.cgi will look in its own directory for a file called imagemap.conf. If imagemap.conf is found, imagemap.cgi will parse it for references that match the pathinfo. Following these references, there will be map file names, which are then used. I have grabbed the following from http://www.anadas.com/ at my UNIX shell to show how it would look directly:

% ls -l *.map *.conf
-rw-r-----  1 anadas  www  590 Apr 16 02:55 button.map
-rw-r-----  1 anadas  www   38 Mar 23 17:42 imagemap.conf
-rw-r-----  1 anadas  www  507 Apr 17 08:53 swatch.map
% cat imagemap.conf
swatch: swatch.map
button: button.map

To do much in the way of CGI programming, a good working knowledge of UNIX is almost essential. ls will list the contents of a directory. cat will concatenate the contents of a file. These commands are roughly equivalent to dir and type in DOS.

Finally, an example of how pathinfo aliases and the imagemap.conf files are put into practice:

<A HREF="exe/imagemap.cgi/swatch"><IMG SRC="pics/swatch.jpg" ISMAP></A>

.map Files-Describing Shapes the Imagemap Way

Since the time of the Babylonians, people have been obsessed with geometry. Farmers, architects, and mathematicians-even lowly computer programmers-have all found it useful to be able to precisely describe shapes. We've come up with quite a few systems of doing so and a bunch of standardized shapes to work with. Of this vast storehouse of knowledge, the standard imagemap system recognizes only four methods: rectangles, circles, polygons, and points.

The basic idea behind the standard imagemap system is that whenever a mouse-click is registered corresponding with one of these shapes in an imagemap, a URL is invoked. This is accomplished by having the imagemap handler issue a Location directive via the Web server. A .map file is the means through which shapes are defined and the standard imagemap program knows how to tie which URL to what shape. The imagemap program gets the mouse-click coordinates and determines which shape contains the click.

A Web server can issue three general sorts of header statements and be understood by a Web browser:
  • A Content Type description
  • A Server Error description
  • A URL Location
The first is issued when all is well. As you have probably guessed, the second occurs when something has gone wrong. The last redirects the Web browser to the URL the server has supplied. The Location directive, among other uses, is how imagemap software turns a mouse-click into a new Web page for the mouse-clicker.

A rectangle can be uniquely identified by specifying its top-left and bottom-right coordinates within an image as x1,y1 and x2,y2. A circle can be uniquely identified by specifying its center, xcen,ycen, and any point on its circumference, xcir,ycir. A polygon can be uniquely specified by a list of all its vertices: x1,y1, x2,y2, x3,y3, .. xn,yn. To make computation easier, imagemap handlers usually limit the number of vertices a polygon may possess to some large number, usually 100. For the purposes of .map files, rectangles are abbreviated to rect and polygons to poly.

Obviously, a point is fully described by itself: x,y. In the context of imagemap.cgi, if a mouse-click lies outside of any defined region (rect, circle, or poly), it is compared against all specified point coordinates. The imagemap handler will redirect the user to the URL specified on the point line whose coordinates are nearest to the mouse-click. As it is stated so eloquently in the ncSA Imagemap tutorial, "The nearest point wins."

The point method makes sense only if there is more than one point line in a file. Otherwise, the one point that is defined would always "win." If that's what you actually want to happen, you should use the default method. This is invoked when two conditions are met: The mouse-click isn't found to lie within any of the described shapes, and there are no point lines in the .map file.

Every line in a .map file has a very straightforward syntax. For all lines except default, it is

shape URL coordinates

This syntax differs for default to make it even simpler:

default URL

Putting all these concepts together, we get a logical .map file:

rect URL x1,y1 x2,y2
circle URL xcen,ycen xcir,ycir
poly URL x1,y1 x2,y2 x3,y3 .. xn,yn
point URL x,y
point URL x,y
default URL

You may have as many rect, circle, poly, and point lines in a .map file as you need. Also, blank lines are skipped, and lines beginning with the # character (also known as "number," "pound," or finally "hash" to the truly hackish) are ignored as comments.

You can find utility programs on the Internet that can help you efficiently create .map files. For Microsoft Windows users, try looking for them at Stroud's Consummate Winsock Application List:

A Tale of Two Web Browsers
While preparing for this chapter, I took a good, hard look at how imagemaps were handled by Web browsers, and I made some very interesting observations. In a variety of trial circumstances, my two trial browsers, Netscape 3.04b and Microsoft Internet Explorer 2.0, both had a very tough time properly determining the boundaries of the imagemaps I constructed.

Both could handle finding the upper-left corner of my images pretty well, but the bottom-right corner (reflecting on both the bottom and right sides of the image) was quite elusive to them; though I knew my test image was 200¥100 pixels in size, I could get both browsers to tell me that I had clicked on pixel (202,103), for instance. Things got even less intuitive and more bizarre when I started doing things like setting BORDER=20, or HEIGHT=50 WIDTH=50, or playing around with HSPACE and VSPACE.

Even worse, there was no consistency between how the two browsers reacted. For instance, with BORDER=50, with Netscape I found that a click on the upper-left corner of the border would show 0,0 on the screen before clicking but would pass -20,-20 to the imagemap handler! MSIE didn't show anything on the screen before clicking but passed 0,0 when I tried clicking on the upper-left part of the oversized border.

And so, my advice is: As with everything else on the Web, take imagemaps with a grain of salt. It might be helpful for you to remember the limits of Web browsers when creating .map files and imagemap handlers.

Client-Side Imagemaps and Magic MIME Types

The best and worst labor-saving devices are those things that save you the trouble of thinking. Thinking is difficult and time consuming, and if you can get the job done without as much thought as you might normally have to invest, then great. However, by accepting someone else's prepackaged thought, you can fall into the trap of accepting the limits they decided to put on the problem. This undesirable condition runs rampant in the field of imagemap handling.

As I've vented before in this chapter, canned imagemap code is far too good. It's reasonably easy to install, easy to use, sufficiently powerful enough that it covers just about all imagemap cases, and allows people to forget that imagemaps can be treated as a creative CGI challenge. But it gets even worse; the canonical imagemap format has been considered a "standard" to the point where some Web browsers and servers now have special features that allow you to bypass imagemap.cgi altogether. These features are client-side imagemaps and the .map Magic MIME type.

Client-Side Imagemaps

Newer versions of Netscape, Microsoft Internet Explorer, and Spyglass Mosaic support a variant on the idea of an imagemap called a client-side imagemap. This technique places the burden of processing the x,y pair produced by the ISMAP attribute on the Web browser (the client) rather than on a remote CGI program. As far as a Web page developer is concerned, this is accomplished through completely nonstandard, nonsupported extensions to HTML.

The implementation of client-side imagemaps discussed here does not have much to do with the proposed HTML 3.0 draft on client-side imagemaps, which is tied to the <FIG> tag. However, virtually no browser supports this style of client-side imagemaps while three of the biggest support the client-side imagemap system proposed by Spyglass-the system I talk about here. It practically reigns supreme. *sigh*

A client-side map is defined within the body of an HTML file. The logic of the map structure is very similar to that found in the .map files used by the ncSA imagemap.cgi program. I will include a portion of an HTML file as an example and discuss it here. You can see this example in action at


As you can see in Figure 12.3, the mouse-pointer shows its "pointing hand" disposition as it is positioned to click on a client-side imagemap. Note that the URL this click will invoke is displayed at the bottom of the screen. This URL display appears only with client-side imagemaps and not server-side imagemaps.

Figure 12.3: A client-side imagemap about to be invoked.

<MAP NAME=anyname>
<AREA SHAPE=rect COORDS="0,0, 117,133" HREF=http://www.anadas.com/?minisearch>
<AREA SHAPE=rect COORDS="118,0, 212,133" HREF=http://www.anadas.com/products/>
<AREA SHAPE=rect COORDS="213,0, 306,133" HREF=http://www.anadas.com/?company>
<AREA SHAPE=rect COORDS="307,0, 419,133" HREF=http://www.anadas.com/?quotes>
<AREA SHAPE=default HREF=http://www.anadas.com>

Here's a quick review of this syntax:

A complete review of Spyglass-style client-side imagemaps can be found online at


But Master, How Can I Depend on Client-Side Imagemaps When So Many Browsers Don't Support Them?

An excellent question, grasshopper! The answer is that you don't have to. You can set up a redundant scheme whereby an image is tied to both a client-side and a server-side imagemap.

To do this, you must define a map in your HTML file as I talked about earlier. You must also create a .map file and store it on the server. Then, reference the image with the following:

<A HREF="/cgi-bin/imagemap.cgi/pathinfo/file.map"><IMG SRC="images/Âmapped_image.gif" USEMAP="#anyname" ISMAP></A>

As always, insert an appropriate URL for the imagemap.cgi executable, the pathinfo, and map file names.

When this format is used, the client-side imagemap is executed if the browser being used supports client-side imagemaps. If it doesn't, then all HTML having to do with client-side imagemaps is ignored and then the markups relating to server-side imagemaps kick in.

While this scheme certainly works, it can become an annoyance to maintain current map information in both the .map file and in the HTML file. There's no automatic way to deal with this; you'll just have to discipline yourself to do it.

The .map Magic MIME Type

Some CGI programmers are lucky enough to be able to control their httpd configuration, either directly or by being "tight" with the sysadmin. If you happen to be among these lucky few, then this section could be of use to you. Even if you aren't, you might be fortunate enough to have a sysadmin who pays special attention to their httpd.

By way of a small refresher, the World Wide Web is a client/server environment. Your Web browser (Netscape, Mosaic, MSIE, or Lynx) is the client. Each time you try to load a Web page, it makes a request of the Web server.

A Web server is also known as an httpd, sometimes capitalized to HTTPd. This is an acronym standing for HyperText Transfer Protocol daemon. In the world of UNIX, a daemon is a program that lurks in the background and performs system-related tasks that don't require any human intervention. Daemons are generally started at boot-time. An httpd is a daemon responsible for dealing with, or serving, hypertext transfer protocol requests.
I will discuss only the ncSA and Apache UNIX Web servers because they are
  • Highly available
  • Widely installed and used
  • Well documented
  • Reasonably powerful
  • Completely free
  • Very similar to each other
Everything you ever wanted to know about these two Web servers can be found at

The ncSA and Apache Web servers are quite configurable. One of the more interesting sections when configuring one of these Web server is the "Magic MIME Type" area. This can be found in the ~/conf/srm.conf file, where ~ represents the home-level directory of the Web server installation, also known as ServerRoot. I'll quote a portion of this file here:

ServerRoot and DocumentRoot are two different configuration variables. For instance, a system might have ServerRoot set at /var/www and DocumentRoot at /var/www/docs. ServerRoot is defined in the httpd.conf file while DocumentRoot is defined in the srm.conf file.

# The following are known to the server as "Magic Mime Types"  They allow
# you to change how the server perceives a document by the extension.
# The server currently recognizes the following mime types for server side
# includes, internal imagemap, and CGI anywhere.  Uncomment them to use them.
# Note: If you disallow (in access.conf) Options Includes ExecCGI, and you
# uncomment the following, the files will be passed with the magic mime type
# as the content type, which causes most browsers to attempt to save the
# file to disk.

#AddType text/x-server-parsed-html .shtml
#AddType text/x-imagemap .map
#AddType application/x-httpd-cgi .cgi

The .map Magic MIME type is supported by the new ncSA/1.5 httpd and in the beta release of Apache httpd 1.1. It is unlikely that your sysadmin will upgrade to either of these newer httpds without it being brought explicitly to their attention, but beware! Sysadmins are quick to anger and their vengeance is mighty.

This about says it all except for the conclusion: Removing the # character from the front of any of these lines and then rebooting the httpd will give you access to that Magic MIME type. If you enable .shtml as a Magic MIME type, then server-parsed HTML files can be accessed outside of directories that are specifically assigned to this role. Enabling the .cgi line will allow the Web server to run CGI files in directories outside of /cgi-bin/. Enabling the .map line will give you awesome cosmic powers!

Enabling the .map line won't give you awesome cosmic powers. However, enabling the .cgi Magic MIME type can be very useful. You might find that this doesn't work the first time around, though. The .cgi Magic MIME type depends somewhat on the setting of a line in the accces.conf file. If you have any problems with enabling the .cgi line in srm.conf, check to see if your access.conf file has an
Options Includes ExecCGI

Seriously, enabling the .map Magic MIME type allows you to write your imagemap HTML statements in a slightly altered format:

<A HREF="map_URL"><IMG SRC="image.gif" ISMAP></A>

What ncSA has essentially done is incorporate their imagemap.c source code into the httpd.c source code, making the standard imagemap handler part of the standard Web server! Rather than unloading imagemap handling onto an external imagemap handling program that references an auxiliary map file, the server references that map file directly and deals with it internally. I have composed a working (if simple) example of this in action:

<HEAD><TITLE>Example using the .map Magic MIME type</TITLE></HEAD>
<P>Click somewhere in the image below:<BR><BR>
<A HREF="exe/magic.map"><IMG SRC="images/magic.gif" ISMAP></A>

The ncSA/1.5 server is instructed by this HTML to reference the following map file:

default /cgiunleashed/imagemaps/elsewhere.html

# inside the black circle
circle /cgiunleashed/imagemaps/circle.html 70,72 70,122

Take a Walk on the Server-Side-Developing Imagemap Code

So far in this chapter, I've talked the talk. Now, I think it's time to walk the walk. For your viewing pleasure, I've written an imagemap handler that is functionally similar to the standard ncSA code but includes a fair number of differences, as well. Being the creative guy that I am, I have decided to call my masterpiece im.cgi.

im.cgi is written in Perl and developed for a UNIX platform. By now I'm sure you've all heard that Perl is the best language for CGI development. I would call this statement a justified simplification. Perl is an excellent language for writing programs that handle strings and files and that are reasonably small and uncomplicated. This description would fit the vast majority of CGI programs. I've written CGI programs in C and C++ when it was appropriate to do so, but for CGI programs, I always go to Perl first.

I'm not going to spend any time in this chapter discussing Perl as a language. Information about it is very available on the Internet and in bookstores-that's how I learned it, after all. im.cgi is fairly well documented, and you should be able to get the meaning of most Perl commands and structures from the context in which they are used.

im.cgi gets the ISMAP-produced x,y pair through the QUERY_STRING environment variable. This coordinate pair is compared against geometries found in an .imap file; .imap files are not structured in exactly the same way as an .map file, though. Valid methods in an .imap file are rect, ellipse, point, and default. The area that a rect or ellipse delimits is described in a new format I have outlined in a logical .imap example later in the chapter. For the sake of simplicity, I have eliminated the poly method.

Dealing with polygons can be horrendously complicated both in computer programming and in mathematics (geometry and topology). A general polygon can be convex or concave. This makes it difficult for a program to determine whether a point is inside or outside of the polygon region. Even worse, a polygon may be connected multiply (see Figure 12.4). Then, it's tough to even know which side of the polygon is on the inside or outside! In any case, a reference point needs to be chosen to allow for a determination of on which side of the polygon an arbitrary point may lie.
Complex geometry is the square root of all evil.

Figure 12.4: Problem polygons.

Here are examples of a concave polygon and a multiply connected polygon. Shapes that fall under these categories are difficult to work with in computer programs. To help make the job easier, assumptions are usually made that simplify (though possibly misrepresent) the situation. In addition to (slightly) changing the allowable methods, I've changed the structure of each line. URLs follow rather than precede the geometry-specifying information. Also, im.cgi doesn't handle relative URLs-they have to be specified in full, including the http:// at the front. Blank lines are skipped, and lines starting with # are treated as comments and are ignored. The first line of an .imap file contains the alias meant to be compared against the PATH_INFO environment variable. Only uppercase and lowercase alphabetic characters are valid for this alias name; im.cgi doesn't support direct references to .imap files as imagemap.cgi does for .map files-pathinfo aliases must be used. As a result, .imap files must be in the same directory as im.cgi. If a mouse-click is found to be contained within a rect or ellipse, the program will "short-circuit" and immediately bring the user to the URL specified by that shape. The consequence of this is that if rects or ellipses overlap, the shape described first in the .imap file takes precedence. It is possible to specify a shape that lies outside the boundaries of the actual image file the .imap file relates to-this could be useful if you want to have a clickable semicircle at the edge of an image. The point method works the same as it did in imagemap.cgi. If a file has any point methods specified, any default methods are ignored. There are no restrictions on where methods are placed within a file apart from that the first line is reserved for the alias name.

This is all very similar to the structure of a .map file. I have added one "twist" to it all, though. rect, ellipse, and default methods may be specified by more than one URL. If more than one URL is specified for any one of these methods, im.cgi will choose one randomly. Also, there can be more than one default line-a convenience to allow for the input of many alternative default URLs to be randomly chosen from, without putting many URLs on one line. To be honest, I can't think of a really good application for this. However, I wanted to demonstrate that things that are completely outside the capabilities of imagemap.cgi are possible when you write your own imagemap handler.

All these principles are demonstrated by the following logical .imap file:

# mapfilealias
# The above is the path info alias for this .imap file.

rect upperleftx,upperlefty xsize ysize URL1 ... URLn
ellipse centerx,centery xsemiaxis ysemiaxis URL1 ... URLn

point x1,y1 URL
point xn,yn URL

default URL ... URL
default URL ... URL

An ellipse is a useful and fascinating shape and fairly easy to work with, too. I'm not sure why the standard imagemap code uses circles instead. After all, a circle is just an ellipse with equal x and y radii. Figure 12.5 is a diagram showing you the geometry of an ellipse and the algebra associated with it.

Figure 12.5: Anatomy of an ellipse.

You can see an example of my im.cgi program (shown in Listing 12.1) at work at the following URL:


Listing 12.1. im.cgi program-an alternative imagemap handler.
# the above line may need to be altered depending on where Perl is
# stored on your system

# This program written by Richard Dice of Anadas Software Development
# as part of the Sams Net "CGI Programming Unleashed" book.  The author
# intends this code to be used for instructional purposes and not for
# resale or commercial gain.
# Any questions or comments regarding this program are welcome.  You
# may contact the author by Internet email: rdice@anadas.com

# initialize some variables, partly because it's an old habit, partly
# so that they aren't treated as variables localized to loops
$imap = '';
$c = 0;
$found = 0;
$this_rect = 0;
$this_ellipse = 0;

# seed the random number generator for the 'rand' command

# Get GET co-ordinate information and place in x and y variables
($x,$y) = split(/,/,$ENV{QUERY_STRING});

# Get PATH_INFO information -- this will be used to determine which .imap file
# to use.  Any character which is not in the range a-z or A-Z will be removed.
# This is like the imagemap.conf file, but forces each .imap file to be
# responsible for its own alias.
($filematch = $ENV{PATH_INFO}) =~ tr/a-zA-Z//cd;

# determines the names of all .imap files in the directory which houses im.cgi
# and uses the UNIX head command to look at the first lines of each of them
# until a match with $filematch (derived from PATH_INFO) is made.
@imapfiles = <*.imap>;
foreach ( @imapfiles ) {
   $firstline = 'head -1 $_';
   if ( $firstline =~ /$filematch/ ) {
      $found = 1;
      $imap = $_;
@imapfiles = ();        # this array is unneeded, so free up memory
&not_there unless $found; # if no matching .imap file, abort with error msg.

open(IMAP,$imap);        # now, I start parsing the desired .imap file
while ( <IMAP> ) {
   next if ($_ eq "\n"); # blank lines are ignored
   next if ($_ =~ /^\#/ ); # lines starting with # are also skipped

# the following line creates an array called @line based on the contents of
# the current line being read in from the .imap file.  Whitespace is used as
# array element delimiters.  Isn't Perl grand? :-)
   @line = split;

# break the .imap file-reading loop if the current line being read in is
# a rect or an ellipse descriptor and $x,$y lies within that shape
   if ($line[0] eq 'rect') {
      if (&check_rect($line[1],$line[2],$line[3],$x,$y)) {
         $this_rect = 1;
   if ($line[0] eq 'ellipse') {
      if (&check_ellipse($line[1],$line[2],$line[3],$x,$y)) {
         $this_ellipse = 1;

# build an array of points and their corresponding URLs
   if ($line[0] eq 'point') {
      $points[$c++] = join($;,$line[1],$line[2]);

# makes a running list of 'default' URLs to be randomly chosen from should
# the $x,$y click not fall within a defined shape and no 'point' has
# been defined
   if ( $line[0] eq 'default' ) {
      $default .= join(' ',@line[1..$#line]);

# output a Location randomly chosen from the list of URLs supplied
# following the co-ord info, should the $x,$y match with a rect or ellipse
if ( $this_rect || $this_ellipse ) {
   splice(@line,0,4);        # remove the first 4 elements from @line
   $i = int(rand($#line+1));
   print "Location: $line[$i]\n\n";
} elsif ( defined(@points) ) {
   $matchingkey = &nearest_point($x,$y,@points);
   ($discard,$keep) = split(/$;/,$points[$matchingkey]);
   print "Location: $keep\n\n";
# If the $x,$y doesn't fall inside any shape and no 'point's are defined,
# then randomly chose a default URL from the list.  If no default URLs
# are provided via the .imap file, return to the page which contains
# the imagemap
elsif ( $default ne '' ) {
   $urls = split(/ /,$default);
   $i = int(rand($#urls+1));
   print "Location: $urls[$i]\n\n";
} else {
   print "Location: $ENV{HTTP_REFERER}\n\n";

exit 0;

sub not_there {
   print "Content-type: text/html\n\n";
   print "IM, Richard's imagemap handler, couldn't find the .imap file ",
   "specified by the path info supplied in the URL.  Sorry!";
   exit 1;

sub check_rect {


   $returnval = 0;
   ($upleftx,$xmax,$ymax,$getx,$gety) = @_;
   ($upleftx,$uplefty) = split(/,/,$upleftx);

   if ( (($getx-$upleftx) >= 0) &&  (($getx-$upleftx) <= $xmax) ) {
      if ( (($gety-$uplefty) >= 0) &&  (($gety-$uplefty) <= $ymax) ) {
         $returnval = 1;

   return $returnval;

sub check_ellipse {


   $returnval = 0;
   ($centx,$a,$b,$getx,$gety) = @_;
   ($centx,$centy) = split(/,/,$centx);

   if ( ( (($getx-$centx)/$a)**2 + (($gety-$centy)/$b)**2 ) <= 1.0 ) {
      $returnval = 1;

   return $returnval;

sub nearest_point {

   local ($min_key, $mindist, $i);
   local ($getx, $gety);

   ($getx,$gety,@pt) = @_;

   $min_key = 0;
   $mindist = 1000000000; # no image will be this large! (I hope)

   for $i (0..$#pt) {
      ($a,$b) = split(/$;/,$pt[$i]);
      ($a,$b) = split(/,/,$a);
      if ( ( ($a-$getx)**2 + ($b-$gety)**2 ) < $mindist ) {
         $min_key = $i;
         $mindist = ($a-$getx)**2 + ($b-$gety)**2;

   return $min_key;

So, smarty, you think you can crash my code? Of course you can! im.cgi assumes that you give it a correct .imap file. Want to see what happens when you give it a rect with no following URL or a negative xsize? That's up to you. However, if you feed it bizarre input, you can expect it to choke or barf. Such is the way of all computer programs.

Creative Imagemap Programming-Breaking the Paradigm with Glorglox

Throughout this chapter, I've been an advocate of thinking for oneself when creating imagemap code. The im.cgi code I wrote departs from the standard imagemap code, but not very much. It still follows the same basic principle of click-on-a-shape-and-we'll-take-you-there. That doesn't have to be the only way to deal with imagemaps, though.

Shape-based imagemap handlers are very good at describing shapes that can be described through simple analytic geometry but very bad at describing arbitrary curvy shapes, such as what one might find on an isometric (contour) map. And what useful maps these are! For the next few days, try keeping you eyes peeled for examples of contour maps in your everyday life. I think you'll be surprised with how many you see.

In addition to isometric maps, text isn't processed very well by shape-based imagemap handlers. It's possible to plot out the various cusps of a nice Geoslab font, I suppose, but it wouldn't be desirable. And brush script... forget it! You'll need every one of those 100 vertices you've got to play with in your poly method and more.

How could an imagemap handler be constructed to deal with these sorts of situations? Glorglox presents us with one possible answer.

Glorglox is an advanced imagemapping program written by Tom Rathborne, a University of Waterloo mathematics and computer science student. He came up with an elegant solution to the problems I discussed earlier. The key is to essentially forget about geometry. Imagemaps are images, period. Forget about the shapes we artificially impose on them and consider their basic property: color. Figure 12.6 shows the Glorglox home page and, in my opinion, immediately gives away its methodology.

Figure 12.6: The Gloralox home page.

The Glorglox Home Page contains two images that show what Glorglox is all about: http://www.uunet.ca/~tomr/glorglox/demo/.

The HTML used to invoke Glorglox is structurally identical to the HTML used to invoke imagemap.cgi. An example is

<A HREF="/cgi-bin/glorglox/glorglox-demo"><IMG SRC="demo.gif" ALT="[glorglox demo
image]" ISMAP WIDTH="512" HEIGHT="128" BORDER="0"></A>

When an x,y coordinate pair is provided to the Glorglox through QUERY_STRING, a .gmap file is referenced. This referencing is done through PATH_INFO and can be done either directly or through an alias found in the glorglox.conf file. The preceding HTML shows the alias method in use, and the glorglox.conf file is identical in form to the standard imagemap.conf file:

glorglox-demo : /u/tomr/www/glorglox/demo/demo.gmap

A Glorglox .gmap file is used to provide glorglox.cgi with information regarding what to do with its x,y pairs. Glorglox operates through pairs of image files: the one that is output to the Web and an auxiliary .gif file. When a click is registered on the viewable image, Glorglox determines the color of the pixel at that location in the auxiliary .gif file. The .gmap file is a look-up table of colors and URLs. Here is an example of a .gmap file:

# The auxiliary image _must_ be on the first line.
# everything else is: [value|"default"]<whitespace>[URL]
default http://www.uunet.ca/~tomr/glorglox/demo/error.html
1 http://www.uunet.ca/~tomr/glorglox/demo/gg-bord.html
2 http://www.uunet.ca/~tomr/glorglox/demo/imap.html
3 http://www.uunet.ca/~tomr/glorglox/demo/gg-in.html
4 http://www.uunet.ca/~tomr/glorglox/demo/gg-out.html
5 http://www.uunet.ca/~tomr/glorglox/demo/box.html
6 http://www.uunet.ca/~tomr/glorglox/demo/adv.html
# For example, if you were to click on the words "image mapper",
# glorglox would send you to imap.html, because the pixel value there
# is 2 (red) in the auxiliary image.
# The new nifty NOWHERE URL sends the user back to the page they came
# from!

Notice that colors are specified as numbers between 0 and 255 in a Glorglox .gmap file. This is due to the fact that GIF images use indexed color mode. Consequently, a Glorglox-based imagemap has a maximum of 256 links it can invoke. This should be enough for most purposes.

In RGB color mode, each of the red, green, and blue channels are represented by 8 bits, making for a 24-bit "true color" image. However, many images are very well represented by only 256 unique colors, as long as you get to choose which 256 colors are used. Indexed color mode is a compromise between having 224 = 16,777,216 colors available in an image and having each pixel represented by 1 byte rather than 3 bytes. In the header of a GIF file, a format that uses indexed color mode, there is a look-up table stating what RGB color is associated with which index. Then, each subsequent use of that 1 byte index represents 3 bytes of information.

The usefulness of Glorglox hinges on that it is often easier to create an auxiliary image than to define geometrical shapes in terms of the vertices of their pixels. Quite often, this is indeed the case. As a final note on Glorglox, take a look at the following URL and ponder for a moment how you'd make the imagemap you'll find there using a standard imagemap system:


Imagebuttons-The End of Imagemaps Is Nigh

HTML was originally conceived as a content-description language. Its major conceptual step forward was the inclusion of native ways to deal with hypertextual links. Within these limits, HTML worked well.

Eventually, the demands of real life started to pull HTML in all sorts of funny directions. The pervasiveness of HTML documents and Web browsers and the flexibility of HTTP gave people the idea to start using the Web as a vehicle for information gathering as well as information distributing. ISINDEX tags and the CGI appeared back in the days of text-only browsing. The CGI layer and GUI-based Web browsers with inline images made room for the concept of an imagemap. HTML was no longer as simple as it once was, but it wasn't made so complicated that there was any real reason to complain.

This same CGI revolution also brought forms onto the scene. Forms blew ISINDEX out of the water. The basic set of form input elements very closely mimics the various dialog box options you'll find in a GUI-based operating system such as radio buttons, checkboxes, and selection lists. What forms didn't contain was any sort of graphical interaction capability...until now.

It has been noticed that throughout history, great conceptual advances tend to be independently developed at almost the same time. In the 1600s Leibnitz and Newton developed calculus within years of each other. In the 1800s a gaggle of mathematicians almost simultaneously challenged Euclid's 9th and 10th axioms and independently derived non-Euclidian geometry. In late 1995, I remarked to my programming partners that I wished there was a way to combine forms and imagemaps. About two months later, I noticed imagebuttons online. Funny how these things happen.

The HTML Side of Imagebuttons

I will assume that the reader has a knowledge of the working of HTML forms and will concentrate only on the imagebutton-related tags.

<FORM ACTION="exe/imagebtn.cgi" METHOD=GET>
Zen words of wisdom : <INPUT TYPE=TEXT NAME="zen" VALUE="It is difficult to kill a Âhorse with a flute." SIZE=40> <BR><BR>
<INPUT TYPE=IMAGE NAME="foo" SRC="images/imagebtn.gif" ALT="Optional">

There are some things here that should be familiar by now-the structure of the form statement, a CGI program as its action, and a specified method: in this case, GET.

Beyond the familiar, this form has two slightly unusual aspects to it. First, the explicit strangeness: the <INPUT TYPE=IMAGE> tag. It is this tag that creates an imagebutton within an HTML document. The second and implicit unusual point is that this form has no <INPUT TYPE=SUBMIT> tag! As people who are experienced with forms know, without such a tag, the form will not connect to its action, making the form absolutely useless apart from didactic purposes. Contrary to what you may think, that is not my goal-at least not right here. This is an honest-to-goodness working form. I'll go into how the imagebutton tag makes it so shortly.

An <INPUT TYPE=IMAGE> tag can be inserted into any form just as one would insert any other INPUT tag. The NAME attribute is optional but highly recommended for a reason I'll talk about soon. The VALUE attribute isn't forbidden, but it does nothing in the context of an imagebutton. Beyond these attributes, the ones that are normally found in INPUT tags, any attributes that are legal in IMG tags are legal here: SRC is necessary while ALT, HEIGHT, WIDTH, and BORDER are optional.

Move your mouse pointer on top of the image produced by the <INPUT TYPE=IMAGE> tag. Notice that the pointer doesn't transform itself into a "pointing hand" icon. However, when you click on the image, the form action is invoked.

I have created an example of an imagebutton that you can view at the following URL:


The action of the form that is found at the preceding URL is a program that "spills its guts," as it were. Listing 12.2 is that program.

Listing 12.2. A Perl program that prints GET/POST query information and environment variables.
# the above line may need to be altered depending on where Perl is
# stored on your system

$env_flag = 1; # set to 1 to print environment variables

} elsif ( $ENV{REQUEST_METHOD} eq 'GET' ) {
   $input = $ENV{QUERY_STRING};
} else {
   print "Content-type: text/plain\n\n";
   die "This program doesn't support the <b>$ENV{REQUEST_METHOD}</b> httpd",
   "request method.\n";
$input =~ tr/+/ /;
@fields = split(/\&/,$input);
$input = '';
foreach $i (@fields) {
   ($field,$data) = split(/=/,$i);
   $field =~ s/%(..)/pack("c",hex($1))/ge;
   $data =~ s/%(..)/pack("c",hex($1))/ge;
   $tokens{$field} = $data;
@fields = (); # delete the @fields array

print "Content-type: text/html\n\n";

print <<END;
<HTML><HEAD><TITLE>Imagebutton Form Processing Results</TITLE></HEAD>

print "<TR><TH ALIGN=CENTER VALIGN=TOP NOWRAP COLSPAN=2>Form-based Variables</TH></ÂTR>",
" NOWRAP>Value</TH></TR>\n";
while ( ($a,$b) = each %tokens ) {
if ( $env_flag ) {
   print "<TR><TH ALIGN=CENTER VALIGN=TOP NOWRAP COLSPAN=2>Environment Variables",
   " VALIGN=TOP NOWRAP>Value</TH></TR>\n";
   while ( ($a,$b) = each %ENV ) {
      " VALIGN=TOP>$b</TD></TR>\n";
print "</TABLE></BODY></HTML>\n";

exit 0;

I'll include the report this form/CGI pair generates here, narrowed down to the relevant items:

Form-Based Variables
foo.x 13
foo.y 5
zen It is difficult to kill a horse with a flute.

Environment Variables
QUERY_STRING zen=It+is+difficult+to+kill+a+horse+with+a +flute.&foo.x=13&foo.y=5
HTTP_USER_AGENT Mozilla/3.0b4 (Win95; I)

Comparing Imagemaps and Imagebuttons
Invoked by <A HREF="url"><IMG SRC=imagefile ISMAP></A> Invoked by <FORM ACTION="url"><INPUT ></A> TYPE=IMAGE NAME="name"></FORM> with options for specifying a method in the FORM tag and a name in the INPUT tag
Coordinates transferred toCoordinates transferred as specified by the
server via the QUERY_STRING associated form: by either GET (QUERY_STRING) or POST (STDIN)
Coordinate click information is in the form X,Y where X and Y are numeric values Coordinate click information is in the form name.x=X&name.y=Y as per the GET/POST data passing schemes
Can pass other information only via the PATH_INFO environment variable Other information can be passed via PATH_INFO; other form fields in either the QUERY_STRING or STDIN. If the POST (STDIN) method is used, additional information can be passed through QUERY_STRING.

If a NAME attribute isn't included with the <INPUT TYPE=IMAGE> tag, then coordinate information is passed simply as x=X&y=Y.

But What Does It All Mean?

To begin, imagebuttons are a logical superset of imagemaps for all CGI applications. Put simply, this means that, with only a little fiddling around, you can use imagebuttons anywhere you use imagemaps. For instance, when using the im.cgi code I developed, the following HTML

<A HREF="imagemap.cgi/pathinfo"><IMG SRC="image.gif" ISMAP></A>

can be replaced with

<FORM ACTION="imagemap.cgi/pathinfo"><INPUT TYPE=IMAGE SRC="image.gif"></FORM>

as long as the im.cgi Perl code is modified so that

($x,$y) = split(/,/,$ENV{QUERY_STRING});

is replaced with

($qs = $ENV{QUERY_STRING}) =~ tr/=xy//d; ($x,$y) = split(/&/,$qs);

While you should be able to perform this direct code substitution, I don't recommend it as a practice. First, this is unnecessarily complicated for what could be done simply with imagemaps. Second, if any more input tags find their way into the form, this Perl modification is no longer applicable. Changes to the standard imagemap.c file are also possible, but I can't think of a good reason to do so, and you might end up shooting yourself in the foot.

Through this direct correspondence between imagemaps and imagebuttons, you can do at least everything with an imagebutton that you can with an imagemap. But there are added benefits to imagebuttons, as well, should you choose to exploit them.

Imagebuttons are by their nature form related. This means that you can tie more information to an imagebutton submission than just the x and y coordinates of the mouse-click. This has been all but impossible with imagemaps.

The unique way in which imagemap coordinate information is passed to the CGI program requires special (though minimal) treatment. Because information created by imagebuttons is passed to the CGI program through the same method as any other GET or POST created data, a standard CGI-handling package can be used to extract the information. This is demonstrated by the code I used earlier to show the environment variables in the imagebutton example.

As has been the case in the past, a multistate CGI based on form input has required the HTML code to possess multiple <INPUT TYPE=SUBMIT> tags, each with a different NAME attribute. This same multistate effect can now be accomplished by having the CGI program react differently depending on the x,y coordinate information supplied by the imagebutton-based form submission.

Imagemaps are still the right choice in many situations; they are both intrinsically simple to deal with and have a wealth of preexisting support. There is more documentation for imagemaps, and there are standard code libraries and "built-in" features supported by some browsers and servers (client-side imagemaps and the server-side .map Magic MIME type). However, there is a limit to the capabilities of imagemaps. Imagebuttons can transcend this limit.


For those of you who have survived reading these past 32 pages, I salute you. Here is a quick summary of what I have covered in this chapter.

Imagemaps are special, but they aren't mystical. What makes imagemaps special are the HTML markups that support their use and the unique way that information is passed from client to server.

There is a wealth of pre-existing imagemap-handling programs. In most cases, it will be sufficient to tackle your CGI problem at hand. But still try to understand how these programs work, as well. At their core, they output a specific Location HTTP header based on a comparison of the coordinate information supplied and various geometries gleaned from a map file.

Imagemap handling has become so common that the "labor-saving devices" of client-side imagemaps and server-parsed imagemaps (the .map Magic MIME type) have been introduced. Know how they work and use them as appropriate.

If necessary, or even if just plain desired, you can build your own imagemap handler. If you take this approach, you inherit a lot of responsibility but at the same time you acquire the power to create a CGI solution that fits the needs of your project. Don't be afraid to jump completely beyond the click-in-a-shape paradigm that has come to dominate the imagemap field when you develop your own code.

Finally, if imagemaps can almost (but not quite) do what you want them to, maybe imagebuttons are better suited for your project. Conversely, if you have a form you'd like to "spruce up" and make more attractive or user-friendly, imagebuttons might provide the kick you want.

Happy hacking!