Chapter 31

A Web Coloring Book


CONTENTS


Now that you have learned all about receiving information on the server side through HTML forms and CGI, you might want to do something fun with it. In this chapter, I explain how my interactive coloring book works (http://www.ravenna.com/coloring/) and how you can program something similar.

To the typical Web user, the coloring book might seem almost magical. After all, the Web originally was designed to easily retrieve static documents. But with a solid knowledge of CGI, forms, and the programming language of your choice, you too can create an application just as dynamic and interactive as the coloring book.

The tips and tricks used to get the coloring book to work are simple concepts that you already learned in earlier chapters in this book, spun together to provide a clean and usable interface to put a twist on the traditional coloring book. In addition, the coloring book uses the gd library to do the real-time graphics processing outside of the actual CGI script.

There are three parts to the operation of the coloring book:

Initial user input  Ask the user which picture to color, and set things up.

The coloring iteration  Receive the coordinate and color information; process and output another option.

Other options and housekeeping  Enable users to keep a copy of their work, and delete leftover files.

For better understanding, I will explain each of the necessary tricks as they are implemented to walk you through the operation of the coloring book.

This explanation assumes that you are using NCSA's httpd server. Settings and directives for your own daemon may differ slightly. Although other programming and scripting languages might offer more or less versatility, this coloring book was implemented in Perl v4.036 and C.

Initial User Input and Setup

The first part of the operation of the coloring book provides users with their initial "canvas" to begin coloring.

Users Choose a Picture to Color

A coloring book naturally has several pictures from which people can choose. The first page of the coloring book offers users the opportunity to choose the desired picture and transmit this choice to the CGI program so that it outputs the correct picture. The simplest way of doing this is by using forms and the SELECT input tag, as shown in Figure 31.1.

Figure 31.1: Selecting a picture to color.

The menu system provides a quick description of the picture, with the actual file name encoded in the VALUE field. In this case, the description and the file name were exactly the same, so the VALUE field was not necessary.

<FORM METHOD="POST" ACTION="/cgi-bin/color/coloring.pl">
Select picture to color:
<SELECT NAME="gif">
<OPTION>birthday
<OPTION>christmas
<OPTION>crown
<OPTION>flower
<OPTION>house
<OPTION>snowman
</SELECT>
<INPUT TYPE="submit" VALUE="Time to color!">
</FORM>

As you can see, the submission method of POST was used. This was chosen simply to have the cleanest URLs. But if you want to have a more colorful interface than a form, such as hyperlinks around thumbnail images of the pictures to color, you can use plain HTML and the GET method to transmit the information. The next section of code has the same functionality as the preceding code, but it provides more flexibility in the interface because it doesn't use form input elements; instead, it hard codes the name/value pair in the hyperlink via the QUERY_STRING.

Select picture to color:
<UL>
<LI> <A HREF="/cgi-bin/color/coloring.pl?gif=birthday"> birthday</A>
<LI> <A HREF="/cgi-bin/color/coloring.pl?gif=christmas"> christmas</A>
<LI> <A HREF="/cgi-bin/color/coloring.pl?gif=crown"> crown</A>
<LI> <A HREF="/cgi-bin/color/coloring.pl?gif=flower"> flower</A>
<LI> <A HREF="/cgi-bin/color/coloring.pl?gif=house"> house</A>
<LI> <A HREF="/cgi-bin/color/coloring.pl?gif=snowman"> snowman</A>
</LI>

Caution
Many CGI-decoding programs can decipher only a single submission method. For example, NCSA's post query only handles methods of POST, and its counterpart query handles methods of GET. Be sure that your CGI program can handle whichever method you choose.

Save a Copy of the File for Users to Work On

As you will see in the next section, the same CGI program, coloring.pl, is used for all of the graphics processing, even when users are just starting and there is no image to color. The key to this operation is the fact that no specified color information is being transmitted in the previous form, contrary to how the coloring iteration works.

if (!$FORM{'color'}) ( &cleargif; }

The CGI program recognizes this, and a copy of the fresh "master" picture is made instead of the flood-fill process.

An important point here is that the daemon needs to have write permissions to whichever directory you are using to save these temporary GIFs. The CGI program is running out of its native /cgi-bin/ directory, so directory paths need to be carefully structured to make sure that not only can the daemon write files, but also that these files can be accessed through the Web.

The /tmp directory on UNIX file systems has permissions set to allow all users to write it. In addition, this directory is completely wiped out on occasion, which makes this the ideal environment for storing temporary files. It is necessary to acknowledge that the Web daemon cannot access files outside the DocumentRoot by default, however. As a trivial workaround, a soft link can be made from the top level of the DocumentRoot directory structure to point to the
/tmp directory with the command

ln -s /tmp tmplink

If you were currently at the top of the DocumentRoot, this command would create a virtual subdirectory called tmplink to allow access to the /tmp filespace.

Caution
If you are going to create a soft link within your DocumentRoot and you expect the daemon to follow it when requested, you need to make sure that the access option FollowSymLinks is specified in the access.conf file for the DocumentRoot.

Perhaps a cleaner and more professional way of adding the /tmp directory to the reach of the Web server would be to create an Alias directive in the srm.conf file that would look like this:

Alias /tmplink/ /tmp/

All this does is allow remapping of document requests that begin with /tmplink/ to the actual directory of /tmp/, which lies outside of the DocumentRoot. If your DocumentRoot was the directory /usr/local/etc/httpd/htdocs/, for example, a request of

http://www.yourserver.com/tmplink/file.gif

would cause the daemon to look for the file /tmp/file.gif instead of /usr/local/etc/httpd/htdocs/tmplink/file.gif.

The process ID of the currently running CGI script is used to assign a temporary file name to the GIF being made. Because the ID constantly is incrementing (and eventually resets), it provides an easy way of distinguishing images in progress. This temporary file name is encoded as a HIDDEN form input field in the resulting HTML form that users are presented with, so the next iteration of the coloring knows which picture in progress to use.

Outputting the HTML Page

In addition to hiding the file name in the coloring page, two other elements are necessary. First, users must be able to select which color to use.

<FORM METHOD="POST" ACTION="/cgi-bin/color/coloring.pl">
Selected fill color: <SELECT NAME="color">
<OPTION VALUE="1" > red
<OPTION VALUE="2" > orange
<OPTION VALUE="3" > yellow
<OPTION VALUE="4" > green
<OPTION VALUE="5" > blue
<OPTION VALUE="6" > indigo
<OPTION VALUE="7" > violet
<OPTION VALUE="8" > brown
<OPTION VALUE="9" > black
<OPTION VALUE="10" > white
</SELECT>
<P>
<INPUT TYPE="IMAGE" NAME="coord" SRC="/tmp/6327.gif">
<INPUT TYPE="hidden" NAME="pidold" VALUE="6327">
</FORM>

The gd library uses C routines to do the image processing. For simplicity, numbers are assigned to the colors at the outset in the HTML form. By using a switch statement in the associated C program, the appropriate fill routine with the designated color is executed.

Second, an input type of IMAGE is used as a submission trigger to transmit the form input. With this input type, a hyperlink appears around the image, and the coordinates at the location where the users clicked are submitted to the CGI program with the NAME prepended. The preceding code, for example, would result in a name/value pair of coord.X = 73 and coord.Y = 96 if users click the (73,96) pixel of the image.

The Coloring Iteration

This part is the loop of the coloring book; users can do this as many times as needed until they are done coloring their picture.

User Submission

As illustrated in Figure 31.2, users have in front of them a form with a choice of colors, their current picture, and the file name of the picture hidden from view. If desired, users can stay at this point and still be able to color as long as the temporary GIF file exists; all the critical information necessary to complete a coloring iteration resides in the form.

Figure 31.2: Waiting for users to click in a region.

Tip
You'll notice later that previous temporary images are not deleted immediately. With the file name information hard coded into each of the forms, users can backtrack a few screens and modify a previous image in case they made a mistake and want to redo it.

When users finally do click the image, a new CGI process with a new process ID (temporary file name) starts to perform the coloring iteration.

The gd Binary

Because the CGI program was written in Perl, a separate C program (using the gd library) was needed to perform the flood-fill routine on the GIF image. After it is compiled, this program executes from within the CGI script with several variables on the command line:

color number
X coordinate
Y coordinate
previous process ID (old file name)
current process ID (new file name)

Tip
If you use a program outside of your CGI processor to perform the graphics routines, you must be sure that it finishes before you output the HTML and the associated IMG SRC. Otherwise, users will get a broken image because the image file doesn't exist yet. In Perl, the system() function waits for a completion signal from the command before it continues executing the script.

The gd binary was passed only the raw PID file names of the temporary GIFs, so the first step is to prepend /tmp/ to the front of the names and tack on a .gif to the end. Then, using the gd library routines, the appropriate "in" and "out" files are initialized for processing.

As illustrated earlier, 10 colors were available for the users to choose. The RGB values listed in Table 31.1 were used for each of the defined colors.

Table 31.1. RGB values for colors.

ColorRGB Value
Red255,0,0
Orange255,165,0
Yellow255,255,0
Green0,255,0
Blue0,0,255
Indigo138,43,226
Violet238,130,238
Brown165,42,42
Black0,0,1
White255,255,255

As you can see, the RGB value for black is not (0,0,0). This is because the gd flood-fill routine propagates along its own color until it finds a different-colored border. The original pictures are in pure black and white. If users fill pure black in one of the regions, the next time the region is filled with a different color, the flood-fill routine spills onto the pure black lines that separate the regions. The slight black variation keeps the borders intact.

Finally, as mentioned earlier, a switch statement is programmed to perform the appropriate flood-fill routine on the "out" image. If the color "4" is passed to the gd binary, for example, a routine is hard coded to use the color green.

gdImageFill(out, x, y, green);

Output Another Coloring Page

Using the same tools as the initial page, the current image is displayed back to the users, with the appropriate IMAGE input tags and hidden file name field in the form. As Figure 31.3 shows, an additional twist is preselecting the color they just used as the default.

Figure 31.3: The current color selected.

Tip
Preselecting the previous color used greatly increases the usability of your application. If users are coloring several regions with the same color, you don't want to have to make them select the color each time!

In the following piece of code, which was shown earlier, you may have noticed the additional spaces at the end of the markup tag:

<OPTION VALUE="1" > red
<OPTION VALUE="2" > orange
<OPTION VALUE="3" > yellow
<OPTION VALUE="4" > green
<OPTION VALUE="5" > blue
<OPTION VALUE="6" > indigo
<OPTION VALUE="7" > violet
<OPTION VALUE="8" > brown
<OPTION VALUE="9" > black
<OPTION VALUE="10" > white

These actually are due to an empty variable. During the coloring iteration, the CGI program knows the number of the color to use. Before the HTML is output, the current color is saved in a numbered array corresponding to the color number. Then, when the preceding HTML is output, each element of the array is included with only one of them having been activated. If green was the previous color, for example, the array element $select[4] is assigned the text SELECTED. If each of the array elements is included at the end of their respective markup tag, the resulting HTML would be

<OPTION VALUE="1" > red
<OPTION VALUE="2" > orange
<OPTION VALUE="3" > yellow
<OPTION VALUE="4" SELECTED> green
<OPTION VALUE="5" > blue
<OPTION VALUE="6" > indigo
<OPTION VALUE="7" > violet
<OPTION VALUE="8" > brown
<OPTION VALUE="9" > black
<OPTION VALUE="10" > white

This presents users with the color menu, with green selected as the default (see Fig. 31.4).

Figure 31.4: Green is preselected in the color menu.

Other Options and Housekeeping

Below the IMAGE input, there are three other form submission buttons: DONE COLORING, Download GIF, and Download PostScript. Each of these is an individual form with the hidden file name ready to be submitted for processing (see Fig. 31.5). Figure 31.5: Individual Submit buttons for other options.

Done Coloring

At some point during the coloring process, users will be finished and want to exit. The DONE COLORING button and associated CGI script simply output HTML that thanks them for their interest, as well as a hyperlink back to the introduction page, in case they want to color another picture.

Note
You might be thinking that this would be the ideal time to delete the previous temporary images, or at least the last picture. Not so, because chances are that some people will want to backtrack a page and select one of the other options so that they can keep a copy of their work.

Download GIF

Instead of displaying the image inline, this option sends users the raw GIF image for displaying on its own-most likely in an external viewer. For consistency with the other two buttons, a form-submit button is used for this link, when the following HTML would have sufficed:

<A HREF="/tmp/17882.gif">Download GIF</A>

Instead, a single CGI script is used to redirect the client to the existing GIF file. Specifically, the following output headers are sent back to the browser, which automatically points users to the named file.

Content-type: text/html
Location: /tmp/17882.gif

Download PostScript

With a simple twist added to the previous option, users can download a PostScript version of their images that they can send directly to their printer. In the Download PostScript form, an additional hidden field is included.

<INPUT TYPE="hidden" NAME="type" VALUE="ps">

This hidden field is a flag that tells the same CGI script as the Download GIF option to do some additional processing and redirects the browser to the generated .ps file. Specifically, the pbmplus utilities giftopnm and pnmtops are used to quickly convert the GIF to a PostScript file using the same system() function as the coloring iteration.

system("giftopnm /tmp/17882.gif | pnmtops > /tmp/17882.ps");

Then, similar headers as before are output to the client as soon as the previous process finishes.

Content-type: text/html
Location: /tmp/17882.ps

Caution
Most likely, your Web daemon is running without any particular environment variables set, including PATH, so it won't know where to find the giftopnm and pnmtogif binaries. You might need to include additional directory path information when calling these programs.

Housekeeping

If many people are using your coloring application, chances are that the process ID counter will have reset, and you will be dealing with process ID file names that already exist in your temporary directory. But, as mentioned before, you don't want to delete the files prematurely in case users want to backtrack and modify previous iterations. As a solution, you rely on the popularity of the application to clean up after itself.

A step not mentioned earlier is that the first time the coloring.pl script is executed and a fresh image is copied, the script also scans the temporary directory to check for "old files." In this case, the Perl script looks for files that end in .gif or .ps and that are older than approximately 15 minutes and then deletes them.

opendir (TEMP, "/tmp/");
@tmpfiles = grep(/\.[gp][is][f]?$/, readdir(TEMP));
foreach $tmpfile (@tmpfiles) {
  if ((-M "/tmp/$tmpfile") > .01) {
    unlink("/tmp/$tmpfile");
  }
}
closedir (TEMP);

Final Advice

As you can see, it's really not difficult to build something spectacular with the right tools. The actual code to implement the coloring book is rather trivial. As the World Wide Web evolves even further, it will become even easier to create similar applications.

A new Perl 5 module has been released, for example, that dynamically accesses the gd library routines from within the Perl script, eliminating the need for a separate C binary to handle the graphics processing. Also, new technologies such as HotJava are redefining the boundaries of interactivity, making such applications as an interactive coloring book rather unimpressive.

The good news is that even though older applications like a coloring book that uses CGI might be old hat to you and me, there always will be someone new to the Web who will think it is amazing. Figure 31.6: The finished product.