programming web graphics with perl

30
Programming Web Graphics with Perl & GNU Software By Shawn P. Wallace 1st Edition February 1999 1-56592-478-9, Order Number: 4789 470 pages, $29.95 Sample Chapter 8: Image Maps Image maps allow a web developer to associate actions with regions of an image. A user's click may activate a link to another page, run a server-side script, or launch a client-side JavaScript program. In the typical nerdy hipster parlance of the Web, these regions are known as "hot spots." The requirements for a working image map are: An image that can be included as an inline image on a web page (generally this means GIF, JPEG, or PNG). Some means of displaying the image and finding the coordinates of the regions you want to define. If a client-side map, a <MAP> element in the same document that defines the regions of the image map.

Upload: gopi1111

Post on 26-Dec-2015

18 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Programming Web Graphics With Perl

Programming Web Graphics with Perl & GNU Software

By Shawn P. Wallace 1st Edition February 1999 1-56592-478-9, Order Number: 4789 470 pages, $29.95

Sample Chapter 8: Image Maps

 

Image maps allow a web developer to associate actions with regions of an image. A user's click may activate a link to another page, run a server-side script, or launch a client-side JavaScript program. In the typical nerdy hipster parlance of the Web, these regions are known as "hot spots."

The requirements for a working image map are:

An image that can be included as an inline image on a web page (generally this means GIF, JPEG, or PNG).

Some means of displaying the image and finding the coordinates of the regions you want to define.

If a client-side map, a <MAP> element in the same document that defines the regions of the image map.

If a server-side map, a map file on the server that defines the regions of the image map in a format that the server recognizes.

If a server-side map, an image map program that handles the resolution of the region from the coordinate clicked by the user (most web servers now come with this program preconfigured or built-in, so you probably don't have to worry about it).

The most difficult part of dealing with image maps from a programming or automation point of view is the generation of the coordinates for the regions within the image. This generally requires some intervention by the author. There

Page 2: Programming Web Graphics With Perl

are some ways of setting up a site so that certain types of image maps may be generated automatically.

Several people and companies have created tools for making it easier to designate regions within an image map. A few of these free or inexpensive tools are discussed at the end of the chapter.

Client-Side Versus Server-Side

In the beginning there was only one option for implementing clickable image maps: the image map program that ran on the NCSA web server. This configuration is now known as the server-side image map. Over the next few years the inadequacies of this approach became apparent and an alternative means of processing the image map within the user agent was proposed and popularly accepted. This approach is known as a client-side image map. The differences are as follows:

Client-sideThe "map" associated with the image is embedded within the same document as the image. When the user clicks on an image, the coordinates of the point are interpreted by the user agent and resolved by looking at the map for the image. If the point is within a region, the target URL is followed by the user agent. Many browsers also provide contextual information based on the internal map by displaying the URL associated with a region in the status bar of the browser when the mouse passes over the region.

Server-sideThe map associated with the image resides on the server in a text file that generally ends with a .map extension. This map file is specified in an HTML document by the HREF attribute of an anchor element that contains the image. When the user clicks on an image, the coordinates of the point are sent to the server as an HTTP request, and the server attempts to resolve the request by parsing the map file. If the point is within one of the defined regions, that region's URL is followed and returned to the client. With server-side maps, the browser has no idea about the region definitions and cannot give feedback to the user.

Client-side maps are preferable to server-side maps for several reasons, the most important of which is that they offer a better response time to the users actions. They are also more efficient in their use of bandwidth, and once a page is loaded,

Page 3: Programming Web Graphics With Perl

they don't necessarily require an open connection to the server. Take, for example, a server-side image map whose regions are not clearly defined. If the user requires ten clicks to successfully find the "hot spot," that results in a lot of network traffic with no results. Here's what the server log could look like for this frustrating session:

dyn090e.shemp.net - - [18/Apr/1998:21:24:16 -0400]

"GET /shawn/face.map?52,61 HTTP/1.1" 200 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:18 -0400]

"GET /shawn/face.map?123,60 HTTP/1.1" 200 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:19 -0400]

"GET /shawn/face.map?211,90 HTTP/1.1" 200 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:20 -0400]

