📄 ch13_03.htm
字号:
<?label 13.3. Generating PNGs with GD?><html><head><title>Generating PNGs with GD (CGI Programming with Perl)</title><link href="../style/style1.css" type="text/css" rel="stylesheet" /><meta name="DC.Creator" content="Scott Guelich, Gunther Birznieks and Shishir Gundavaram" /><meta scheme="MIME" content="text/xml" name="DC.Format" /><meta content="en-US" name="DC.Language" /><meta content="O'Reilly & Associates, Inc." name="DC.Publisher" /><meta scheme="ISBN" name="DC.Source" content="1565924193L" /><meta name="DC.Subject.Keyword" content="stuff" /><meta name="DC.Title" content="CGI Programming with Perl" /><meta content="Text.Monograph" name="DC.Type" /></head><body bgcolor="#ffffff"><img src="gifs/smbanner.gif" alt="Book Home" usemap="#banner-map" border="0" /><map name="banner-map"><area alt="CGI Programming with Perl" href="index.htm" coords="0,0,466,65" shape="rect" /><area alt="Search this book" href="jobjects/fsearch.htm" coords="467,0,514,18" shape="rect" /></map><div class="navbar"><table border="0" width="515"><tr><td width="172" valign="top" align="left"><a href="ch13_02.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0" /></a></td><td width="171" valign="top" align="center"><a href="index.htm">CGI Programming with Perl</a></td><td width="172" valign="top" align="right"><a href="ch13_04.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0" /></a></td></tr></table></div><hr align="left" width="515" /><h2 class="sect1">13.3. Generating PNGs with GD</h2><p>The <a name="INDEX-2562" /><a name="INDEX-2563" /><a name="INDEX-2564" />GD module was created and is maintainedby Lincoln Stein, who is also the author of CGI.pm. GD provides aPerl port of the <em class="emphasis">gd</em> graphics library created byThomas Boutell for the C programming language. The<em class="emphasis">gd</em> library was originally created for creatingand editing GIFs. As a result of the Unisys patent issue, however, itwas rewritten for PNG (incidentally, Thomas Boutell was a co-authorand the editor for the PNG specification). Current versions of the<em class="emphasis">gd</em> library and the GD module no longer supportGIFs, and older versions are no longer distributed. If you have anolder version of these modules (for example, an older version wasincluded with your system) that does support GIFs, you shouldprobably contact Unisys for licensing terms and/or an attorneyfamiliar with patent issues before using them.</p><a name="ch13-11-fm2xml" /><div class="sect2"><h3 class="sect2">13.3.1. Installation</h3><p>You can install <a name="INDEX-2565" /><a name="INDEX-2566" />GD just like other CPAN modules, exceptthat you should ensure that you have the latest version of<em class="emphasis">gd</em>. GD contains <a name="INDEX-2567" />C code that must be compiled with<em class="emphasis">gd</em>, and if you have an older version of<em class="emphasis">gd</em>, or if <em class="emphasis">gd</em> is missing,you will get errors during compilation.</p><p>The <em class="emphasis">gd</em> library is available at <a href="http://www.boutell.com/">http://www.boutell.com/</a>. This site also hasinstructions for building <em class="emphasis">gd</em> plus references toother optional packages that <em class="emphasis">gd</em> uses ifavailable, such as the FreeType engine, which enables<em class="emphasis">gd</em> (and thus GD) to support TrueType fonts. Notethat gd requires the latest versions of<em class="emphasis">libpng</em><a name="INDEX-2568" /> <a name="INDEX-2,569" /> and <em class="emphasis">zlib</em>; you can findlinks to these libraries at <a href="http://www.boutell.com/">http://www.boutell.com/</a> too.</p></div><a name="ch13-12-fm2xml" /><div class="sect2"><h3 class="sect2">13.3.2. Using GD</h3><p>In this section, we'll develop an application that uses the<tt class="command">uptime</tt><a name="INDEX-2570" /> <a name="INDEX-2,571" /><a name="INDEX-2572" /> Unix system command to plot thesystem load average (see <a href="ch13_03.htm#ch13-31232">Figure 13-1</a>). As we willsee in the next section, there are modules to help us generate graphsmore easily, but let's first see <em class="emphasis">gd</em>'s graphics primitives in action.</p><a name="ch13-31232" /><div class="figure"><img width="125" src="figs/cgi2.1301.gif" height="113" alt="Figure 13-1" /></div><h4 class="objtitle">Figure 13-1. Sample graph generated by loads.cgi</h4><p>The application itself is rather straightforward. First, we invokethe <tt class="command">uptime</tt> command, which returns three values,representing the load averages for the previous 5, 10 and 15 minutes,respectively -- though this may differ among the various Uniximplementations. Here is the output of an <em class="emphasis">uptime</em>command:</p><blockquote><pre class="code">2:26pm up 11:07, 12 users, load average: 4.63, 5.29, 2.56</pre></blockquote><p>Then, we use <em class="emphasis">gd</em>'s various<a name="INDEX-2574" /><a name="INDEX-2575" />drawing primitives, such as linesand polygons to draw the axes and scale and to plot the load values.</p><p><a href="ch13_03.htm#ch13-23495">Example 13-2</a> shows the code.</p><a name="ch13-23495" /><div class="example"><h4 class="objtitle">Example 13-2. loads.cgi </h4><a name="INDEX-2576" /><a name="INDEX-2,577" /><blockquote><pre class="code">#!/usr/bin/perl -wTuse strict;use CGI;use GD;BEGIN { $ENV{PATH} = '/bin:/usr/bin:/usr/ucb:/usr/local/bin'; delete @ENV{ qw( IFS CDPATH ENV BASH_ENV ) };}use constant LOAD_MAX => 10;use constant IMAGE_SIZE => 170; # height and widthuse constant GRAPH_SIZE => 100; # height and widthuse constant TICK_LENGTH => 3;use constant ORIGIN_X_COORD => 30;use constant ORIGIN_Y_COORD => 150;use constant TITLE_TEXT => "System Load Average";use constant TITLE_X_COORD => 10;use constant TITLE_Y_COORD => 15;use constant AREA_COLOR => ( 255, 0, 0 );use constant AXIS_COLOR => ( 0, 0, 0 );use constant TEXT_COLOR => ( 0, 0, 0 );use constant BG_COLOR => ( 255, 255, 255 );my $q = new CGI;my @loads = get_loads( );print $q->header( -type => "image/png", -expires => "-1d" );binmode STDOUT;print area_graph( \@loads );# Returns a list of the average loads from the system's uptime commandsub get_loads { my $uptime = `uptime` or die "Error running uptime: $!"; my( $up_string ) = $uptime =~ /average: (.+)$/; my @loads = reverse map { $_ > LOAD_MAX ? LOAD_MAX : $_ } split /,\s*/, $up_string; @loads or die "Cannot parse response from uptime: $up_string"; return @loads;}# Takes a one-dimensional list of data and returns an area graph as PNGsub area_graph { my $data = shift; my $image = new GD::Image( IMAGE_SIZE, IMAGE_SIZE ); my $background = $image->colorAllocate( BG_COLOR ); my $area_color = $image->colorAllocate( AREA_COLOR ); my $axis_color = $image->colorAllocate( AXIS_COLOR ); my $text_color = $image->colorAllocate( TEXT_COLOR ); # Add Title $image->string( gdLargeFont, TITLE_X_COORD, TITLE_Y_COORD, TITLE_TEXT, $text_color ); # Create polygon for data my $polygon = new GD::Polygon; $polygon->addPt( ORIGIN_X_COORD, ORIGIN_Y_COORD ); for ( my $i = 0; $i < @$data; $i++ ) { $polygon->addPt( ORIGIN_X_COORD + GRAPH_SIZE / ( @$data - 1 ) * $i, ORIGIN_Y_COORD - $$data[$i] * GRAPH_SIZE / LOAD_MAX ); } $polygon->addPt( ORIGIN_X_COORD + GRAPH_SIZE, ORIGIN_Y_COORD ); # Add Polygon $image->filledPolygon( $polygon, $area_color ); # Add X Axis $image->line( ORIGIN_X_COORD, ORIGIN_Y_COORD, ORIGIN_X_COORD + GRAPH_SIZE, ORIGIN_Y_COORD, $axis_color ); # Add Y Axis $image->line( ORIGIN_X_COORD, ORIGIN_Y_COORD, ORIGIN_X_COORD, ORIGIN_Y_COORD - GRAPH_SIZE, $axis_color ); # Add X Axis Ticks Marks for ( my $x = 0; $x <= GRAPH_SIZE; $x += GRAPH_SIZE / ( @$data - 1 ) ) { $image->line( $x + ORIGIN_X_COORD, ORIGIN_Y_COORD - TICK_LENGTH, $x + ORIGIN_X_COORD, ORIGIN_Y_COORD + TICK_LENGTH, $axis_color ); } # Add Y Axis Tick Marks for ( my $y = 0; $y <= GRAPH_SIZE; $y += GRAPH_SIZE / LOAD_MAX ) { $image->line( ORIGIN_X_COORD - TICK_LENGTH, ORIGIN_Y_COORD - $y, ORIGIN_X_COORD + TICK_LENGTH, ORIGIN_Y_COORD - $y, $axis_color ); } $image->transparent( $background ); return $image->png;}<a name="INDEX-2578" /><a name="INDEX-2579" /></pre></blockquote></div><p>After importing our modules, we use a<tt class="function">BEGIN</tt><a name="INDEX-2580" /><a name="INDEX-2581" /> block to make the environmentsafe for taint. We have to do this because our script will use theexternal <tt class="command">uptime</tt> command (see <a href="ch08_04.htm#ch08-28640">Section 8.4, "Perl's Taint Mode"</a>).</p><p>Then we set a large number of constants. The<tt class="literal">UPPER_LIMIT</tt> constant sets the upper limit on theload average. If a load average exceeds the value of 10, then it isset to 10, so we don't have to worry about possibly scaling theaxes. Remember, the whole point of this application is not to createa highly useful graphing application, but one that will illustratesome of GD's drawing primitives.</p><p>Next, we choose a size for our graph area,<tt class="literal">GRAPH_SIZE</tt>, as well as for the image itself,<tt class="literal">IMAGE_SIZE</tt>. Both the image and the graph aresquare, so these sizes represent length as well as width.<tt class="literal">TICK_LENGTH</tt> corresponds to the length of each tickmark (this is actually half the length of the tick mark onceit's drawn).</p><p><tt class="literal">ORIGIN_X_COORD</tt> and<tt class="literal">ORIGIN_Y_COORD</tt> contain the coordinates of theorigin of our graph (its lower left-hand corner).<tt class="literal">TITLE_TEXT</tt>, <tt class="literal">TITLE_X_COORD</tt>, and<tt class="literal">TITLE_Y_COORD</tt> contain values for the title of ourgraph. Finally, we set <tt class="literal">AREA_COLOR</tt>,<tt class="literal">AXIS_COLOR</tt>, <tt class="literal">TEXT_COLOR</tt>, and<tt class="literal">BG_COLOR</tt> to an array of three numbers containingred, green, and blue values, respectively; these values range from<tt class="literal">0</tt> to <tt class="literal">255</tt>.</p><p>The system's load is returned by<tt class="function">get_loads</tt>. It takes the output of<tt class="command">uptime</tt><a name="INDEX-2582" />, parses out the load averages,truncates any average greater than the value specified by<tt class="literal">UPPER_LIMIT</tt>, and reverses the values so they arereturned from oldest to newest. Thus, our graph will plot from leftto right the load average of the system over the last 15, 10, and 5minutes.</p><p>Returning to the main body of our CGI script, we output our header,enable binary mode, then fetch the data for our PNG from<tt class="function">area_graph</tt> and print it.</p><p>The <tt class="function">area_graph</tt><a name="INDEX-2583" /> function contains all of our imagecode. It accepts a reference to an array of data points, which itassigns to <tt class="literal">$data</tt>. We first create a new instanceof <a name="INDEX-2584" />GD::Image, passing to it the dimensionsof the canvas that we want to work with.</p><p>Next, we allocate four colors that correspond to our earlierconstants. Note that the first color we allocate automaticallybecomes the background color. In this case, the image will have awhite background.</p><p>We use the <tt class="function">string</tt> method to display our titleusing the <tt class="function">gdLarge</tt> font. Then, we draw two lines,one horizontal and one vertical from the origin, representing the xand y axes. Once we draw the axes, we iterate through the entiregraph area and draw the tick marks on the axes.</p><p>Now, we're ready to plot the load averages on the graph. Wecreate a new instance of the <a name="INDEX-2585" />GD::Polygon class to draw a polygon withthe vertices representing the three load averages. Drawing a polygonis similar in principle to creating a closed path with severalpoints.</p><p>We use the <tt class="function">addPt</tt> method to add a point to thepolygon. The origin is added as the first point. Then, each loadaverage coordinate is calculated and added to the polygon. We add afinal point on the x axis. GD automatically connects the final pointto the first point.</p><p>The <tt class="function">filledPolygon</tt> method fills the polygonspecified by the <tt class="literal">$polygon</tt> object with theassociated color. And finally, the graph is rendered as a PNG and thedata is returned.</p><p>GD supports many methods beyond those listed here, but we do not havespace to list them all here. Refer to the GD documentation or<em class="citetitle">Programming Web Graphics</em> for full <a name="INDEX-2586" /> <a name="INDEX-2,587" /> <a name="INDEX-2,588" />usage.</p></div><hr align="left" width="515" /><div class="navbar"><table border="0" width="515"><tr><td width="172" valign="top" align="left"><a href="ch13_02.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0" /></a></td><td width="171" valign="top" align="center"><a href="index.htm"><img src="../gifs/txthome.gif" alt="Home" border="0" /></a></td><td width="172" valign="top" align="right"><a href="ch13_04.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0" /></a></td></tr><tr><td width="172" valign="top" align="left">13.2. Outputting Image Data</td><td width="171" valign="top" align="center"><a href="index/index.htm"><img src="../gifs/index.gif" alt="Book Index" border="0" /></a></td><td width="172" valign="top" align="right">13.4. Additional GD Modules</td></tr></table></div><hr align="left" width="515" /><img src="../gifs/navbar.gif" alt="Library Navigation Links" usemap="#library-map" border="0" /><p><font size="-1"><a href="copyrght.htm">Copyright © 2001</a> O'Reilly & Associates. All rights reserved.</font></p><map name="library-map"><area href="../index.htm" coords="1,1,83,102" shape="rect" /><area href="../lnut/index.htm" coords="81,0,152,95" shape="rect" /><area href="../run/index.htm" coords="172,2,252,105" shape="rect" /><area href="../apache/index.htm" coords="238,2,334,95" shape="rect" /><area href="../sql/index.htm" coords="336,0,412,104" shape="rect" /><area href="../dbi/index.htm" coords="415,0,507,101" shape="rect" /><area href="../cgi/index.htm" coords="511,0,601,99" shape="rect" /></map></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -