A text
access counter
A
graphical access counter
As mentioned in
Chapter 1, CGI programs can make your Web pages dynamic, and one popular
application of CGI is the addition of an access counter. An access counter
is a text or graphical representation of the number of times that your Web
page has been requested. Access counters work either by including a CGI
script within an HTML page with Server Side Includes or the <IMG> tag, or
by generating the entire HTML page from a CGI script. When a user requests
the HTML page containing an access counter, the CGI script checks the
current access count, increments it by one, and displays the results in
the Web page. Many Web sites have added access counters to their home
pages. Figure 6.1 displays some ways that access counters can be
displayed.
- As shown in the
figure, access counters can be displayed as text or as an image file
representing the value of the counter. As you might imagine, the graphical
version requires more work in the CGI script than simply displaying the
value of the counter as text. To display the graphic, you must either have
a graphic file for every possible number the counter can reach or you must
construct the graphical image from several image files. If you use the
latter method, you only need to store 10 files, one for each digit 0
through 9. Then, when necessary, your CGI script creates the image file by
putting together the separate GIF graphic files into a single GIF that the
browser displays. For example, when the access counter is at 986, the CGI
script takes the individual GIF files of the digits 9, 8, and 6 and
combines them into one GIF that is the combination of all three. GIF image
files are just one of many image file formats. Two other types of image
file types that you can work with are JPEG and XBM images, because many
Web browsers can display them. However, this chapter uses GIF image files,
as you'll see in the section "A Graphical Access Counter" later in this
chapter.
This chapter starts
with an example text access counter. You should read this section even if
you want to create a graphical access counter, because the basic
functionality of the two types of counters is the same. The second half of
the chapter explains how to create a graphical access counter. In this
case, the process is a bit more challenging, but the results are a lot
more impressive.
A text
access counter
The text access
counter displays the count number in plain text within the HTML page.
Figure 6.1 displayed several text and graphical access counters. Because
the text counter uses plain text, it is much easier to implement than a
graphical counter. The biggest decision is how to call the access counter
script. It's easiest to do this with Server Side Includes, but not all
systems have Server Side Includes enabled. The following sections describe
how to create the text access counter script and explain two different
ways of adding it to your Web pages.
Creating the Counter Script
Before starting
your CGI script, you should spend a moment thinking about what it must do.
The script needs access to the current value of the access counter. For
this information to be available, you must store it in a text file. Every
time your text access counter script is called, it opens the counter file
in which the current access count is stored, increments the access count,
and saves the new number in the counter file. After the counter value has
been incremented, the number is included in the Web page.
The
Increment subroutine
Your text access
counter first needs to read in the current access count, which is stored
in the counter file. In this chapter, the text file containing the access
count is named count.dat. You can read in the value stored in count.dat
simply by opening the file and reading the first line into the variable
$count,, as in the following lines of Perl code:
open(COUNT, "$file") || die "Content-type: text/html\n\nCannot open counter
file!";
$count
= <COUNT>; close(COUNT);
The first line opens the file whose name is stored in the variable $file.
You set this variable when you put together the entire text access counter
script shown in Listing 6.2. For now, just note that $file will contain
the path and name of the file that stores the current number of accesses
to your Web page. The second part of the first line of Perl code contains
a die statement that will terminate the program and output the contents of
the string:
Content-type: text/html\n\nCannot open counter file!
The || operator between the open and die statements is the logical OR
operator. When this operator is between the two statements, the Perl
interpreter first tries to execute the open statement. If the open is
successful, the Perl interpreter moves on to the next line of code.
However, if the file cannot be opened, the Perl interpreter executes the
die statement. This is a common way to verify that a file is successfully
opened and to terminate the Perl program if it is not.
The second line of
code reads in the contents of the first line of the counter file from the
input stream <COUNT> and places it into the variable $count. After the
line has been read in from the file, you can close the input stream
<COUNT> by using the close command, as in the third line of code.
Now that you have
the code to get the current value of the access counter, you need to
increment the value and write the new value to the counter file. You can
easily increment the value by using the ++ operator. If you append this
operator to a variable name, the integer value stored in the variable is
increased by 1. For example, if the current value of the access counter
were 2, the Perl code
would change the value to 3.
Once the access
count has been incremented, you need to store the new value in the counter
file for the next time the script is called. The following three lines of
Perl code open the counter file and write the new access count to the
file:
open(COUNT, ">$file") || die "Content-type: text/html\n\nCannot open counter file!";
print COUNT $count;
close(COUNT);
Again, you use the open statement to open the counter file. However, this
time you output to the file instead of receiving input from the file.
Notice the > operator before the $file variable in the open statement.
This operator opens the file for output. If the file already exists, it is
overwritten. The second line prints the contents of the $count variable
into the file.
You now have all
the code necessary to read in the current access count, increment it, and
write it back to the file. To make your code easier to read, place it
within a subroutine called Increment. Listing 6.1 shows the contents of
the Increment subroutine.
Listing 6.1: The Increment Subroutine
sub Increment {
local ($count);
# Get the current value of the access counter.
open(COUNT, "$file") || die "Content-type: text/html\n\nCannot open
counter file!";
$count = <COUNT>;
close(COUNT);
# Increment the access counter
$count++;
# Store the value of the counter in the counter file.
open(COUNT, ">$file") || die "Content-type: text/html\n\nCannot open
counter file!";
print COUNT $count;
close(COUNT);
return $count;
}
|
Besides the sub Increment line, which declares the subroutine, the only
lines of code that have been added are the local statement at the
beginning and the return statement at the end. The local statement,
declares that the $count variable is local only to the Increment
subroutine. A local variable is a variable that exists only within a
portion of your Perl code, usually within a subroutine. If a variable with
the same name existed outside the subroutine, Perl would consider it a
different variable than the one that is declared local within the
subroutine. Declaring your subroutine's variables as local helps to keep
your subroutines from overwriting values of global variables. A global
variable is one that is accessible throughout the entire Perl program,
including any subroutines in the same Perl file. In Listing 6.1, the
variable $file is a global variable. Listing 6.2 in the next section will
show where this variable is set.
The
other line of Perl code added to your Increment subroutine is the return
statement
This statement causes the subroutine to return the current value of the
count as its return value. In Perl, every subroutine returns a value. You
can set this return value explicitly by using the return statement. The
next section demonstrates how this return value is used.
The
complete text access counter script
Now
that it can increment the counter, your script just needs to return the
value of the counter for display in the Web page. You can do this by using
the following three lines of Perl:
$access_number = &Increment;
print "Content-type: text/html\n\n";
print $access_number;
The first line calls the Increment subroutine and assigns the return value
to the variable $access_number. The next line prints the required parsed
header. The last line prints the value of the access counter.
You
can place the preceding lines of Perl code, along with the code for the
Increment subroutine, in a file called access.pl. Listing 6.2 shows the
complete access.pl file. Notice that the global variable $file is set at
the beginning of the file and is used within the Increment subroutine.
Also, if you use this counter on a machine running a version of Windows,
remove the first line and change the path for the count.dat file.
Listing 6.2: The access.pl file
#!/usr/local/bin/perl
# All users need to change the value of this
# variable to the path for their machine. Windows
# users need to use a format similar to
# "c:\\robertm\\count.dat"
$file = "/users/robertm/count.dat";
$access_number = &Increment;
print "Content-type: text/html\n\n";
print $access_number;
sub Increment {
local ($count);
# Get the current value of the access counter.
open(COUNT, "$file") || die "Content-type: text/html\n\nCannot open
counter file!";
$count = <COUNT>;
close(COUNT);
# Increment the access counter
;
$count++;
# Store the value of the counter in the counter file.
open(COUNT, ">$file") || die "Content-type: text/html\n\nCannot open
counter file!";
print COUNT $count;
close(COUNT);
return $count;
}
|
Seeding the
Counter
Before you can use your access counter, you need to supply the file
count.dat with the initial count value. This is called seeding the
counter. Normally, you can simply create a text file with the number 0 on
the first line. This will start your counter at zero. The first person
visiting your Web page with the counter would see the access number 1.
(For the graphical counter script, you need to seed the count.dat file
with the number 1 for the first person visiting your Web page to see the
access number 1. You'll learn why in the section "A Graphical Access
Counter" later in this chapter.) If you want to start your counter at a
higher number, just change the first line of the count.dat file to that
number. For example, if your Web page has already been accessed 342 times,
create a text file named count.dat with the number 342 on the first line
(use 343 for the graphical counter).
Adding
the Counter to Your HTML Page
With the access.pl and count.dat files completed, you are now ready to add
the counter to your Web page. The easiest way to do so is to use a Server
Side Include. However, after reading about security issues in Chapter 3
you may have disabled Server Side Includes on your system. Or you may be
using a Web server on which the administrator has turned off Server Side
Includes and does not wish to re-enable them. For these reasons, this
section also describes an alternative way of adding your text access
counter that doesn't require a Server Side Include. This technique takes
more work to implement, however, because you have to make changes to the
access.pl script.
With
Server Side Includes
As
mentioned, it's easiest to implement your text access counter using Server
Side Includes. Simply choose which Web page you want to display your
counter and add some surrounding text and the following Server Side
Include:
<!--#exec cgi="/cgi-bin/access.pl" -->
If Server Side Includes are enabled on your Web server, the Web server
will parse this line before sending it to the user's Web browser. The CGI
script access.pl is executed and the output from the script is substituted
for the preceding line in the HTML.
For example, the following
HTML code demonstrates how to add the Server Side Include statement to
your existing HTML page:
<HTML>
<HEAD>
<TITLE>Example of Text Access Counter</TITLE>
</HEAD>
<BODY>
<H1>Text Access Counter</H1>
This is my Home page. Thank you for visiting. Please come again.
<P>This page has been accessed
<!--#exec cgi="/cgi-bin/access.pl" -->
times.
</BODY>
</HTML>
As you can see, the Server Side Include was added along with some
surrounding text to explain what the number represents. The surrounding
text is "This page has been accessed x times." The x represents where the
Web server would insert the value returned by your access.pl script.
Figure 6.2 shows how the HTML page will appear when the access count is
13.
Without Server Side Includes
Suppose you want to use the text access counter without using a Server
Side Include. It is still possible, but you have to generate the entire
page from your CGI script. You can change your HTML page to a template and
have your CGI script read in the template, place the current value of the
counter within the template, and output the entire page to the user's Web
browser.
Creating the Template File
The
first thing to do is to change the HTML file, which you want to contain
the counter, into a template file. Do this by changing the file's
extension to .tmpl (.tmp if you want to use a three-character extension).
Then edit the file, inserting the text that will surround the counter
value. Use the value XXXX as a placeholder for where you will insert the
access count. For example, using the HTML from the preceding example, you
would place the HTML code in a file called text.tmpl, whose contents are
shown here:
<HTML>
<HEAD>
<TITLE>Example of Text Access Counter</TITLE>
</HEAD>
<BODY>
<H1>Text Access Counter</H1>
This is my Home page. Thank you for visiting. Please come again.
<P>This page has been accessed XXXX times.
</BODY>
</HTML>
Modifying the access.pl Script
After you have created the template file, you must modify your access.pl
script to open the template file, read in the contents, change the XXXX
placeholder into the current access count, and output the HTML code to the
Web browser. It is easiest to add this code in a subroutine called
Display_Page and simply call the subroutine after the counter has been
incremented. Listing 6.3 contains the Perl code for this subroutine.
Listing 6.3: The Display_Page Subroutine
sub Display_Page {
local ($count) = @_;
# All users need to change the following path
# to be correct for their system. Windows users
# need it in the form "c:\\robertm\\text.tmpl".
local ($html) = "/users/robertm/text.tmpl";
local (@template);
open(TEMPLATE, "$html") || die "Content-type: text/html\n\nCannot
open template!";
@template = <TEMPLATE>;
close(TEMPLATE);
# Be sure to change the index number to the correct one
# for your HTML template. Count down from the first line
# being at Ø to the line containing the XXXX placeholder.
$template[7] =~ s/XXXX/$count/e;
print "Content-type: text/html\n\n";
print @template;
}
|
The Display_Page subroutine begins by declaring the two variables and one
array that will be local to this subroutine. The variable $html stores the
name and path to the template file that the access counter will be added
to. Then the template file is opened and all of its contents are read into
the @template array. In this example, the placeholder for the access
number XXXX is on the eighth line of text.tmpl file. When the file is read
into the array, the eighth line is placed in the eighth element of the
@template array. Because the index of Perl arrays begins with 0, the
eighth element is at index 7. So, the statement
$template[7] =~ s/XXXX/$count/e;
takes the eighth element of the @template array and replaces the access
number placeholder with the current count. If you use this subroutine for
your HTML template, you need to change the index of the @template array
element to the correct index for your template file.
After the placeholder for the access count is changed to the actual count
number, the required parsed header and the contents of the @template array
are returned to the user's Web browser. Because this entire page is being
generated from the CGI script, you need to change all the links to this
page to call your CGI script, access.pl, which should now be modified to
contain the new Display_Page subroutine. Listing 6.4 is the Perl code for
the new access.pl script. Notice how the two lines that previously output
the parsed header and the access count have been replaced with a call to
the Display_Page subroutine. Don't forget to modify the paths for the
$file and $html variables to contain the correct paths for your system.
Also, Windows users must remember to remove the first line of the script
file. Figure 6.3 shows how Netscape displays a call to the modified
access.pl script when the counter value is 13. Notice that the page looks
the same as the one in Figure 6.2, which is called with a Server Side
Include.
Listing 6.4: The modified access.pl script
#!/usr/local/bin/perl
# All users need to change the value of this
# variable to the path for their machine. Windows
# users need to use a format similar to
# "c:\\robertm\\count.dat"
$file = "/users/robertm/count.dat";
$access_number = &Increment;
&Display_Page($access_number);
sub Increment {
local ($count);
# Get the current value of the access counter.
open(COUNT, "$file") || die "Content-type: text/html\n\nCannot open
counter file!";
$count = <COUNT>;
close(COUNT);
# Increment the access counter
$count++;
# Store the value of the counter in the counter file.
open(COUNT, ">$file") || die "Content-type: text/html\n\nCannot open
counter file!";
print COUNT $count;
close(COUNT);
return $count;
}
sub Display_Page {
local ($count) = @_;
# All users need to change the following path
# to be correct for their system. Windows users
# need it in the form "c:\\robertm\\text.tmpl".
local ($html) = "/users/robertm/text.tmpl";
local (@template);
open(TEMPLATE, "$html") || die "Content-type: text/html\n\nCannot
open template!";
@template = <TEMPLATE>;
close(TEMPLATE);
# Be sure to change the index number to the correct one
# for your HTML template. Count down from the first line
# being at Ø to the line containing the XXXX placeholder.
$template[7] =~ s/XXXX/$count/e;
print "Content-type: text/html\n\n";
print @template;
}
|
A
graphical access counter
The
graphical access counter presents more challenges than the text counter.
The graphical counter has the same basic functionality as the text access
counter, such as reading in the counter value from the counter file,
incrementing the counter value, and saving the value in the counter file.
However, it must also create a graphical image that is a representation of
the access count. For example, if the access count were 293, the graphical
access counter would return an image containing the number 293.
The
easiest way to accomplish this is to have a GIF file for every possible
number the access counter can reach. For example, if the counter value
were 769, your graphical counter script would only have to return the GIF
file containing the number 769. However, doing the counter this way wastes
a great deal of disk space. If your access counter only goes up to 1,000,
you have to create and save 1,000 different GIF files on your Web server.
Even if these files are 1K or less apiece, this still amounts to nearly 1
megabyte in disk storage. Maybe 1 megabyte doesn't seem like that big of a
deal to you, but keep in mind that 1,000 accesses on the World Wide Web is
a very small number. Realistically, if you have a moderately popular site,
you want your counter to reach at least 100,000. It would take a
tremendous amount of disk space and a lot of time to create files for this
many numbers.
The
more difficult method for returning a graphical image for the counter
value is to create the graphical image dynamically from ten existing GIF
files for the digits 0 through 9. Your CGI script would construct the
graphical counter image from these digits by combining the GIF files in a
new image. For example, if the counter value were 769, your CGI script
would take the digit file for the 7, append the digit file for the 6, and
then append the digit file for the 9, creating a GIF image representing
the number 769.
As
you can imagine, this approach is much more difficult than the text access
counter example. To write the code for this GIF creation, you need to know
quite a bit about the GIF file format and must be able to write routines
that create a single GIF image from multiple existing GIF files. Although
you could probably learn how to do this, there is a easier way.
Recall from Chapter 3 that one of the reasons to use a common language for
CGI scripting is to be able to use library routines written by other
people. Well, now is a perfect time to take advantage of one such library.
Thomas Boutell has written a graphics library of functions in C that
handle GIF creation and manipulation. This library, called gd, is
available from http://www.boutell.com/gd/. His only restriction on the use
of the gd library is that the credit and copyright given to the Quest
Protein Database Center, Cold Spring Harbor Labs remain intact in all
derived works. In other words, if you distribute the gd graphics library
files with your code you cannot remove the copyright lines in the gd
files. Although Lincoln Stein has written a Perl interface to the gd
graphics library called GD.pm, the following graphical access counter does
not use GD.pm. To use GD.pm, you must still download and install the gd
graphics library, and GD.pm may not be easily installed on all systems.
For this reason, the C programming language is used to create the
graphical counter script for this graphical access counter example.
Before you start creating the graphical counter script, you should
download and install the gd graphics library. You can download the source
files from Thomas Boutell's Web site, http://www.boutell.com/gd/. Once you
have downloaded and uncompressed the source files for gd, you should find
a README file that contains directions for installing the graphics
library.
Most of the code for the graphical access counter script is logically
similar to the code for the text access counter. There is one major
difference, however. In the text access counter example, the counter file
contained the current count. When the text access counter script is
called, it increments the counter value before displaying it in the Web
page. The graphical counter, in contrast, stores the next counter value in
the counter file instead of the current counter value. When the graphical
counter script is called, it displays the value read from the file as the
current count, not the incremented value. This makes it easier to create
the GIF image, as explained in more detail in the section "Creating the
GIF Image."
Incrementing the Counter
Incrementing the graphical access counter is similar to incrementing the
text access counter. However, the C code for the graphical counter looks
dramatically different than the Perl code for the text access counter.
Listing 6.5 contains the C code that reads in the counter value from the
counter file, increments the value, and writes the new counter value to
the counter file.
Listing 6.5: The C code for incrementing the counter
/* Counter file handle */
FILE *counter;
/* string version of counter file value */
char s_count[2Ø];
/* Counter file
All users need to change the path to the actual
path for their machines. Windows users need to
use a path in the form "c:\\robertm\\count.dat" */
char *cnt_file = "/users/robertm/count.dat";
/* integer version of count */
int count;
/* open the counter file */
counter = fopen(cnt_file, "r");
/* check whether the file was opened successfully */
if (!counter) {
printf("Content-type: text/html\n\nCannot open counter file!\n");
return(Ø);
} else {
/* if the file was opened, read in the string containing the count */
fgets(s_count, 2Ø, counter);
fclose(counter);
/* extract the integer value of the count from the string */
sscanf(s_count, "%d", &count);
/* increment the count */
count++;
/* open the count file for output */
counter = fopen(cnt_file, "w");
/* check whether the file was opened successfully */
if (!counter) {
printf("Content-type: text/html\n\nCannot open counter file!\n");
return(Ø);
} else {
/* if the file was opened, write the count value to it */
fprintf(counter, "%d", count);
fclose(counter);
}
}
|
The first several lines define the variables that will be used in this
code. After the variable declarations, the counter file is opened with the
line
counter = fopen(cnt_file, "r");
This line opens an input stream, named counter, to the file whose name and
path are in the cnt_file variable. The "r" string designates that the file
will be opened for input only. It is always a good idea to verify whether
the file has been successfully opened. The line
does this by making sure that the counter holds a nonzero value. If the
file cannot be opened, the counter variable is set to 0. If counter is
equal to 0, the first part of the if...else statement executes. In this
section, you output an error message that the file could not be opened.
Under
the else portion of the if...else, the access counter value is read in
from the file with the line
fgets(s_count, 2Ø, counter);
This statement reads up to 20 characters from the file pointed to by the
counter variable and places the characters in the s_count string.
(Actually, s_count is an array of characters, which is the C equivalent of
a string.)
After
the access counter value is read from the file, the stream to the file is
closed. The next line
sscanf(s_count, "%d", &count);
converts the counter value from a string to an integer and stores the
integer value in the count variable, which can then be incremented with
the
statement.
Finally, the counter file is once more opened, and the incremented value
of the access count is written to the file. Notice how the open statement
counter = fopen(cnt_file, "w");
has the string "w" rather than the string "r". The "w" means to open the
file for output. The value of the access counter is written to the file
with the line
fprintf(counter, "%d", count);
which prints the decimal value of the count variable into the file pointed
to by the counter variable, which is the access counter file.
Creating the GIF Image
Once the counter value has been read in from the count.dat file, the GIF
image can be created. Recall that you create the GIF image from the
counter value you read in from the file, not from the incremented value.
This is because the incremented version is stored in an integer, and it is
easier to create the GIF based on a value stored in a string. Remember, a
string in C is actually an array of characters. So, if the counter value
is 769, the s_count variable contains the string 769. Each individual
digit can easily be accessed by addressing the specific array element. For
example, the first digit, 7, can be accessed by using the variable
s_count[0], which is the first element in the s_count array. In this way,
you can loop over each digit in the counter value and append the
corresponding GIF image file to the GIF image you are creating. With the
769 example, you would first add the contents of the 7 GIF image file,
next the contents of the 6 GIF image file, and then the contents of the 9
GIF image file.
Before looping over the s_count array and creating the new GIF image, you
must either create or download the ten GIF images representing each digit.
For this example, the ten GIF images have been downloaded from the Digit
Mania Web site, http://cervantes.learningco.com/kevin/digits/index.html.
This Web site contains many digit styles for use in graphical access
counter programs. The style used here is the tiny style, which was created
by Muhammad A. Muquit. Each one of these GIF digits is a 9-pixel wide by
13-pixel high image. To implement this example exactly as shown, you need
to download these images and place them in a directory that your graphical
counter CGI script can access.
Now
that you have the ten GIF images, one for each digit, you can write the C
code for creating the graphical counter image that will be displayed
within your Web page. First you must create a blank version of the
graphical counter image that is the same width and height as your final
image. By doing this, you allocate the memory necessary to store the final
graphical counter image and you create the image to which the individual
digit images can be copied. Because the digit images will each be 13
pixels high, your final image will also be 13 pixels high. However, the
width must be calculated based on the number of digits the number
contains. Remember that the length of the s_count array is also the number
of digits your graphical counter image will contain. Therefore, you can
calculate the width of your final image by multiplying the length of the
s_count array by the width of each individual digit image, which in this
case is 9 pixels. You can do this with the code
width = strlen(s_count) * real_width;
where real_width is an integer that is set to 9 earlier in the program.
Listings 6.6 and 6.7 later in this chapter show where the real_width
variable is set.
After
you have calculated the dimensions of the graphical counter image, you can
use the gdImageCreate function from the gd library to create the new
graphical counter image. This function takes two parameters, a width and
height, and creates a blank GIF image with the specified dimensions. The C
statement
im_final = gdImageCreate(width, real_height);
calls the gdImageCreate function, passing it the width value that was just
calculated and the height of the individual digit images, which is stored
in the variable real_height. Listings 6.7 and 6.8 will show the code that
sets the real_height variable to 13, which is the height for the tiny
style digit images. Notice that the return value of the function
gdImageCreate is assigned to the variable im_final. This variable is a
pointer to the image that was just created, and it is through this
variable that you will reference the image later on in the code.
Next you need to loop over
the array s_count and append the digit image files to the im_final image
you just created. The loop you use will be similar to the for loops used
in Perl. The line
for (i=Ø; i < strlen(s_count); i++) {
starts the for loop. The loop executes once for every element in the
s_count array and the variable i represents the index of the current
s_count array element. For example, if s_count holds the string 769, the
for loop executes three times. The first time, the i variable is 0, so the
first digit of the s_count array can be accessed by using the variable
s_count[i]. As i is incremented, the variable s_count[i] moves to the next
elements in the array. For each iteration of the loop, you should check
the value of the s_count[i] array element, open the digit image file that
corresponds to that value, and append the contents of that digit's GIF
image file to the graphical counter image you are creating. Listing 6.6
shows the C code for the entire for loop.
Listing 6.6: The graphical counter for loop
for (i=Ø; i < strlen(s_count); i++) {
switch (s_count[i]) {
case 'Ø':
image = fopen(zero_gif, "rb");
break;
case '1':
image = fopen(one_gif, "rb");
break;
case '2':
image = fopen(two_gif, "rb");
break;
case '3':
image = fopen(three_gif, "rb");
break;
case '4':
image = fopen(four_gif, "rb");
break;
case '5':
image = fopen(five_gif, "rb");
break;
case '6':
image = fopen(six_gif, "rb");
break;
case '7':
image = fopen(seven_gif, "rb");
break;
case '8':
image = fopen(eight_gif, "rb");
break;
case '9':
image = fopen(nine_gif, "rb");
break;
default :
break;
}
if (!image) {
printf("Content-type: text/html\n\nCannot open image file!\n");
return(Ø);
} else {
im_file = gdImageCreateFromGif(image);
fclose(image);
gdImageCopy(im_final, im_file,
destx, Ø, Ø, Ø, real_width, real_height);
destx += real_width;
gdImageDestroy(im_file);
}
}
|
The first section of the for loop uses the switch conditional, which
compares the value of the s_count[i] array element with each case until a
match is found. When a matching case is found, the code under the case is
executed. If no match is found, the code under the default is executed.
For example, if the s_count array contains 769, the first iteration of the
loop would have the s_count[i] element equal to 7. The switch would
execute the code under the case '7': line, which is
image = fopen(seven_gif, "rb");
break;
The first line opens the 7 digit's GIF image file, whose path and file
name are stored in the seven_gif variable. The code setting this variable
is shown in Listings 6.6 and 6.7. The difference between this fopen
statement, and the ones in the previous section is the "rb" string. This
string specifies to open the file for binary input.
At the end of the switch
statement, an if...else conditional verifies that the image file has been
successfully opened. In the else portion of the conditional, the digit
image is appended to the graphical counter image being created. This is
done with the following lines of code:
im_file = gdImageCreateFromGif(image);
fclose(image);
gdImageCopy(im_final, im_file,
destx, Ø, Ø, Ø, real_width, real_height);
destx += real_width;
gdImageDestroy(im_file);
The first line creates a new gd graphical image from the digit's GIF image
file. The pointer im_file points to this image. After the im_file image
has been created, you can close the stream to the file with the fclose
statement. The statement
gdImageCopy(im_final, im_file,
destx, Ø, Ø, Ø, real_width, real_height);
appends the digit's image just read in from the image file to the
graphical counter image that is being constructed. The parameters for
gdImageCopy are the following: a pointer to the destination image, a
pointer to the source image, the x coordinate in the destination image to
start placing the copy, the y coordinate in the destination image to start
placing the copy, the x coordinate in the source image to begin the copy,
the y coordinate in the source image to begin the copy, the number of
pixels to copy in the x direction, the number of pixels to copy in the y
direction. In this statement, the destination image is the graphical
counter image you are creating, which is pointed to with the im_final
variable. The source image is the image that was just read in from the
digit's GIF file, and is pointed to with the im_file. The x coordinate for
the destination of the copy is a variable, destx. This variable is
increased after each iteration of the loop, in the line
destx += real_width;
For example, with the s_count array containing 769, the first iteration
copies the 7 digit's GIF image file to the im_final image. Because this is
the first image being copied, the x coordinate in the destination image is
0. However, on the next iteration of the loop, the im_final image already
contains the 7 digit. So, the x coordinate has to be moved over 9 pixels.
Why 9 pixels? Because that is the width of each individual digit's GIF
image. After the 6 digit's image file is added to the im_final image, the
destx variable is increased by another 9 pixels. So, when the 9 digit is
added to the im_final image, the destx variable is 18, which is the sum of
the widths of the previous two digits 7 and 6. Finally, after the im_file
image is appended to the im_final image, the im_file image is destroyed
with the gdImageDestroy command. Listing 6.7 shows the all of the C code
for creating the graphical counter image.
Listing 6.7: The C code for creating the graphical counter image
/* Image file handle */
FILE *image;
/* Input and output images */
gdImagePtr im_file, im_final;
/* Image files
All users need to change the paths to the actual
paths for their machines. Windows users need to
use paths in the form "c:\\robertm\\Øtiny.gif" */
char *zero_gif = "/users/robertm/0tiny.gif";
char *one_gif = "/users/robertm/1tiny.gif";
char *two_gif = "/users/robertm/2tiny.gif";
char *three_gif = "/users/robertm/3tiny.gif";
char *four_gif = "/users/robertm/4tiny.gif";
char *five_gif = "/users/robertm/5tiny.gif";
char *six_gif = "/users/robertm/6tiny.gif";
char *seven_gif = "/users/robertm/7tiny.gif";
char *eight_gif = "/users/robertm/8tiny.gif";
char *nine_gif = "/users/robertm/9tiny.gif";
/* string version of counter file value */
char s_count[2Ø];
/* width of image created image,
x position for new image, and loop variable */
int width;
int destx = Ø;
int i;
/* the real_height is the height (in pixels) for all of the image files.
the real_width is the width (in pixels) for all of the image files. */
int real_height = 13;
int real_width = 9;
width = strlen(s_count) * real_width;
im_final = gdImageCreate(width, real_height);
for (i=0; i < strlen(s_count); i++) {
switch (s_count[i]) {
case 'Ø':
image = fopen(zero_gif, "rb");
break;
case '1':
image = fopen(one_gif, "rb");
break;
case '2':
image = fopen(two_gif, "rb");
break;
case '3':
image = fopen(three_gif, "rb");
break;
case '4':
image = fopen(four_gif, "rb");
break;
case '5':
image = fopen(five_gif, "rb");
break;
case '6':
image = fopen(six_gif, "rb");
break;
case '7':
image = fopen(seven_gif, "rb");
break;
case '8':
image = fopen(eight_gif, "rb");
break;
case '9':
image = fopen(nine_gif, "rb");
break;
default :
break;
}
if (!image) {
printf("Content-type: text/html\n\nCannot open image file!\n");
return(0);
} else {
im_file = gdImageCreateFromGif(image);
fclose(image);
gdImageCopy(im_final, im_file,
destx, Ø, Ø, Ø, real_width, real_height);
destx += real_width;
gdImageDestroy(im_file);
}
}
|
Returning the Graphical
Counter Image
Now that the counter value is
incremented and saved to the file and the graphical counter image is
created, you are ready to return the graphical counter image from your CGI
script. Just before returning the image, you can call the gd library
function
gdImageInterlace(im_final, 1);
which interlaces the im_final image. Interlacing displays the GIF image
incrementally as it is downloaded in certain Web browsers that support
interlaced GIFs. An interlaced GIF first appears somewhat distorted and
then gradually become more clear as the remainder of the file is
downloaded across the Internet. When you use interlaced GIF images, the
users viewing your Web pages see an image more quickly, even if it's a
little distorted. This makes it appear as though the whole image is
downloading more quickly. Because the graphical counter image is not very
large, interlacing is not imperative. However, if you used one of the
larger digit styles available from the Digit Mania Web page, you would
probably want to interlace your image.
Before outputting the
graphical counter image, you must return a parsed header, as you did for
the text access counter. However, the parsed header for the graphical
image counter needs to specify that the data being returned is a GIF
image. The following lines output the parsed header and the graphical
counter image.
printf("Content-type: image/gif\n\n");
gdImageGif(im_final, stdout);
With the graphical counter image returned for inclusion in your Web page,
you can now destroy the im_final image with the statement
gdImageDestroy(im_final);
You should always call the gdImageDestroy function when you are finished
with a gd Image because it frees up any memory that was allocated for that
image.
The Graphical Counter
Script
In the previous two sections,
you developed all of the code for the graphical counter script. Listing
6.8 puts all of the code together and places the necessary #include
statements at the beginning. You need to make the specified changes to the
variable assignments that assign the path and file names of the counter
and digit image files. Because the graphical counter script is written in
C, you also need to compile the code before you run it. The procedure for
doing so varies depending on what system you are on and which C compiler
you have. Using your C compiler, compile the graphical-counter.c file and
create the graphical-counter executable file. (If your system restricts
you to an eight-character file name, call the file gph-cnt.c and the
executable gph-cnt.)
Listing 6.8: The graphical-counter.c File
#include <stdio.h>
#include <string.h>
#include "gd.h"
int main(void)
{
/* Counter file handle and image file handle */
FILE *counter;
FILE *image;
/* Input and output images */
gdImagePtr im_file, im_final;
/* Counter file and image files
All users need to change the paths to the actual
paths for their machines. Windows users need to
use paths in the form "c:\\robertm\\count.dat" */
char *cnt_file = "/users/robertm/count.dat";
char *zero_gif = "/users/robertm/Øtiny.gif";
char *one_gif = "/users/robertm/1tiny.gif";
char *two_gif = "/users/robertm/2tiny.gif";
char *three_gif = "/users/robertm/3tiny.gif";
char *four_gif = "/users/robertm/4tiny.gif";
char *five_gif = "/users/robertm/5tiny.gif";
char *six_gif = "/users/robertm/6tiny.gif";
char *seven_gif = "/users/robertm/7tiny.gif";
char *eight_gif = "/users/robertm/8tiny.gif";
char *nine_gif = "/users/robertm/9tiny.gif";
/* string version of counter file value */
char s_count[2Ø];
/* integer version of count, width of image,
x position for new image, and loop variable */
int count;
int width;
int destx = Ø;
int i;
/* the real_height is the height (in pixels) for all of the image files.
the real_width is the width (in pixels) for all of the image files.
Note: If you want to use different digit image files, replace these
numbers with the width and height (in pixels) of the digit image
files you will be using. */
int real_height = 13;
int real_width = 9;
/* open the counter file */
counter = fopen(cnt_file, "r");
/* check whether the file was opened successfully */
if (!counter) {
printf("Content-type: text/html\n\nCannot open counter file!\n");
return(Ø);
} else {
/* if the file was opened, read in the string containing the count */
fgets(s_count, 2Ø, counter);
fclose(counter);
/* extract the integer value of the count from the string */
sscanf(s_count, "%d", &count);
/* increment the count */
count++;
/* open the count file for output */
counter = fopen(cnt_file, "w");
/* check whether the file was opened successfully */
if (!counter) {
printf("Content-type: text/html\n\nCannot open counter file!\n");
return(Ø);
} else {
/* if the file was opened, write the count value to it */
fprintf(counter, "%d", count);
fclose(counter);
}
}
width = strlen(s_count) * real_width;
im_final = gdImageCreate(width, real_height);
for (i=Ø; i < strlen(s_count); i++) {
switch (s_count[i]) {
case 'Ø':
image = fopen(zero_gif, "rb");
break;
case '1':
image = fopen(one_gif, "rb");
break;
case '2':
image = fopen(two_gif, "rb");
break;
case '3':
image = fopen(three_gif, "rb");
break;
case '4':
image = fopen(four_gif, "rb");
break;
case '5':
image = fopen(five_gif, "rb");
break;
case '6':
image = fopen(six_gif, "rb");
break;
case '7':
image = fopen(seven_gif, "rb");
break;
case '8':
image = fopen(eight_gif, "rb");
break;
case '9':
image = fopen(nine_gif, "rb");
break;
default :
break;
}
if (!image) {
printf("Content-type: text/html\n\nCannot open image file!\n");
return(Ø);
} else {
im_file = gdImageCreateFromGif(image);
fclose(image);
gdImageCopy(im_final, im_file,
destx, Ø, Ø, Ø, real_width, real_height);
destx += real_width;
gdImageDestroy(im_file);
}
}
gdImageInterlace(im_final, 1);
printf("Content-type: image/gif\n\n");
gdImageGif(im_final, stdout);
gdImageDestroy(im_final);
}
|
Calling the
Counter with the <IMG> Tag
With the graphical counter
script completed, you are ready to add the graphical access counter to
your Web page. As with the text access counter, you need to create and
seed the count.dat file. If you didn't follow the text access counter
example, read the section "Seeding the Counter" earlier in this chapter.
The graphical access counter is called from the <IMG> HTML tag. The line
<IMG SRC="/cgi-bin/graphical-counter">
calls the graphical-counter file and displays the results returned from
the script. Listing 6.9 contains the HTML code for an example using the
graphical counter script, and Figure 6.4 shows how the page would appear
in the Netscape browser.
Listing 6.9: HTML Example Using the Graphical Counter
<HTML>
<HEAD>
<TITLE>Example of the Graphical Access Counter</TITLE>
</HEAD>
<BODY>
<H1>Graphical Access Counter</H1>
This is my Home page. Thank you for visiting. Please come again.
<P>This page has been accessed <IMG SRC="/cgi-bin/graphical-counter">
times.
</BODY>
</HTML>
|