"GET /shawn/face.map?95,320 HTTP/1.1" 200 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:21 -0400]

"GET /shawn/face.map?21,302 HTTP/1.1" 200 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:22 -0400]

"GET /shawn/face.map?247,341 HTTP/1.1" 200 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:23 -0400]

"GET /shawn/face.map?248,103 HTTP/1.1" 200 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:25 -0400]

"GET /shawn/face.map?65,80 HTTP/1.1" 200 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:27 -0400]

"GET /shawn/face.map?36,205 HTTP/1.1" 200 -

Page 4: Programming Web Graphics With Perl

dyn090e.shemp.net - - [18/Apr/1998:21:24:31 -0400]

"GET /shawn/face.map?260,196 HTTP/1.1" 302 -

dyn090e.shemp.net - - [18/Apr/1998:21:24:32 -0400]

"GET /panicband.html HTTP/1.0" 302 -

Another major advantage to using client-side maps is that they allow the user agent to provide the user feedback about which regions of the image map are "hot" and which are not. This is especially important for images where the defined regions are not visually obvious.

The last important reason why you should generally choose to use client-side maps is that they provide text-based browsers the ability to interpret the options embedded in the image map description and represent them as a textual list of links.[1] A client-side image map rendered in Lynx, for example, will first appear as [USEMAP] (unless the image has an ALT tag specified, in which case that text will be displayed). When this link is selected, the following menu is provided, where each numbered item is a link to the appropriate URL:

facemap MAP: http://www.shemp.org/index.html#facemap 1. Ear 2. Nose 3. Eye

Having said all that nasty stuff about how awful server-side image maps are, I'll take several steps back and say that they do have their uses. If you have an image map that is meant to be used on many pages, aserver side map will allow you to share a map file between many images. Or you may have a very complicated image map that would be too bulky to include as part of a document. Or you may want to generate an image map dynamically and reference it from a static HTML page. We will look at some of these applications later in the chapter.

While most browsers support client-side image maps, some older browsers do not (specifically, Netscape Navigator before Version 2.0 or Internet Explorer before Version 3.0). To accommodate those users, you may implement both client- and server-side image maps within the same tag. In this case, the

Page 5: Programming Web Graphics With Perl

user agent will use the client-side map description named internalmap if it is able, and failing that, it will use the server-side map described in mapfile.map:

<A HREF = "mapfile.map"> <IMG SRC = "images/imagemap.gif" ISMAP USEMAP="#internalmap"></A>

Implementing Client-Side Maps

The USEMAP attribute of the <IMG> tag indicates that the image is a client-side image map. The USEMAP attribute is assigned a reference to a named <MAP> element that exists separately within the document. Multiple images within a document may share the same region definitions by simply referring to the same <MAP> element. For example, the following HTML element indicates that the image calledface.gif (shown in Figure 8-1) should be associated with the <MAP> element named facemap:

<!-- A Client-side image map --><IMG SRC="face.gif" USEMAP="#facemap">

Figure 8-1. An image map

  

The <MAP> tag

The definition for a client-side map is constructed with the <MAP> element. The <MAP> element is defined for HTML specifications after Version 2.0.

The <MAP> tag must contain a NAME attribute, which assigns the name that the <IMG> tag's USEMAP attribute will specify, as shown in the earlier example. The <MAP> tag requires a start tag and a closing tag. The content of the <MAP> element can be a number of AREA elements which define the regions. Defining the regions with a list of AREA elements would look like this:

Page 6: Programming Web Graphics With Perl

<MAP NAME="facemap"> <AREA SHAPE=rect COORDS="252,168, 273,246" HREF="http://www.shemp.org/panicband.html" ALT="Ear"> <AREA SHAPE=poly COORDS="141,166, 125,236, 193,232, 164,162" HREF="http://www.shemp.org/frodus.html" ALT="Nose"> <AREA SHAPE=circle COORDS="100,180,30" HREF="http://www.shemp.org/as220.html" ALT="Eye"></MAP>

