This chapter covers how to create GIF images from within a Perl program. The output of the programs developed here can be used in HTML pages or for other graphical applications. If you have not already done so, please read Chapter 20, "An Introduction to Web Pages and CGI," for some of the background you'll need for this chapter.
When writing Perl applications, it's often necessary to display the results of your application graphically. One common way to display these results is to print them in columns and pipe the output to a display program such as gnuplot.
For most applications, gnuplot works great for plotting charts or columnar data. By using gnuplot, you can print many types of charts, using different types of axes, colors, and so on. It's possible to write a script for gnuplot to extract data from columns from within a data file. I won't go into too much detail here. The purpose of this chapter is to show you how to use Perl to control gnuplot, not how to program gnuplot. Type help at the gnuplot prompt to get an interactive manual that contains details on how to program in gnuplot.
Though this scheme of using gnuplot works well for most applications, it does have a few drawbacks:
To extract a GIF image from a plot generated by a plotting program such as gnuplot, you can use yet another utility, the Portable BitMap (pbmutils) package. The pbmutils package is a general-purpose image format conversion tool available from many Ftp sites as the netpbm.tar.gz file.
Before I go too far in generating images, let's use a tool to view the output of the images that we'll be creating. For this chapter, we'll be using the xv (version 3.1) tool set by John Bradley. The xv tool is remarkably practical for interactively viewing images in many formats. It also performs resizing, cropping, and color palette modifications for images. The tool is available from sunsite.unc.edu and various other sites. Search for xv. The 3.1 version was copyrighted by John Bradley in 1994. The xv tool proved to be invaluable in debugging the output of the programs in this chapter. You can use any image-viewing tool you like, depending on your platform.
Now that we have the means to create and display images, let's see how to perform image plotting with Perl and gnuplot.
First, here's a simple example of how to plot two-dimensional data with gnuplot. For the data, we'll use the file with columnar data, part of which is shown in Listing 32.1.
Listing 32.1. Input data file.
930830 32.375 31.750 32.000 3700.0 118.4000
930831 32.250 31.625 32.125 3962.2 127.2857
930902 31.750 31.375 31.500 3721.8 117.2367
930903 31.500 31.125 31.375 3447.6 108.1685
930907 32.000 30.750 31.000 4425.0 137.1750
930908 31.625 30.125 31.500 10847.8 341.7057
930909 32.750 31.250 32.375 10338.6 334.7122
930910 33.375 32.500 32.750 9323.2 305.3348
930913 33.000 32.375 32.375 3809.2 123.3229
930915 32.500 31.375 32.438 11356.8 368.3862
What we'll do is plot the difference between the items in column 1 and column 2 for all the samples. The data file is a history of stock prices, and we want to look at the fluctuation of prices in a per-day basis. Listing 32.2 contains the Perl script used to extract the data from the display file and to display the data using gnuplot.
Listing 32.2. Using Perl to generate 2D plots.
1 #!/usr/bin/perl
2
3
4 open( PLOTFILE, ">data1") || die "\n Cannot open plot file $! \n";
5 open( DATAFILE, "INTC") || die "\n Cannot open $! \n";
6
7 $i = 0;
8 while (<DATAFILE>) {
9 ($date,$hi,$lo,$cl,$vol,$other) = split(' ',$_);
10 printf PLOTFILE "%6.1f %6.1f\n", $i, $hi-$lo;
11 $i++;
12 }
13 close PLOTFILE;
14 close DATAFILE;
15
16 open (GNUPLOT,"| gnuplot");
17
18 print GNUPLOT <<gnuplot_commands;
19 set term pbm color small
20 set output "myoutput.pbm"
21 set title "Test 1"
22 plot "data1"
23 gnuplot_commands
24
25
26 close GNUPLOT;
27
28 `ppmtogif myoutput.pbm > myoutput.gif 2>/dev/null `;
29 # The next line is used to view the output file
30 `xv myoutput.gif`;
The input data file is organized as one record per line. Each record contains the date, followed by the high, low, and closing prices of the day and the volume. In line 9 of the listing, we extract the high and low prices of the day and in line 10 we print out the maximum fluctuation of these prices along with an index value.
This listing does four things: From lines 4 to 14 we are extracting the data from the input file, INTC, and creating a temporary disk file called data1. Then lines 18 through 26 render the image in PBM format. Line 28 uses the PBM utilities to convert from PBM to GIF. All functions for the PBM utilities as well as the xv program must be located somewhere in the directories listed in the PATH environment variable. Line 30 uses the xv tools to view the image just created. Line 30 is really not required, but is included only as a debug line.
Look at lines 4 and 5, where the output and input files are opened. Each line from the input file is extracted in line 9. The calculated output is written to the output file in line 10. A value for the horizontal axis is created for the output by auto-incrementing the $i variable. After the input file is completely read, both the input and output files are closed in lines 13 and 14.
At line 16, we open the GNUPLOT handle to send all output to gnuplot. You must have the program gnuplot located somewhere in the directories listed in the PATH environment variable. For line 16 to work, gnuplot must exist in a directory defined by the PATH or reside in the current directory. The following commands are sent to the gnuplot program from lines 19 up to line 22. Line 23 terminates the input to the gnuplot program:
19 set term pbm color small
20 set output "myoutput.pbm"
21 set title "Test 1"
22 plot "data1"
Here's how the commands to gnuplot work. The terminal type is set to PBM output in color using small fonts. The output file is set to myoutput.pbm instead of the terminal. The title of the plot is set to Test 1. The plot command is used to render the image with the data in the file data1.
Finally, at line 27, we take all data from the PBM file we just created and convert it to a GIF file using the `ppmtogif` utility. The command shown in line 28 also redirects the standard error handle, stderr, which is 2, to redirect its output to /dev/null.
Line 30 is used to call the xv program to view what we have just created. The call to xv is not required, and in fact should be made only if you have already successfully installed xv or to call whatever image-viewing software you happen to be using.
The process to generate the data plot is quite straightforward. Simply write the data to a temp file, open a pipe to gnuplot, and then write commands directly to gnuplot to render the data. The output from gnuplot may have to go to a different rendering or image-converting utility to get the format you want.
Now let's look at how to plot 3D images using gnuplot. Rather than writing your own 3D plotting software, it's probably better to use gnuplot. In fact, in the next section, "Using the Output in CGI Applications," we'll only draw 2D plots. For quick plots of 3D data, using gnuplot will be faster than rolling your 3D-rendering software.
Here's another example of how to use gnuplot to print the output of a Perl script. Look at Listing 32.3, which generates the image shown in Figure 32.2. The formula in Listing 32.3 produces a nice wave form with an equation from a math textbook. The formula is code in lines 7 and 14. The complete formula for generating the points on a surface is:
z = C * sin( 2 * A * PI * sqrt($x * $x + $y * $y)) ;
where A and C
are constants that the user can play with in order to affect the
number and
amplitude of the waves generated. The display output from the
points generated with this
formula is shown in Figure 32.2.
Listing 32.3. Using gnuplot to generate images.
1 #!/usr/bin/perl
2 #
3 # Check out the use of GNU plot.
4 $a=3.0;
5 $c=0.25;
6 $pi= 3.141593;
7 $ta = 2 * $a * $pi;
8
9 open( PLOTFILE, ">tdata") || die "\n Cannot open plot file $! \n";
10
11 for($x=-1;$x < 1 ;$x += 0.02) {
12 for($y= -1 ;$y < 1.00;$y += 0.02) {
13
14 $z = $c * sin( $ta * sqrt($x * $x + $y * $y)) ;
15 printf PLOTFILE "%6.3f\n", $z;
16 }
17 print PLOTFILE "\n";
18 }
19 close PLOTFILE;
20
21
22 open (GNUPLOT,"| gnuplot");
23
24 print GNUPLOT <<gnuplot_commands;
25 set term pbm color small
26 set output "myoutput.pbm"
27 set noparametric
28 set title "Test GNUPLOT"
29 splot "tdata"
30 gnuplot_commands
31
32
33 close GNUPLOT;
34
35 `ppmtogif myoutput.pbm > myoutput.gif 2>/dev/null `;
The constants for the formula used in this program are defined in lines 4 through 7. The plot file to hold the intermediate points is the tdata file, which is opened in line 9. All points stored in the data file are stored in a matrix form with a value of z for each x,y location in the matrix. This format is the nonparametric form of data for gnuplot. (The parametric form involves writing vectors to create wire frame models and is thankfully beyond the scope of this book.) Each line in the input file is one row of x coordinates. All y coordinate values for the row are stored in a line. At line 22, we open a pipe to gnuplot. The commands sent to gnuplot are slightly different here because we are plotting nonparametric data with the height of each z coordinate given in a matrix. The x and y coordinates will be the row and column number from which the data is picked up. The terminal type is set to PBM output in color using small fonts. The output file is set to myoutput.pbm instead of the terminal. The title of the plot is set to Test GNUPLOT. We have to explicitly force gnuplot to the nonparametric mode for the type of data we are about to feed it. Finally, the splot command is used to render a 3D image with the data in the file tdata.
As in Listing 32.2, the output is converted from PBM to GIF format with this line:
35 `ppmtogif myoutput.pbm > myoutput.gif 2>/dev/null `;
The output can be viewed with xv, as before, using the `xv myoutput.gif` command. Instead of doing this, let's see how the images can be sent as GIF images for use with World Wide Web browsers.
The output from both the programs shown in Listings 32.2 and 32.3 are GIF files. The output can be used in HTML pages in two steps from the Web browser side:
There are two glaring problems with using Perl and gnuplot in this manner. First, you are creating two steps for the user to work through. Second, reloading an image in a browser may reload the cached version of the image at the client side and not the image at the server side. So, unless the user flushes the images at the browser side, he or she is not likely to see the changes in the image.
A way out of these problems is to use the image-generating Perl script as the URL instead of the image file itself. The Perl script will have to be modified to print out the MIME header and the contents of the image back to the standard output. To do this, replace line 35 in Listing 32.2 with these three lines:
$| = 1;
print "Content-Type: image/gif \n\n";
system('ppmtogif myoutput.pbm 2>/dev/null');
The first of the three lines shown here will force flushing of all output to standard output immediately. This is required for Perl scripts that are replying to Web browsers. The next line sets the MIME header required for all GIF images being sent back to the browser. The two carriage returns in the print line are required, so do not remove them. If you do not place the two carriage returns, the browser requesting the image is likely to hang up. The last line is very similar to line 35, except that the output from the pptogif program is sent to stdout instead of a file. Since this Perl script is called to send replies back to a browser, the GIF image just created will be sent back.
The URL for the Perl script is the filename for the Perl program itself. See Listing 32.4 for a sample HTML page using this URL. Note how the URL for the IMG tag is set to a Perl script. The output for the HTML file is shown in Figure 32.3.
Figure 32.3 : Using plots with HTML files.
Listing 32.4. The HTML page using GIF images.
1 <HTML><HEAD><TITLE>MPS Inc. Home Page </TITLE>
2 </HEAD>
3 <BODY>
4 <P>
5 <p>
6 <H2>Test loading images </H2>
7 <P>
8 <FORM NAME="mineFormkh">
9 <IMG SRC="http://ikra.com/cgi-bin/my3D.pl" BORDER > <B>Hello</B>
10 </FORM
11 </BODY>
12 <HTML>
So far, you've learned how to create GIF files from within a Perl script using gnuplot. Next, you'll generate GIF files directly without using external programs by using the GD.pm module.
The primary motivation for using the GD.pm library is that it's easy to create GIF images directly from within Perl programs. The module is loaded directly into a Perl program and does not involve the use of system resources in order to convert to a different format.
The GD.pm module has been developed and placed in the CPAN archives by Leonard Stein (lstein@genome.wi.mit.edu). You can find an HTML page with good information about this package at
http://www-genome.wi.mit.edu/~lstein
Go to the nearest CPAN site to get the tar file with the module and extensions. The files used in this chapter are collected from the CPAN archive at
http://www-genome.wi.mit.edu/ftp/pub/software/WWW/GD.html
The GD.pm library relies on a C function library called libgd. This library has been developed by Tom Boutell (boutell@boutell.com). There is a great detailed Web page available that you can see at
http://www.boutell.com/gd
The version used for this book is 1.2; however, it's best if you use the latest version.
You have to take two steps to install both these libraries. First, install the gd library, and then install the GD.pm module and its libraries. Use only gcc for compiling all the gd code. The code hasn't been tested with many ANSI C compilers and might not work with any other type of compiler. In the example for this book, the code only worked and compiled cleanly with gcc, and the makefile did not work with Visual C++. If you are using the Microsoft VC++ compiler, stop. Get a version of gcc and use it instead if you don't want to mess with the makefiles.
Installing the gd library and source code involves these steps:
To test if you have a working version of the libraries, run the gddemo program. If you do not get any output in the demoout.gif, you have not compiled the libraries correctly. At this point, you can review the source files to see what kinds of functions are available for use with the package. Now that we have the libgd.a library built, we can go ahead and install the GD.pm files.
Version 1.00 of GD.pm is meant for use with gd version 1.2. If you are not sure which version to use, check out the documentation at
http://www-genome.wi.mit.edu/pub/software/WWW/GD.html
The installation of GD.pm can be done in one of two ways: using static linking or using dynamic linking. Use dynamic linking only if your operating system supports it; otherwise, use static linking. Using static linking does make the perl.exe program a bit larger. In systems that do not support dynamic linking, your only course may be to use static linking. In either case, you have to unpack the source files into the ext/GD subdirectory, where your Perl 5 distribution is unpacked.
For dynamic linking, take these steps:
For static linking, take these steps:
After you have run the make install program, test whether you have the correct version by running the demos in ext/GD/demos. You may have to edit the first line of each script in the demo files to point to your installation of perl.exe.
Also, the script files in the ext/GD /demos directory (under the Perl distribution directory tree) print a GIF file to standard output. If you do not redirect this output to another file or to a viewing program like xv, your terminal settings might get mangled, so run the script like this:
brushes.pl > test.gif
xv test.gif
Remember to include xv in your pathname or use another GIF viewer if you like. After you have confirmed that the demos are working fine, you can start with your own GIF files.
Once you have the GD.pm utilities installed, you can work with the GD.pm file by using the use GD statement. Basically, you create an image object, write a bunch of drawing commands to it, and then print it out. A small example is shown in Listing 32.5. The output of this listing is shown in Figure 32.4.
Figure 32.4 : Using the GD.pm module.
Listing 32.5. Using the GD.pm module.
1 #!/usr/bin/perl
2
3 use GD;
4
5 $im = new GD::Image(300,200);
6
7 $white = $im->colorAllocate(255, 255, 255);
8 $black = $im->colorAllocate(0, 0, 0);
9 $red = $im->colorAllocate(255, 0, 0);
10 $blue = $im->colorAllocate(0,0,255);
11 $yellow = $im->colorAllocate(255,250,205);
12
13 $im->transparent($white); # white color is transparent
14 $im->interlaced(1); # cool venetian blinds effect
15
16 # Create a flat square paintbrush
17 $brush = new GD::Image(5,5);
18 $brush->colorAllocate(255,255,255); # white
19 $brush->colorAllocate(0,0,0); # black
20 $brush->transparent($white); # white is transparent
21 $brush->filledRectangle(0,0,5,2,$black); # a black rectangle
22
23 #
24 # Draw some text
25 #
26 $im->string(gdLargeFont,150,10,"Large Font!",$red);
27 $im->string(gdSmallFont,150,28,"The small font!",$blue);
28 $im->stringUp(gdTinyFont,280,150,"Vertical Text!",$black);
29 #
30 # Draw some arcs
31 #
32
33 $im->setBrush($brush);
34 $im->arc(50,100,50,50,0,90,gdBrushed);
35 $im->arc(100,100,50,50,0,180,gdBrushed);
36 $im->arc(150,100,50,50,0,270,gdBrushed);
37 $im->arc(200,100,50,50,0,360,gdBrushed);
38 $im->fill(210,110,$blue);
39
40
41 $poly = new GD::Polygon;
42 $poly->addPt(10,10);
43 $poly->addPt(50,10);
44 $poly->addPt(150,50);
45 $poly->addPt(10,100);
46 $im->polygon($poly,gdBrushed);
47
48 $im->fill(40,40,$yellow);
49
50 # print the image to stdout
51 print $im->gif;
At line 3, we include the GD.pm module with the use statement. Line 5 starts a new image that is 300 pixels wide and 200 pixels high. Lines 7 through 11 allocate colors in a color map for the image. The first color allocated for the map is the background for the image. Line 13 forces white to be transparent; that is, colors written over white will be seen as though written over a transparent background.
The interlaced method (line 14) lets the image be constructed in stages, with each stage enhancing the focus a little instead of being rendered in one slow step.
At line 17, we create a brush to draw with. This brush is a mini-image in itself and has to be allocated its own color maps. Line 21 creates the filled rectangle to make it a solid brush.
In lines 26 through 28 we draw some text using different fonts. Note how easy it is to draw vertical text. In each of the text-drawing routines you have to specify the font, the x,y location to start at, the text to write, and the color to use. The font types are listed in the GD.pm file.
GD.pm allows you to draw various shapes. The arc() function can be used to draw portions of an arc. The parameters to the call are the location to start drawing, its width and height, the angle to draw, and the paint brush to use, if any. The paintbrush parameter is optional and if left out will use the default paint brush. At line 38, we fill in the circle with the color blue. (Note that a circle is simply an arc drawn for 360 degrees with its height equal to its width.)
At line 41 we draw a polygon and in the subsequent lines add points. The polygon is drawn on the image area at line 46. In line 48 we fill the inside of the polygon with a yellow background color.
At line 51 we print the image to the standard output from where it can be piped to a viewing program or stored on disk. Actually, what we do in the next section is to pipe the output to the standard output as a response to a Web page request.
Listing 32.6 presents another program which creates a tree of sorts and prints it to the standard output as a GIF file. This program recursively creates the branches of a tree. At the end of each recursion it draws a little, green rectangle. Admittedly, it's not a pretty tree, but with a few random number tricks, you can make this script generate a new tree every time it's called. This regeneration feature makes the script excellent for use in scripts. For example, you can make the output of the script be the source URL to an HTML image tag. Whenever the HTML page is loaded, a new tree image is displayed.
Listing 32.6. Using the tree-generation program.
1 #!/usr/bin/perl
2
3
4 $PI=3.1415926; # no need to use a jillion digits
5 $PIBY2 = $PI/2; # Use a fanout of -pi/2 to +pi/2
6 $NPIBY2 = -1. * $PI/2;
7
8 sub deg2rad {
9 my $deg = shift;
10 $deg %= 360;
11 return $rad = $deg * $PI / 180.0;
12 }
13
14 # GLOBALS:
15 # $height = of image
16 # $width = of image
17 # $maxfans = max fanout per branch
18 # $fanAngle = angle per fanout
19 # $fanRatio = new branch length / parent branch length
20 #
21 # INPUT PARAMETERS:
22 # $h = current depth
23 # $x = current x
24 # $y = current y
25 # $theta = angle to start from
26 # $flen = length of parent branch
27 #
28 # LOCAL PARAMETERS:
29 # $nx,$ny = offsets from current $x,$y
30 # $i = angle to draw
31 # $incr = increment
32 # $maxAngle = up to this angle.
33
34 sub tree {
35 my ($h,$x,$y,$theta,$flen) = @_; # local variables
36 my ($nx,$ny,$nlen,$mycolor); # for drawing to.
37 my ($i,$incr,$startAngle,$maxAngle); # for the fanout
38 my ($randFans);
39
40 #
41 # Stopping conditions first.
42 #
43 if ($h < 1) {
44 $im->filledRectangle($x-1,$base_y+$y-1,$x+2,$base_y+$y+2,$leaf);
45 return 0; }
46 $nlen = $flen * $fanRatio;
47 if ($nlen < 1) { return 0;}
48
49 $mycolor = $brown;
50 if ($h == 2) { $mycolor = $color3; }
51 if ($h == 3) { $mycolor = $color2; }
52 if ($h == 4) { $mycolor = $color1; }
53 if ($h == 5) { $mycolor = $color0; }
54 #
55 # Important!!!
56 #
57 $h -= 1; # for recursion
58
59
60 $maxAngle = $theta + $fanAngle;
61 $incr = ($maxAngle)/($maxfans + int(rand() * 3));
62
63 $startAngle = $theta - ($fanAngle/2);
64
65 if ($startAngle < $NPIBY2) { $startAngle = $NPIBY2; }
66 if ($startAngle > $NPIBY2) { $startAngle = $PIBY2; }
67
68 # printf "\n From %5.1f --> %5.1f, incr %5.1f",
69 # $startAngle,$maxAngle, $incr;
70
71 for ($i = $startAngle ; $i < $maxAngle; $i += $incr)
72 {
73 $nx = $nlen * sin($i);
74 $ny = $nlen * cos($i);
75 #
76 #
77 #
78 $im->line($x,$base_y+$y,$nx+$x,$base_y+$ny+$y,$mycolor);
79 tree($h,$nx+$x,$ny+$y,$i,$nlen);
80 }
81 }
82
83 $height = 300;
84 $width = 300;
85 $maxfans = 3;
86 $maxdepth = 6;
87 $fanAngle = deg2rad(120); # Maximum 120 degrees
88 $fanRatio = 0.65; # Each new branch is 4/5 of old.
89 $base_y = $height / 2;
90 #
91 #
92 #
93 $start_x = $width/2;
94 $start_y = 25;
95 $start_length = 80;
96 $start_angle = deg2rad(80);
97
98 srand(time);
99
100 use GD;
101
102 $im = new GD::Image(300,300);
103 $backgd = $im->colorAllocate(154,192,205);
104 $white = $im->colorAllocate(255, 255, 255);
105 $black = $im->colorAllocate(0, 0, 0);
106 $brown = $im->colorAllocate(188,130, 130);
107 $leaf = $im->colorAllocate(157,255,12);
108 $color0 = $im->colorAllocate(245,196,90);
109 $color1 = $im->colorAllocate(95,158,60);
110 $color2 = $im->colorAllocate(46,139,87);
111 $color3 = $im->colorAllocate(152,251,152);
112 $red = $im->colorAllocate(255,0,0);
113 $blue = $im->colorAllocate(0,0,255);
114 $im->transparent($white); # white color is transparent
115 $im->interlaced(1); # cool venetian blinds effect
116
117 tree ($maxdepth,$start_x,$start_y,$start_angle,$start_length);
118
119
120 $text_y = $base_y / 2 + $width / 2;
121 $text_x = $width / 5;
122 $infoStr = "Welcome to Kamran's Home Page";
123 $dateStr = "Today is " . `date`;
124 $message = " http://www.ikra.com/index.html";
125
126 $im->string(gdLargeFont,$text_x,$text_y,$infoStr,$red);
127 $im->string(gdSmallFont,$text_x,$text_y + 30,$dateStr,$blue);
128 $im->stringUp(gdLargeFont,10,$height - 10,$message,$blue);
129
130
131 # print the image to stdout
132
133 $|=1;
134
135 print "Content-type: image/gif \n\n";
136 print $im->gif;
Lines 4 through 12 define some of the constants for using degrees and radians. The script uses the following global variables:
Each call to the recursive function, tree(), uses the following variables:
If the depth is 1 or less, the tree() function draws a rectangle at the current location and backs out of the recursion. See lines 43 and 44. It also backs out without drawing a leaf if the new branch is too small to draw.
Different colors are used based on the depth of the recursion in lines 49 through 53.
Line 57 is where the depth of the recursion is marked off. The value of $h is decremented before a recursive call to tree() is made. Lines 60 to 66 make sure that the fanout (maximum number of degrees between the outer most branches) and number of sub-branches is set in the correct -PI/2 to +PI/2 range. You may want to customize these lines to see how you want your tree to "grow."
At line 71 we use a for loop to make a recursive call to the tree() function with different fanout angles, locations to draw, and colors.
The initialization of global variables is done from lines 83 to 98. The random number generator is seeded at line 98.
The image is created at line 102, with colors for its components created in lines 103 to 115. The tree() function is called at line 117. Add more calls to tree() if you want more than one tree printed. Some text is drawn in the image area to customize the output in the lines up to 131.
Lines 133 to 136 actually print the image to standard output as the response to an HTTP request.
To use the previous listing in an HTML file, you can add this tag to an HTML page:
<IMG SRC="http://www.ikra.com/cgi-bin/tree.pl">
The browser showing the HTML page will call the tree.pl script, which in turn will return the GIF image back. Therefore, every time you reload the Web page, you'll see a new image. Neat. Actually, you can also refer to the tree.pl in the Location prompt on your browser. Refer to Figure 32.5 to see how it's referred to in a URL. The script is called fractal.pl in Figure 32.5.
Figure 32.5 : Dynamic image creating with Web pages.
You can see how easy it is to create GIF images on-the-fly and use them in Web pages. Using the GD.pm module, it's easy to have Perl write your output as a GIF file for use in other applications.
In this chapter I covered two ways of creating images: using gnuplot and directly through the use of the GD.pm module. The gnuplot method is simpler to use when you have a set of data points in a logical order that can be read directly from within gnuplot. An example for using gnuplot is when drawing charts and 3D plots. If you are drawing more than just charts and plots, consider generating the images directly from within Perl by using the GD.pm module. Using GD.pm is a bit more difficult because you have to manage the details of drawing yourself, but it does eliminate the need for an external program like gnuplot.