If regions defined within the same <MAP> element overlap with each other, the region that is defined first will be the one that takes precedence. This definition can be used effectively to produce complicated clickable areas with the simple shape primitives available with the AREA element. For example, to produce a clickable 20 × 20 circle, surrounded by a "dead region" of 20 pixels, which in turn is surrounded by another 20-pixel-wide clickable concentric ring, use the following ordering of AREA statements (whose regions are pictured in Figure 8-2):

<IMG SRC="circles.gif" USEMAP="#circles"><MAP NAME="circles"> <AREA SHAPE=circle COORDS="75,75,20" HREF="inner.html"> <AREA SHAPE=circle COORDS="75,75,40" NOHREF > <AREA SHAPE=circle COORDS="75,75,60" HREF="outer.html"></MAP>

Figure 8-2. Clickable concentric rings in a client-side map

  

The <AREA> tag

The <AREA> tag defines a region of a client-side image map. It consists of a lone start tag and one or more of the following attributes:

SHAPE

Page 7: Programming Web Graphics With Perl

The SHAPE attribute defines the shape of the clickable region. Acceptable values are:

default: The entire image rect: A rectangular region circle: A circular region poly: A polygonal regionCOORDS

The COORDS attribute is a comma-delimited list of coordinates that define the edges of the region. The coordinate system is based on (0, 0) being the upper left corner of the image. The number and order of coordinates depends on the shape of the region:

default: No coordinates are specified. rect: A rectangular region needs two coordinates, the upper left and lower right corners. circle: A circular region needs one coordinate and a value for the radius of the circle, in pixels. poly: For a polygonal region the coordinates should be a list of three or more points that define the vertices of the polygon. It is assumed that the side of the polygon between the last and first points are joined. A polygon can have at most 100 vertices.HREF

The HREF attribute specifies the URL of the resource or link that will be requested when the user clicks on this region.

NOHREF

The NOHREF is a Boolean attribute that specifies that the region has no action associated with it. It can be used to "knock out" area regions defined by other AREA tags within the same <MAP> element.

ALT

The ALT attribute provides a short description that may be used by the browser to provide contextual information about the clickable areas, or may be used by browsers that are not equipped to deal with images.

ONFOCUS

This attribute may be used to assign a JavaScript event handler script to the region that will be called whenever the mouse is over that region.

ONBLUR

This attribute can be used in a similar way as the ONFOCUS attribute, but it is activated when the region of the image map loses the focus.

Server-Side Maps

Page 8: Programming Web Graphics With Perl

The ISMAP Boolean attribute in an <IMG> tag indicates that the image should be treated as a server-side image map. The <IMG> element should be enclosed in a hyperlink that points to the map file on the server to be used by the image:

<!-- A Server-side image map --><A HREF = "face.map"> <IMG SRC = "images/facemenu.gif" ISMAP></A>

When the user clicks on a point in the image, its coordinates are sent to the server by appending a question mark and the x and y coordinates to the URL given in the HREF attribute. Text-based user agents will always send a (0, 0) coordinate when the link is selected. In the above example, the server would get the following HTTP request when a user clicks on the point at (65, 80):

GET /face.map?65,80 HTTP/1.1

 

NOTE: In general, it is a good practice to specify the complete URL for each of the actions in the map file. Relative path names (such as ../foo.html ) will be interpreted by the web server asrelative to the location of the map file. This could lead to confusion, so just use the whole URL.

The map file resides on the server and provides a description of the various regions within the image map, in much the same way as the area statements of a client-side map. There are a few different formats for server-side map files. The most popular is the original NCSA format (used by Apache, WebStar, WebSite, etc.), which describes the regions as in the following example:

# NCSA-style image map file = face.map # The Right Ear: upper left and lower left points of a rectanglerect http://www.shemp.org/example/panicband.html 252,168 273,246  # The Nose: a polygon described by four pointspoly http://www.shemp.org/example/frodus.html 141,166 125,236 193,232 164,162  # The Right Eye: A circle with a center point and a radiuscircle http://www.shemp.org/example/as220.html 100,180 30

The older, deprecated format for map files is the CERN format, which orders the coordinates and URL slightly differently:

circ (306,204) 7 http://www.shemp.org/example/foobar.html

Page 9: Programming Web Graphics With Perl

rect (324,131) (353,261) http://www.shemp.org/example/bargum.htmlcirc (103,279) 43 http://www.shemp.org/example/foogum.html

 

NOTE: A note about scaled image maps: if an image is scaled by the web browser with the WIDTH and HEIGHT attributes (by specifying dimensions larger or smaller than that of the source image), and the image is used as an image map, the clickable regions of the map are not scaled along with the image. Each of the clickable regions are defined as points within the frame of the image and have no relation to the content of the image. This should be obvious, but is still worth noting.

An Alternative: Image Buttons with the<INPUT> Tag

The HTML form element can also be used to simulate a clickable image map. If an INPUT field of a form is given the type image, the field will be rendered as an inline image which, when clicked, will perform the action given in the ACTION field of the form enclosing it. This type of element is generally called an Image Button. An example of an Image Button that calls a script called processclick.cgi is:

<FORM ACTION="http://www.shemp.net/cgi-bin/processclick.cgi"> <INPUT TYPE="image" NAME="img" SRC="menu.gif"> <INPUT TYPE="hidden" NAME="dept" SRC="hardware"></FORM>

When the action specified by the form is a script, the script is sent the x and y coordinates that the user clicked and the other information contained in the form. This is why the Image Button is useful; scripts called from regular image maps have direct access to the clicked coordinates, but cannot be sent additional input. The x and y values for this example would be passed as parameters named img.x and img.y, like this:

http://www.shemp.net/cgi-bin/processclick.cgi?img.x=189&img.y=122&dept=hardware

This example could be useful in that the same menu image and CGI script could be used for different departments, and the script could handle the context, rather than having a separate CGI script for each department's image map.

As another example, the following CGI script will allow the user to click on the Image Button and, using the GD module, will flood-fill the area indicated by the

Page 10: Programming Web Graphics With Perl

user's click with the color specified by the user, as seen in Figure 8-3. Here's the HTML for the page:

<!--An example of an Image Button within an INPUT field --><HTML><BODY><FORM ACTION="cgi-bin/floodfill.cgi"> <INPUT TYPE="image" NAME="image1" SRC="face.gif"> <INPUT TYPE="hidden" NAME="name" VALUE="face.gif"> <SELECT NAME="color"> <OPTION VALUE="255,0,0">Red</OPTION> <OPTION VALUE="0,255,0">Green</OPTION> <OPTION VALUE="0,0,255">Blue</OPTION> </SELECT></FORM></BODY></HTML>

And here's the code, which needs to be set up as a CGI script with all the proper permissions and such:

#!/usr/local/bin/perl # floodfill.cgi# A CGI script that will fill a region that is specified # by an Image Button from the floodfill.html page.#use strict;use CGI;use GD; # Get the parameters:# 1. x and y coordinates of click# 2. The name of the image file (passed from hidden field)# 3. The color with which to fill the region#my $query = new CGI;my $x = $query->param('image1.x'); my $y = $query->param('image1.y'); my $imagename = $query->param('name');my @color = split /,/, $query->param('color'); my $fillcolor; # The fillcolor is the index in the colormap # Open the image and read it in as a GIF#open(INFILE, "$imagename") || die "Couldn't open file.";my $image = newFromGif GD::Image(\*INFILE);close INFILE; # If the color map is full, substitute the nearest color,# otherwise allocate the color.#if ($image->colorsTotal() == 256) { $fillcolor = $image->colorClosest(@color);

Page 11: Programming Web Graphics With Perl

} else { $fillcolor = $image->colorAllocate(@color);} $image->fill($x, $y, $fillcolor); # fill the regionprint $query->header(-type => 'image/gif'); # print the headerbinmode(STDOUT); print $image->gif; # send the image

Figure 8-3. An INPUT field may be included as a part of an HTML form element as an alternative clickable image map method. These two screenshots show the image before (top)

and after (bottom) the form was submitted by clicking on the forehead in the image.

  

Page 12: Programming Web Graphics With Perl

The INPUT tag essentially provides the same interface as a server-side image map, but it requires that you implement the translation of the clicked coordinate into a meaningful form. It can be used in applications where the few shape-defining primitives of the image map are not robust enough to describe a very complicated shape. The Perl CGI::ImageMap module provides some routines for dealing with this implementation, and for emulating the behavior of regular image maps.

Image Map Tools

There are basically two ways of specifying the coordinates of the regions within an image map. The first is the "brute force" or "map hacker" method of using an image display program such as xv, Paint Shop Pro, or display to bring up the image in a window and manually picking out the points that define the shapes. The second method is with a WYSIWYG image map editor.

Most of the commercial image editing packages aimed at web developers (such as Adobe ImageReady and Macromedia Fireworks) offer built-in tools for making image map regions fairly painless. Some web server packages also come with image map tools. The following is a collection of tools that are either freeware or shareware; ftp addresses are provided when available.

Map Hacker Tools for Generating Coordinates

These tools offer the ability to manually pick out coordinates for use in image maps:

xvxv is a useful all-purpose interactive image manipulation program written by John Bradley for the X Window System. It offers the ability to quickly manipulate many image file formats, and provides a nice interface for common tasks such as cropping, sizing, and manipulating the color tables of images. It can be used to find coordinates for defining image map regions by clicking the mouse inside the image window and drawing a selection rectangle, which can be fine-tuned with handles. The info window will display the size and position of the rectangular region. Individual points of a polygon or a circle must be picked out individually. Version 2.x was free (Version 3.0 and later are shareware). Version 2.0 of xv is available from ftp://ftp.x.org/R5contrib/ and Version 3.0 may be had atftp://ftp.cis.upenn.edu/pub/xv/.

Page 13: Programming Web Graphics With Perl

displayThe display program comes with the ImageMagick distribution (see Chapter 5, Industrial-Strength Graphics Scripting with PerlMagick) and will display a wide range of graphics file formats on any client connected to an X server. It provides a graphical interface to many of the ImageMagick manipulation functions and acts very nicely as a tool for quickly determining the coordinates of an image map region. Figure 8-4 shows the information provided by display when an area of an image is selected. It is available from the ImageMagick home page athttp://www.wizards.dupont.com/cristy/ImageMagick.html.

Figure 8-4. ImageMagick's display utility may be used (on X-based platforms) to gather coordinates for image map files

  Gimp

As of this writing there is not a Gimp plug-in that will automatically generate an image map file in the manner of the WYSIWYG tools. The Gimp can be used in a similar way as xv or display, however, and I'm sure someone will write a plug-in at some point. Check http://registry.gimp.org for Gimp plug-ins. See Chapter 7, Web Graphics with the Gimp, for more on the Gimp.

WYSIWYG Map Editing Tools

There are several commercial packages that have built-in support for image maps, such as Adobe Fireworks or most WYSIWYG HTML authoring programs. There are also several free utilities that do just as good a job:

MapEdit

Page 14: Programming Web Graphics With Perl

MapEdit is an image map editor written in Java, which means it should be pretty much platform-independent. It allows you to define regions within an image with several drawing tools. It is primarily intended for creating client-side maps, but it will also export NCSA style server-side map files. MapEdit will prompt you for an HTML file in which the image map is referenced and will change the file directly. If there are multiple image maps within a file it will provide a selection list. When an image is selected and a region is defined, the program will prompt you for the information to be added to that region's AREA tag. MapEdit is available at http://www.boutell.com/mapedit/.

Client-Side Image Map Editor (CSIME)CSIME is another Java-based editor for creating client-side image maps. It is platform-independent (if you can run Java) and free. You can get it at http://web.wwa.com/~myc/csime/.

Map ThisMap This is a free image map utility for Windows. It also creates server-side (NCSA or CERN) or client-side map files. Map This is available at http://galadriel.ecaetc.ohio-state.edu/tc/mt/.

Translating Server-Side Image Maps toClient-Side Format

The NCSA server-side image map format can be a convenient way of storing the information about an image map's region because it is a very simple format stored in a portable text file. It is also good form to offer a server-side version of an image map script as a backup to the client-side map. You may find it helpful to have a script that will translate a server-side map file into a client-side format for these reasons or because you are "modernizing" legacy image maps.

The following script will take an NCSA-format server-side map file as an argument and print it to the command line for cutting and pasting into an HTML document (or for inclusion in a larger HTML formatting project). It handles two special cases that crop up when dealing with older-format server-side image maps:

1. Circular regions were sometimes described by two (x, y) coordinates--a center point and a point on the outer edge of the circle--rather than the client-side syntax of center point and the radius.

Page 15: Programming Web Graphics With Perl

2. If the default region is listed first in a client-side map it will mask the definitions of other regions. This script will move default region descriptions to the end of the map element.

The translation script, called servertoclient.pl, is called with the name of a server-side map file as a parameter. Here is the code:

#!/usr/local/bin/perl -w # servertoclient.pl# Converts NCSA-format image map files to client-side format#use strict; my $servermap = $ARGV[0];my $validshape = "default|rect|circle|poly";my (@lines, @endlines, $shape, $url, @coords, $coordstring);my $count = 0; # We might need these, if there are circles defined with four values#my ($x1, $y1, $x2, $y2, $radius); # The file is read in first and stored in a list of lines # so that we can move any default regions at the top of the file#if ($servermap) { open(INFILE, $servermap) || die "Can't open file $servermap...\n"; while (<INFILE>) { if ($_ =~ /^default/) { push @endlines, $_; } else { push @lines, $_; } } push @lines, @endlines; # tack on any default definitions close INFILE;} else { die "Please specify a server-side image map file...\n";} print "<IMG SRC=\"**image name goes here**\" USEMAP=\"#$servermap\">\n";print "<MAP NAME=\"$servermap\">\n"; foreach my $line (@lines) { $count++; if ( ($line =~ /^#/) || ($line eq "\n") ) { # A comment or blank line; do nothing } else { # The format of the map file is: # SHAPE (arbitrary whitespace) URL (WS) COORD1 (WS) COORD2... # Where the coordinates may be separated by commas or by # arbitrary whitespace. This regex should do the job. #

Page 16: Programming Web Graphics With Perl

($shape, $url, @coords) = split /\s+?/, $line; if ($shape =~ m/$validshape/o) { # The coords list may still contain x,y chunks; # split the coordinates into a discrete list # @coords = map {split ','} @coords; # Check to see if it is an old circle definition; # two x,y coordinates means it is... # if (($shape=~/circle/) && (@coords == 4)) { ($x1, $y1, $x2, $y2) = @coords;  # Good ol' pythagorean theorem # $radius = int(sqrt(abs($x2-$x1)**2 + abs($y2-$y1)**2)); @coords = ($x1, $y1, $radius); } # In a client-side definition, all coordinates are # separated by commas... # $coordstring = join ",", @coords; print "<AREA SHAPE=$shape\n"; print " COORDS=\"$coordstring\"\n"; print " HREF=\"$url\">\n"; } } }print "</MAP>\n";

Image Maps on the Fly: A Clickable "Wander" Engine

As an interesting example of generating clickable image maps on the fly, let's create a web page that will be driven by what we'll call a wander engine. The web page will allow the user to wander through a landscape by clicking on directional links, and the changing landscape will appear as a dynamically generated image (see Figure 8-5). Each landscape area will be called a room. Each room can contain objects that will be overlaid on the background by the CGI script that dynamically creates the image (the background used in this example is pictured in Figure 8-6 and the three objects are in Figure 8-7). The composite image will be an image map where each of the objects can be clicked on to perform some action.

Figure 8-5. The wander engine allows the user to navigate a landscape with objects by clicking on navigational links or on the objects themselves

Page 17: Programming Web Graphics With Perl

  

Figure 8-6. The background image for room 3 of the "wander"

  

Figure 8-7. The image files for objects 2, 3, 4, and 6 in the example

  

All of the information about a room is contained in the rooms.db database. For this example, the database will be implemented using Shishir Gundavaram's Sprite module (available from CPAN), which uses SQL queries to access the data and stores the data as simple text files. The fields in the rooms.db database are:

The room number The filename of the background image A description of the room The rooms numbers to the north, south, east, and west A list of the objects in the room

Page 18: Programming Web Graphics With Perl

Here is the rooms.db file that we will be using for this example:

Room::Background::Description::North::South::East::West::Objects1::images/room1.gif::You are standing on a hillside.::2::3::4::5::2,3,4,62::images/room2.gif::You are in a maze of twisty wormholes, all alike.::6::7::8::9::5

The information for each of the objects is stored in the objects.db database. The fields in the object database are:

The object number The filename of the image file for the object A descriptive name for the object The x and y values for the coordinate that specifies the placement of

the upper left corner of the image The URL of the action associated with the image

The object database would look something like this:

Object::File::Name::X::Y::URL1::images/foogum.gif::some gum::50::50::http://www.shemp.org/eat.cgi?item=12::images/worm.gif::a sad worm::28::98::http://www.shemp.org/wormhole.cgi3::images/tree.gif::a lumpy tree::100::15::http://www.shemp.org/tree.html4::images/flowers.gif::a patch of flowers::275::20::http://www.shemp.org/f.html5::images/bargum.gif::some bargum::70::100::http://www.shemp.org/bargum.cgi6::images/apple.gif::a juicy apple::185::120::http://www.shemp.org/eat.cgi?item=6

The CGI script that generates the web page is called wander.cgi. It is called by passing it a room number as a parameter, as in the URL http://shemp.net/cgi-bin/rooms.cgi?room=1. This script does a number of things:

1. Retrieves the information about the current room and the objects in that room from the room and object databases.

2. Sorts the objects in order of increasing area. This is so that when we add the AREA elements to the <MAP> element, the smallest images will be added first and will get precedence over the larger images. Also, when the image is created, the object images must be added in the reverse order, so that the larger objects are added first and the smaller objects added later.

3. Prints the HTML for the resulting page. This page includes a description for a client-side image map (a <MAP> element) that is generated by the data retrieved from the object database, and an<IMG> element that calls the drawroom.cgi script to dynamically

Page 19: Programming Web Graphics With Perl

create the image of the room. The background image and the objects (if any) are passed to the drawroom.cgi script in the following form:

 

<IMG SRC="drawroom.cgi?background=backgroundname,0,0 &object1=object1name,x,y &object2=...">

The navigation links and description for the room are also appended after the image. Here is the code for wander.cgi:

#!/usr/local/bin/perl -wuse strict;use CGI;use Image::Size;use Sprite; # The Sprite module is used to manage the databases my ($number, $background, $description, $north, $south, $east, $west, $objectlist);my (%file, %name, %x, %y, %width, %height, %area, %url); # Retrieve the parameters passed in via name value pairs#my $query = new CGI;my $room = $query->param('room'); # Get relevant info from the room database with the Sprite module.# Sprite allows you to use SQL queries with simple flat text files.# my $db = new Sprite;$db->set_delimiter('Read', '::'); # Sprite may need to know your operating system; valid strings are:# Unix, Win95, Windows95, MSDOS, NT, WinNT, OS2, VMS, or MacOS. $db->set_os('Unix');  # Print the header for the HTML page...do it here so that error# messages can be sent to the browser...#print $query->header(-type=>'text/html'); print <<EndHead;<HTML><HEAD><TITLE>Wander Room #$room</TITLE></HEAD> <BODY>EndHead # Query the room database (rooms.db) and select all of the columns of # data for the room in question.

Page 20: Programming Web Graphics With Perl

#my @data = $db->sql (<<EndSQLQuery); select * from rooms.db where (Room = "$room")EndSQLQuery # The data is returned as a multi-dimensional array.# The first element gives the return status of the query.#if (!(shift @data)) { die "There was an error reading from the room database!<BR></BODY></HTML>";} # De-reference the return data and store it in our local room variables#foreach my $row (@data) { ($number, $background, $description, $north, $south, $east, $west, $objectlist) = @$row;} # The object list for the room is stored in the database # as a comma-delimited list#my @objects = split ",", $objectlist; # Find information about the objects in the room by querying the object# database (objects.db)...#foreach my $object (@objects) { @data = $db->sql (<<EndSQLQuery); select * from objects.db where (Object = "$object")EndSQLQuery  if (!(shift @data)) { print "There was an error reading from the object database!<BR>"; }  # De-reference the object data and store it in the various # object info hashes # foreach my $row (@data) { $file{$object} = @$row[1], $name{$object} = @$row[2], $x{$object} = @$row[3], $y{$object} = @$row[4], $url{$object} = @$row[5]; ($width{$object}, $height{$object}) = imgsize($file{$object}); $area{$object} = $width{$object} * $height{$object}; }} $db->close; # Sort the objects by size first, in order of increasing area.# The objects should be added to the image from largest to smallest.# The objects should be listed in the MAP element from smallest to

Page 21: Programming Web Graphics With Perl

# largest.#@objects = sort { $area{$a} <=> $area{$b} } @objects; # Print the image tag that will call the image creation script#print "<IMG SRC=\"drawroom.cgi?background=$background,0,0";my $count = 0; foreach my $object (@objects) { $count++; print "&object$count=".$file{$object}; print ",$x{$object},$y{$object}";}print "\" USEMAP=\"#Room$room\">"; # Print the room description and the navigation links#print <<EndHTML;<P>$description</P><A HREF="wander.cgi?room=$north">North</A> | <A HREF="wander.cgi?room=$south">South</A> | <A HREF="wander.cgi?room=$east">East</A> | <A HREF="wander.cgi?room=$west">West</A> <BR>EndHTML # Print the map element. Each object will be clickable and will# have a url action associated with it.#print "<MAP NAME=\"Room$room\">\n";for my $object (@objects) { print "<AREA HREF=\"$url{$object}\""; print " SHAPE=rect"; print " COORDS=\"$x{$object},$y{$object},"; print $width{$object} + $x{$object} . ","; print $height{$object} + $y{$object} . "\">"; print "There is $name{$object} here.<BR>\n";}print "</MAP>";print "</BODY></HTML>";

The drawroom.cgi script uses the GD module to combine the background and objects into a single image on the fly. It is called from the image tag of the page generated by wander.cgi with parameters indicating the background GIF file and the individual object GIF files. Each parameter consists of a comma-delimited list of the image filename and the x, y offset of each object. The background image has a hard-coded offset of 0, 0 to keep a consistent format. The URL that would invoke the drawroom.cgi script would look something like:

<IMG SRC="drawroom.cgi?background=images/background.gif,0,0&object1=images/frobotz3.gif,100,100&object2=images/frobotz1.gif,150,100&object3=images/frobotz2.gif,0,0" USEMAP="#Room2">

Page 22: Programming Web Graphics With Perl

The code for drawroom.cgi is as follows:

#!/usr/local/bin/perl -w  use strict;use CGI;use GD; my (%filename, %x, %y); # Get the parameters. Since there is not a fixed number of objects# in any particular room, we must get the list of parameter names # and iterate over the number of parameters.#my $query = CGI->new;my @paramnames = $query->param; # Each parameter consists of a comma-delimited string containing# the filename, and x and y offset of the object. #foreach my $paramname (@paramnames) { ( $filename{$paramname}, $x{$paramname}, $y{$paramname} ) = split ",", $query->param($paramname); } open(INFILE, $filename{'background'});my $background = newFromGif GD::Image(\*INFILE); my ($overlay, $white);

One important thing to note is the order that the objects are placed onto the background image. Each of the objects was sorted by area in the wander.cgi script and passed into drawroom.cgi in order of increasing size. The largest object should be placed first, and smaller objects placed on top. This will reduce the risk of larger objects completely obscuring smaller objects in case their regions overlap. Note that this is the reverse of the order that the regions are listed in the <MAP> element.

# Copy the objects onto the background. # Grep will return only the parameter names beginning with the string `object.'#for (reverse (grep /^object/, @paramnames)) { open INFILE, $filename{$_}; $overlay = newFromGif GD::Image(\*INFILE); $white = $overlay->colorClosest(255, 255, 255); $overlay->transparent($white); $background->copy($overlay, $x{$_}, $y{$_}, 0, 0, $overlay->getBounds());} # Print the MIME header, then the GIF data

Page 23: Programming Web Graphics With Perl

#print $query->header(-type => 'image/gif', -expires=>'-1s');binmode(STDOUT);print STDOUT $background->gif;