#!perl
# $Id: counter.pl,v 1.3 2004/09/30 07:34:13 jlinoff Exp $
# ================================================
# Copyright Notice
# Copyright (C) 1998-2003 by Joe Linoff (http://www.joelinoff.com)
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL JOE LINOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
# 
# Comments and suggestions are always welcome.
# ================================================
#
# Acknowledgements are reported in interactive help.
#
# ================================================
# Generate counter output for displaying accesses to my web pages. It
# can be used as a cgi script because it will output the GIF
# information to standard out by default.
#
# The 8x8 font was derived from text2gif written by the authors of
# libungif.  The 16x24 font was hand generated by me.
#
# The algorithm used here generates uncompressed GIF. In fact it
# generates expanded GIF so there is no violation of the LZW patent.
#
# If you run the program in debug mode it will create a counter.gif
# file that can be used to analyze the font. Here is how you would run
# it from the command line:
#
#    % perl counter.pl -debug -noct -font=8x8 -img_file=x1.gif
#    % ls -l x1.gif
#    % perl counter.pl -debug -noct -font=16x24 -img_file=x2.gif
#    % ls -l x2.gif
#
# By default, this script reads a file called counter.dat in the local
# directory. This is a simple text file that stores the state of the
# counter.
#
# Use the -help switch to get up to date information about running the
# script.
# ================================================
use strict;

&Main;

# ================================================
# Main
# ================================================
sub Main
{
    # Get the arguments.
    my $align;
    my $bg_red;
    my $bg_green;
    my $bg_blue;
    my $commas_flag;
    my $counter_file;
    my $debug_flag;
    my $fg_red;
    my $fg_green;
    my $fg_blue;
    my $font;
    my $format;
    my $id;
    my $img_file;
    my $ct_flag;
    my $num_digits;
    my $pad;
    my $text;
    ( $align, $bg_red, $bg_green, $bg_blue, $commas_flag,
      $counter_file, $debug_flag, $fg_red, $fg_green, $fg_blue,
      $font, $format, $id, $img_file, $ct_flag, $num_digits,
      $pad, $text )
	= &ParseArgs(\@ARGV);

    # ================================================
    # Now open the file to see what is already
    # in there unless the -text switch as specified.
    # ================================================
    my $current_count = 0;
    if( $text eq "" ) {
	$current_count = &UpdateCounterFile($counter_file,$id);
    }
    else {
	$current_count = $text;
    }

    # ================================================
    # Format the string.
    # ================================================
    my $counter_string = &FormatString($align,
				       $commas_flag,
				       $current_count,
				       $num_digits,
				       $pad);

    # ================================================
    # Constants.
    # ================================================
    my $digit_width = 8;
    my $digit_height = 8;
    if( $font eq "16x24" ) {
	$digit_width = 16;
	$digit_height = 24;
    }

    # ================================================
    # Derive the image info.
    # ================================================
    my $len = length $counter_string;
    my $width = $len * $digit_width;
    my $height = $digit_height;

    # ================================================
    # Debug output.
    # ================================================
    if( $debug_flag ) {
	print "digit_width       = $digit_width\n";
	print "digit_height      = $digit_height\n";
	print "FG red            = $fg_red\n";
	print "FG green          = $fg_green\n";
	print "FG blue           = $fg_blue\n";
	print "BG red            = $bg_red\n";
	print "BG green          = $bg_green\n";
	print "BG blue           = $bg_blue\n";
	print "commas            = $commas_flag\n";
	print "num_digits        = $num_digits\n";
	print "align             = $align\n";
	print "counter_file      = $counter_file\n";
	print "id                = $id\n";
	print "img_file          = $img_file\n";
	print "length            = $len\n";
 	print "width             = $width\n";
	print "height            = $height\n";
	print "counter_string    = $counter_string\n";
    }

    # ================================================
    # Raster image storage
    # Fill in the image contents from the
    # available pixmaps.
    # ================================================
    my @raster_image = ();
    my $row;
    for($row=0;$row<$digit_height;$row++) {
	my $col;
	for($col=0;$col<$len;$col++) {
	    my $digit = substr($counter_string,$col,1);
	    my @digit_map = ();
	    if( $font eq "8x8" ) {
		@digit_map = &Pixmap8x8Digit0()    if( $digit eq "0" );
		@digit_map = &Pixmap8x8Digit1()    if( $digit eq "1" );
		@digit_map = &Pixmap8x8Digit2()    if( $digit eq "2" );
		@digit_map = &Pixmap8x8Digit3()    if( $digit eq "3" );
		@digit_map = &Pixmap8x8Digit4()    if( $digit eq "4" );
		@digit_map = &Pixmap8x8Digit5()    if( $digit eq "5" );
		@digit_map = &Pixmap8x8Digit6()    if( $digit eq "6" );
		@digit_map = &Pixmap8x8Digit7()    if( $digit eq "7" );
		@digit_map = &Pixmap8x8Digit8()    if( $digit eq "8" );
		@digit_map = &Pixmap8x8Digit9()    if( $digit eq "9" );
		@digit_map = &Pixmap8x8Comma()     if( $digit eq "," );
		@digit_map = &Pixmap8x8Colon()     if( $digit eq ":" );
		@digit_map = &Pixmap8x8SemiColon() if( $digit eq ";" );
		@digit_map = &Pixmap8x8Dot()       if( $digit eq "." );
		@digit_map = &Pixmap8x8Space()     if( $digit eq " " );
	    }
	    elsif( $font eq "16x24" ) {
		@digit_map = &Pixmap16x24Digit0()    if( $digit eq "0" );
		@digit_map = &Pixmap16x24Digit1()    if( $digit eq "1" );
		@digit_map = &Pixmap16x24Digit2()    if( $digit eq "2" );
		@digit_map = &Pixmap16x24Digit3()    if( $digit eq "3" );
		@digit_map = &Pixmap16x24Digit4()    if( $digit eq "4" );
		@digit_map = &Pixmap16x24Digit5()    if( $digit eq "5" );
		@digit_map = &Pixmap16x24Digit6()    if( $digit eq "6" );
		@digit_map = &Pixmap16x24Digit7()    if( $digit eq "7" );
		@digit_map = &Pixmap16x24Digit8()    if( $digit eq "8" );
		@digit_map = &Pixmap16x24Digit9()    if( $digit eq "9" );
		@digit_map = &Pixmap16x24Comma()     if( $digit eq "," );
		@digit_map = &Pixmap16x24Colon()     if( $digit eq ":" );
		@digit_map = &Pixmap16x24SemiColon() if( $digit eq ";" );
		@digit_map = &Pixmap16x24Dot()       if( $digit eq "." );
		@digit_map = &Pixmap16x24Space()     if( $digit eq " " );
	    }
	    my $i;
	    my $j = $row * $digit_width;
	    for($i=0;$i<$digit_width;$i++,$j++) {
		$digit = $digit_map[$j];
		push @raster_image,$digit;
	    }
	}
    }

    # ================================================
    # Write out the image file.
    # ================================================
    if( $format eq "gif" ) {
	&WriteGifImage($img_file,
		       \@raster_image,
		       $debug_flag,
		       $width,
		       $height,
		       $ct_flag,
		       $bg_red,
		       $bg_green,
		       $bg_blue,
		       $fg_red,
		       $fg_green,
		       $fg_blue);
    }
    if( $format eq "png" ) {
	&WritePngImage($img_file,
		       \@raster_image,
		       $debug_flag,
		       $width,
		       $height,
		       $ct_flag,
		       $bg_red,
		       $bg_green,
		       $bg_blue,
		       $fg_red,
		       $fg_green,
		       $fg_blue);
    }

    exit 0;
}
# ================================================
# Write out a buffer.
# ================================================
sub Write
{
    my $hnd = shift;
    my $bytes = shift;
    my $byte;
    foreach $byte ( @$bytes ) {
	my $fld = pack("C",$byte);
	syswrite $hnd,$fld,1;
    }
}
# ================================================
# UpdateCounterFile
# ================================================
sub UpdateCounterFile
{
    my $counter_file = shift;
    my $id = shift;

    my $current_count = 0;

    # Wait for the locks to break.
    my $lock_file = "counter.lck";
    while( -e $lock_file ) {
	sleep 1;
    }
    open LOCK,">$lock_file";
    print LOCK "locked\n";
    close LOCK;

    # Allow any number of user ids.
    my %records;
    if( -e $counter_file ) {
	open HND,"$counter_file";
	while( <HND> ) {
	    chop;
	    if( /^\s*(\S+)\s+(\d+)\s*$/ ) {
		my $val = $2;
		if( "$id" eq "$1") {
		    $current_count = 1 + $val;
		    $records{$1} = $current_count;
		}
		else {
		    $records{$1} = $val;
		}
	    }
	    elsif( /^(\d+)$/ ) {
		# This is an old-style entry, convert to anonymous.
		$records{"__anonymous__"} = $1;
	    }
	}
	close HND;
	if( $current_count == 0 ) {
	    # Add a new record.
	    $current_count = 1;
	    $records{$id} = $current_count;
	}
    }
    else {
	$records{$id} = 1;
    }
    
    # Save the new count.
    open HND,">$counter_file";
    my $key;
    foreach $key ( sort keys %records ) {
	my $val = $records{$key};
	print HND "$key $val\n";
    }
    close HND;
    unlink $lock_file;

    return $current_count;
}
# ================================================
# WriteGifImage
# ================================================
sub WriteGifImage
{
    my $img_file = shift;
    my $in_raster_image = shift;
    my $debug_flag = shift;
    my $width = shift;
    my $height = shift;
    my $ct_flag = shift;
    my $bg_red = shift;
    my $bg_green = shift;
    my $bg_blue = shift;
    my $fg_red = shift;
    my $fg_green = shift;
    my $fg_blue = shift;

    # ================================================
    # Constants.
    # ================================================
    my @raster_image = @$in_raster_image;
    my $N = 7;
    my $CC = 2**($N);
    my $EI = $CC+1;
    my $CC_cycle = (2**$N)-2;
    my $width_lsb;
    my $width_msb;
    ($width_msb,$width_lsb) = &IntDiv($width,256);
    my $height_lsb;
    my $height_msb;
    ($height_msb,$height_lsb) = &IntDiv($height,256);

    # ================================================
    # Debug
    # ================================================
    if( $debug_flag ) {
	print "N                 = $N\n";
	print "CC                = $CC\n";
	print "EI                = $EI\n";
	print "CC_cycle          = $CC_cycle\n";
 	print "width             = $width\n";
 	print "width_lsb         = $width_lsb\n";
 	print "width_msb         = $width_msb\n";
	print "height            = $height\n";
 	print "height_lsb        = $height_lsb\n";
 	print "height_msb        = $height_msb\n";
    }

    # ================================================
    # Write it out as a GIF87a image.
    # ================================================
    if( $img_file ne "" ) {
	unlink $img_file;
	open HND,">$img_file";
    }
    else {
	open HND,">-";
    }
    binmode HND;

    # Write out the content type:
    #  Content-type: image/gif\n\n
    my $fld;
    if( $ct_flag ) {
	$fld = pack("aaaaaaaa",'C','o','n','t','e','n','t','-');
	syswrite HND,$fld,8;
	$fld = pack("aaaaaa",'t','y','p','e',':',' ');
	syswrite HND,$fld,6;
	$fld = pack("aaaaaa",'i','m','a','g','e','/');
	syswrite HND,$fld,6;
	$fld = pack("aaacc",'g','i','f',10,10);
	syswrite HND,$fld,5;
    }

    # Write out the header: GIF87a
    $fld = pack("aaaaaa",'G','I','F','8','7','a');
    syswrite HND,$fld,6;

    # ================================================
    # Screen Descriptor
    # 7654 3210
    # 1010 0110 <- 7 bits deep for each pixel (6+1)
    # ================================================
    my @screen = ( $width_lsb, $width_msb,   # 1,2: raster width
		   $height_lsb, $height_msb, # 3,4: raster height
		   0xa6,                     #   5: 1010 0110 bits
		   0x00,                     #   6: bg color index
		   0x00 );                   #   7: global color map
    Write(\*HND,\@screen);

    # ================================================
    # Initialize the color table.
    # ================================================
    my @colors = ();
    my $i;
    for($i=0;$i<128;$i++) {
	push @colors,0x00;
	push @colors,0x00;
	push @colors,0x00;
    }
    $colors[0] = $bg_red;
    $colors[1] = $bg_green;
    $colors[2] = $bg_blue;

    $colors[3] = $fg_red;
    $colors[4] = $fg_green;
    $colors[5] = $fg_blue;

    Write(\*HND,\@colors);

    # ================================================
    # Image descriptor
    # ================================================
    my @image_descriptor = ( 0x2c,                     #   1: ','
			     0x00, 0x00,               # 2,3: image left
			     0x00, 0x00,               # 4,5: image top
			     $width_lsb, $width_msb,   # 6,7: image width
			     $height_lsb, $height_msb, # 8,9: image height
			     0x00 );                   #  10: bit map
    Write(\*HND,\@image_descriptor);

    # ================================================
    # Create the raster image compression header.
    # In this case we are faking out the LZW compression
    # algorithm.
    # ================================================
    my @image_header = ( 7 ); # 8 bit code entries
    Write(\*HND,\@image_header);

    # ================================================
    # Insert clear-code information in the appropriate
    # places.
    # ================================================
    my @image_out = ();
    my $num_bytes = $width * $height;
    my $image_size = 1 + $#raster_image;
    if( $debug_flag ) {
	if( $num_bytes != $image_size ) {
	    print "ERROR: Image size ($image_size) does not\n";
	    print "       match the number of image bytes ($num_bytes).\n";
	    exit 1;
	}
	print "image size        = $image_size\n";
    }
 
    # ================================================
    # Walk through the image bytes and output a CC
    # every 126.
    # ================================================
    my @image_data = ();
    my $byte;
    my $jj = 0;
    foreach $byte ( @raster_image ) {
	push @image_data,$CC if( $jj == 0 );
	push @image_data,$byte;
	$jj++;
	$jj = 0 if( $jj == 126 );
    }

    # ================================================
    # Convert the image data for output.
    # This means the block size must be inserted for
    # each block.
    # ================================================
    $num_bytes = 1 + $#image_data;
    my $block_size = 0xff;
    my $num_blocks = sprintf "%d",(($num_bytes + ($block_size-1))/$block_size);
    
    if( $debug_flag ) {
	print "image bytes (+CC) = $num_bytes\n";
	print "block size        = $block_size\n";
	print "number of blocks  = $num_blocks\n";
    }
    my $ii = 0;
    for($i=0;$i<$num_blocks;$i++) {
	my $num = $block_size;
	$num = $num_bytes+2 if( $num_bytes < $block_size );
	if( $debug_flag ) {
	    print "block $i has $num bytes\n";
	}
	push @image_out,$num;
	my $j;
	for($j=0;$j<$num;$j++,$ii++) {
	    my $byte = $image_data[$ii];
	    push @image_out,$byte; # send the byte
	}
	$num_bytes = $num_bytes - $block_size;
    }

    # ================================================
    # End of data.
    # ================================================
    push @image_out,$EI; # end of data
    push @image_out,0;
    push @image_out,0x3b;
    if( $debug_flag ) {
	print "$ii image codes written\n";
    }
    Write(\*HND,\@image_out);
    
    close HND;
}
# ================================================
# WritePngImage
# ================================================
sub WritePngImage
{
    my $img_file = shift;
    my $in_raster_image = shift;
    my $debug_flag = shift;
    my $width = shift;
    my $height = shift;
    my $ct_flag = shift;
    my $bg_red = shift;
    my $bg_green = shift;
    my $bg_blue = shift;
    my $fg_red = shift;
    my $fg_green = shift;
    my $fg_blue = shift;

    # TODO: Implement PNG format.
}
# ================================================
# Integer divide with modulus.
# ================================================
sub IntDiv
{
    my $num = shift;
    my $div = shift;

    my $x = 0;
    if( $num < $div ) {
	return (0,$num);
    }
    while( $num > $div ) {
	$num = $num - $div;
	$x++;
    }
    return ($x,$num);
}
# ================================================
# Pixmap16x24Digit0
# ================================================
sub Pixmap16x24Digit0
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit1
# ================================================
sub Pixmap16x24Digit1
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,
                   0,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit2
# ================================================
sub Pixmap16x24Digit2
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,
                   0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,
                   0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,
                   0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,
                   0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit3
# ================================================
sub Pixmap16x24Digit3
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit4
# ================================================
sub Pixmap16x24Digit4
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,1,1,1,0,0,
                   0,0,0,0,0,0,1,1,1,0,0,1,1,1,0,0,
                   0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,
                   0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,
                   0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit5
# ================================================
sub Pixmap16x24Digit5
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit6
# ================================================
sub Pixmap16x24Digit6
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,
                   0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,1,1,1,1,0,0,0,0,0,1,1,1,0,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit7
# ================================================
sub Pixmap16x24Digit7
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
                   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,
                   0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,
                   0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,
                   0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit8
# ================================================
sub Pixmap16x24Digit8
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Digit9
# ================================================
sub Pixmap16x24Digit9
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,
                   0,0,1,1,1,1,0,0,0,0,1,1,1,1,1,0,
                   0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
                   0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,
                   0,0,0,0,0,1,1,1,1,1,1,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,
                   0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,
                   0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,
                   0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
                   0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Comma
# ================================================
sub Pixmap16x24Comma
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,
                   0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Colon
# ================================================
sub Pixmap16x24Colon
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Dot
# ================================================
sub Pixmap16x24Dot
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24SemiColon
# ================================================
sub Pixmap16x24SemiColon
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,
                   0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap16x24Space
# ================================================
sub Pixmap16x24Space
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit0
# ================================================
sub Pixmap8x8Digit0
{
    my @pixmap = ( 0,1,1,1,1,1,0,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   0,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit1
# ================================================
sub Pixmap8x8Digit1
{
    my @pixmap = ( 0,0,0,1,1,0,0,0,
                   0,0,1,1,1,0,0,0,
                   0,1,1,1,1,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit2
# ================================================
sub Pixmap8x8Digit2
{
    my @pixmap = ( 0,1,1,1,1,1,0,0,
                   1,1,0,0,0,1,1,0,
                   0,0,0,0,0,1,1,0,
                   0,0,0,0,1,1,0,0,
                   0,0,1,1,0,0,0,0,
                   0,1,1,1,0,0,0,0,
                   1,1,1,1,1,1,1,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit3
# ================================================
sub Pixmap8x8Digit3
{
    my @pixmap = ( 0,1,1,1,1,1,0,0,
                   1,1,0,0,0,1,1,0,
                   0,0,0,0,0,1,1,0,
                   0,0,1,1,1,1,0,0,
                   0,0,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   0,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit4
# ================================================
sub Pixmap8x8Digit4
{
    my @pixmap = ( 0,0,0,0,1,1,1,0,
                   0,0,0,1,1,1,1,0,
                   0,0,1,1,0,1,1,0,
                   0,1,1,0,0,1,1,0,
                   1,1,1,1,1,1,1,0,
                   0,0,0,0,0,1,1,0,
                   0,0,0,0,0,1,1,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit5
# ================================================
sub Pixmap8x8Digit5
{
    my @pixmap = ( 1,1,1,1,1,1,1,0,
                   1,1,0,0,0,0,0,0,
                   1,1,0,0,0,0,0,0,
                   1,1,1,1,1,1,0,0,
                   0,0,0,0,0,1,1,0,
                   0,0,0,0,0,1,1,0,
                   1,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit6
# ================================================
sub Pixmap8x8Digit6
{
    my @pixmap = ( 0,1,1,1,1,1,0,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,0,0,0,
                   1,1,1,1,1,1,0,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   0,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit7
# ================================================
sub Pixmap8x8Digit7
{
    my @pixmap = ( 1,1,1,1,1,1,1,0,
                   0,0,0,0,0,1,1,0,
                   0,0,0,0,1,1,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,1,1,0,0,0,0,
                   0,1,1,0,0,0,0,0,
                   0,1,1,0,0,0,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit8
# ================================================
sub Pixmap8x8Digit8
{
    my @pixmap = ( 0,1,1,1,1,1,0,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   0,1,1,1,1,1,0,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   0,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Digit9
# ================================================
sub Pixmap8x8Digit9
{
    my @pixmap = ( 0,1,1,1,1,1,0,0,
                   1,1,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   0,1,1,1,1,1,1,0,
                   0,0,0,0,0,1,1,0,
                   1,1,0,0,0,1,1,0,
                   0,1,1,1,1,1,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Comma
# ================================================
sub Pixmap8x8Comma
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,1,1,1,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Colon
# ================================================
sub Pixmap8x8Colon
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Dot
# ================================================
sub Pixmap8x8Dot
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8SemiColon
# ================================================
sub Pixmap8x8SemiColon
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,1,1,0,0,0,
                   0,1,1,1,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Pixmap8x8Space
# ================================================
sub Pixmap8x8Space
{
    my @pixmap = ( 0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0,
                   0,0,0,0,0,0,0,0 );
    return @pixmap;
}
# ================================================
# Format the string according to the -align,
# -pad, -num_digits and -commas flags.
# ================================================
sub FormatString
{
    my $align = shift;
    my $commas_flag = shift;
    my $current_count = shift;
    my $num_digits = shift;
    my $pad = shift;

    my $counter_string;
    if( $commas_flag == 0 ) {
	if( $pad eq "0" ) {
	    # Pad with leading zeros.
	    # The alignment specification is ignored.
	    my $fmt = "%0${num_digits}d";
	    $counter_string = sprintf $fmt,$current_count;
	}
	elsif( $pad eq " " ) {
	    # Pad with leading spaces.
	    # Make sure that it is aligned correctly.
	    if( $align eq "right" ) {
		my $fmt = "%${num_digits}d";
		$counter_string = sprintf $fmt,$current_count;
	    }
	    elsif( $align eq "left" ) {
		my $fmt = "%-${num_digits}d";
		$counter_string = sprintf $fmt,$current_count;
	    }
	    elsif( $align eq "center" ) {
		my $tmp1 = sprintf "%d",$current_count;
		my $len = length $tmp1;
		if( $len < $num_digits ) {
		    my $tmp2 = $num_digits - $len;
		    $tmp2 = sprintf "%d",$tmp2/2;
		    my $fmt = "%${tmp2}s";
		    $tmp2 = sprintf $fmt," ";
		    $counter_string = "${tmp2}${tmp1}${tmp2}";
		    $len = length $counter_string;
		    if( $len < $num_digits ) {
			$counter_string = " ${counter_string}";
		    }
		}
		else {
		    $counter_string = $tmp1;
		}
	    }
	}
    }
    else {
	# Comma's were specified.
	if( $pad eq "0" ) {
	    # Zero pad requires special processing.
	    # First the current_count string is
	    # padded out with zeros, then the
	    # leading characters are trimmed.
	    # Alignment is ignored because all characters
	    # are used.
	    my $fmt = "%0${num_digits}d";
	    my $tmp = sprintf $fmt,$current_count;
	    $current_count = $tmp;
	    my $len = length $current_count;
	    while( $len > 3 ) {
		my $digits = substr($current_count,$len-3,3);
		$counter_string = ",$digits$counter_string";
		$tmp = substr($current_count,0,$len-3);
		$current_count = $tmp;
		$len = length $current_count;
	    }
	    $counter_string = "$current_count$counter_string";

	    # Now trim off the leading characters.
	    $len = length $counter_string;
	    while( $len > $num_digits ) {
		$tmp = substr($counter_string,1);
		$counter_string = $tmp;
		$len = length $counter_string;
	    }

	    # Now change the leading comma to a space,
	    # if necessary.
	    if( $counter_string =~ /^,/ ) {
		$counter_string =~ s/^,/ /;
	    }
	}
	else {
	    my $len = length $current_count;
	    while( $len > 3 ) {
		my $digits = substr($current_count,$len-3,3);
		$counter_string = ",$digits$counter_string";
		$current_count = sprintf "%d",($current_count/1000);
		$len = length $current_count;
	    }
	    $counter_string = "$current_count$counter_string";
	    if( $align eq "right" ) {
		$len = length $counter_string;
		if( $len < $num_digits ) {
		    my $num_spaces = $num_digits - $len;
		    my $spaces = sprintf "%${num_spaces}s",$pad;
		    $counter_string = "$spaces$counter_string";
		}
	    }
	    elsif( $align eq "left" ) {
		$len = length $counter_string;
		if( $len < $num_digits ) {
		    my $num_spaces = $num_digits - $len;
		    my $spaces = sprintf "%${num_spaces}s",$pad;
		    $counter_string = "$counter_string$spaces";
		}
	    }
	    elsif( $align eq "center" ) {
		$len = length $counter_string;
		if( $len < $num_digits ) {
		    my $num_spaces = ($num_digits - $len) / 2;
		    my $spaces = sprintf "%${num_spaces}s",$pad;
		    $counter_string = "$spaces$counter_string$spaces";
		    $len = length $counter_string;
		    if( $len < $num_digits ) {
			$counter_string = " ${counter_string}";
		    }
		}
	    }
	}
    }

    return $counter_string;
}

# ================================================
# Parse args
# This parse both cgi type arguments and
# command line arguments. In cgi mode
# it assumes that the separator is &.
# ================================================
sub ParseArgs
{
    my $in_args = shift;
    my @args = @$in_args;

    # 0 if this a command line help request
    # 1 if this a CGI help request
    my $html_flag = 0;
    my $tmp1 = $ENV{'GATEWAY_INTERFACE'};
    if( $tmp1 ne "" ) {
	# This is a CGI script.
	$html_flag = 1;
	my $arg = $ENV{'QUERY_STRING'};
	@args = split(/\&/,$arg);
    }

    # Initialize the return arguments.
    my $align = "right";
    my $bg_red = 0x00;
    my $bg_green = 0x00;
    my $bg_blue = 0x00;
    my $commas_flag = 0;
    my $counter_file = "counter.dat";
    my $debug_flag = 0;
    my $fg_red = 0xff;
    my $fg_green = 0xff;
    my $fg_blue = 0xff;
    my $font = "8x8";
    my $format = "gif";
    my $id = "__anonymous__";
    my $img_file = "";
    my $ct_flag = 1;
    my $num_digits = 6;
    my $pad = "0";
    my $text = "";

    # Initialize the internal arguments.
    my $env_flag = 0;

    # Process the command line arguments.
    # The leading - for switches is optional.
    while( $#args >= 0 ) {
	my $arg = shift @args;

	# Strip off the leading - if it is there.
	# This means that I only have to check
	# for the whole word switch values.
	$arg =~ s/^-// if( $arg =~ /^-/ );
	if( $arg =~ /^align=(right|left|center)/ ) {
	    $align = $1;
	}
	elsif( $arg =~ /^bg=(\d+),(\d+),(\d+)/ ) {
	    $bg_red = $1;
	    $bg_green = $2;
	    $bg_blue = $3;
	}
	elsif( $arg =~ /^commas$/ ) {
	    $commas_flag = 1;
	}
	elsif( $arg =~ /^counter_file=(.*)/ ) {
	    $counter_file = $1;
	}
	elsif( $arg eq "env" ) {
	    # This variable is not documented because it
	    # is really only useful for CGI debugging.
	    $env_flag = 1;
	}
	elsif( $arg =~ /^debug$/ ) {
	    $debug_flag = 1;
	}
	elsif( $arg =~ /^fg=(\d+),(\d+),(\d+)/ ) {
	    $fg_red = $1;
	    $fg_green = $2;
	    $fg_blue = $3;
	}
	elsif( $arg =~ /^font=8x8/ ) {
	    $font = "8x8";
	}
	elsif( $arg =~ /^font=16x24/ ) {
	    $font = "16x24";
	}
	elsif( $arg =~ /^format=gif/ ) {
	    $format = "gif";
	}
	elsif( $arg =~ /^format=png/ ) {
	    $format = "png";
	}
	elsif( $arg =~ /^id=(.+)/ ) {
	    $id = $1;
	}
	elsif( $arg =~ /^img_file=(.+)/ ) {
	    $img_file = $1;
	}
	elsif( $arg =~ /^help$/ ) {
	    &Usage($html_flag,"",$counter_file,$env_flag);
	    exit 0;
	}
	elsif( $arg =~ /^noct$/ ) {
	    $ct_flag = 0;
	}
	elsif( $arg =~ /^num_digits=(\d+)/ ) {
	    $num_digits = $1;
	}
	elsif( $arg =~ /^pad=space/ ) {
	    $pad = " ";
	}
	elsif( $arg =~ /^pad=zero/ ) {
	    $pad = "0";
	}
	elsif( $arg =~ /^text=(\d+)/ ) {
	    $text = $1;
	}
	elsif( $arg =~ /^width=(\d+)/ ) {
	    $num_digits = $1;
	}
	elsif ( $arg ne "" ) {
	    &Usage($html_flag,$arg,$counter_file,$env_flag);
	    exit 0;
	}
    }

    # Make sure that the GIF information doesn't
    # go to stdout in debug mode.
    $img_file = "counter.gif" if( $img_file eq "" && $debug_flag == 1 );

    # Return arguments.
    return ( $align, $bg_red, $bg_green, $bg_blue, $commas_flag,
	     $counter_file, $debug_flag, $fg_red, $fg_green, $fg_blue,
	     $font, $format, $id, $img_file, $ct_flag, $num_digits, $pad,
	     $text );
}
# ================================================
# Display the program usage.
# ================================================
sub Usage
{
    my $html_flag = shift;
    my $arg = shift;
    my $counter_file = shift;
    my $env_flag = shift;

    my $errmsg = "";
    if( $arg ne "" ) {
	$errmsg =  "ERROR: Unrecognized switch '$arg'\n";
	$errmsg .= "       or invalid switch arguments.\n";
    }
    my $msg =<<'END';

Usage: counter.pl [<switches>]

    It is important to note that the '-' prefix on the switches is
    optional. This makes it easier to code the IMG SRC reference in
    your HTML file.

    -align=[left,center,right]
        Define the alignment.

    -bg=<red>,<green>,<blue>
        The background color. Red is a number from 0-255 that
	specifies the red intensity. Green is a number from 0-255 that
	specifies the green intensity and blue is a number from 0-255
	that specifies the blue intensity. Here is an example usage
	that specifies a black background (the default): -bg=0,0,0

    -commas
        Insert commas, these are counted as digits. The default is to
	not insert commas.

    -counter_file=<file_name>
        The name of the counter file.

    -debug
        Enable interactive debugging. It should not be used from the
	CGI invocation. It causes the GIF data to be written to
	counter.gif instead of standard out. It also generates a
	number of informational messages.

    -fg=red,green,blue
        The foreground color. Red is a number from 0-255 that
	specifies the red intensity. Green is a number from 0-255 that
	specifies the green intensity and blue is a number from 0-255
	that specifies the blue intensity. Here is an example usage
	that specifies a white foreground (the default): -fg=255,255,255

    -font=[8x8,16x24]
        Specify the font. The 8x8 format is pretty small but useful
        because it conveys the information without cluttering up the
        page. The 16x24 is larger and more readable. Both were hand
        generated by me and can updated by editing this script. The
        8x8 font was derived from the font generated by text2gif from
        authors of the libungif package.

    -format=[gif,png]
        Specify the image format. The choices are uncompressed GIF and
	PNG.  Note that png format is not yet available.

    -help
        Display this help message and exit.

    -id=<unique_user_id>
        The user id that designates your unique counter in the data
	file. The unique id should only contain alpha-numeric
	characters, dots, dashes and underscores. It should be derived
	from something like your email address and project. For ccdoc,
	the id that I use is jlinoff-www.joelinoff.com-ccdoc.

	If no id is specified, the default __anonymous__. Beware that
	using this id will not provide valid count information unless
	you are using a custom data file.

    -img_file=<file_name>
        The output image file name (for debugging). If this switch is
	not specified, the image is output to stdout.

    -noct
        Don't output the content-type header. This is useful for
	debugging when creating a standalone GIF. In normal operation
	a type header is output to inform the HTML processor that the
	incoming bits represent a GIF image.

    -num_digits=<number>
        The number of digits to display (with leading zero's). This
        is the same as width.

    -pad=[space,zero]
        Defines the pad character.

    -text=<string>
        Manually specify a digit string on the command line. The
	string can contain only contain decimal digits.  When this
	switch is specified, no data is written to the counter file.

    -width
        The width of the field. Same as -num_digits.

    The examples below show how to use this script from the command line:

        % perl counter.pl -help
	% perl counter.pl -debug
	% perl counter.pl > tmp.gif
	% perl counter.pl -fg=255,0,0 -bg=0,255,0
	% perl counter.pl -commas -counter_file=counter.dat
	% perl counter.pl -noct -commas -num_digits=10 -text="1234567" > tmp.gif
	% perl counter.pl -noct -num_digits=10 -text="1234567" -img_file tmp.gif

    The example below shows how to use this script from an HTML
    file.

        <IMG BORDER=1
              SRC="http://mysite/cgi-bin/counter.pl?id=jlinoff-www.joelinoff.com-general&align=center&pad=space&commas&counter_file=/path/to/counter/dat/file/mycounter.dat">

    Acknowlegements:
        
    Eric S. Raymond
        For the libungif package. I used text2gif to derive the 8x8
        fonts used here.

    Dr. Tom Lane
       For his explanation of the LZW patent issues in his posting to
       comp.graphics.misc on Dec 05 1996 as referenced in Subject V-7
       in part of the FAQ found at:

       ftp://rtfm.mit.edu/pub/usenet/news.answers/graphics/fileformats-faq/part.

       I also used his explanation of where to insert the CC codes to
       create an uncompressed (or expanded) GIF that does not use the
       LZW algorithm.

END
my $tmpx; # hack to fix editor alignment
    if( $html_flag ) {
	$msg =~ s/&/&amp;/g;
	$msg =~ s/</&lt;/g;
	$msg =~ s/>/&gt;/g;

	# ================================================
	# Arguments
	# ================================================
	print "Content-type: text/html\n\n";
	print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n";
	print "<html>\n";
	print "<head>\n";
	print "<title>counter.pl help</title>\n";
	print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n";
	print "</head>\n";
	print "<body>\n";
	print "<center>\n";
	print "<h3>Help</h3>\n";
	print "</center>\n";
	print "<pre>\n";
	print $errmsg;
	my $tmp = "\$";
	print "\$Id: counter.pl,v 1.3 2004/09/30 07:34:13 jlinoff Exp $tmp\n";
	print "$msg";
	print "</pre>\n";

	# ================================================
	# HTML Examples
	# ================================================
	print "<hr>\n";
	print "<center>\n";
	print "<h3>HTML Examples</h3>\n";
	print "</center>\n";
	print "<p>\n";
	print "This section shows how to add a counter to your HTML file.\n";
	print "All of the examples assume that the image statement looks like this:\n";
	print "&lt;img border=1 src=<i>example</i>&gt;.\n";
	print "</p>\n";

	my $cgi = "http://www.joelinoff.com/cgi-bin/counter.pl";
	##my $cgi = "http://ccdoc.sourceforge.net/cgi-bin/counter.pl";
	##my $cgi = "http://web/randd/cgi-bin/jdl_test_counter.pl";
	my $src;
	##my $extra = "&counter_file=jdl_test_example.dat";
	my $extra = "";
	my $id = "counter-help-examples";

	$src = "$cgi?id=${id}";
	print "<h4>Example 1: Simple</h4>\n";
	print "<center>\n";
	print "<font size=2>&lt;img border=1 src=\"$src\"&gt;</font><br>\n";
	print "<img border=1 src=\"$src$extra\">";
	print "</center>\n";

	$src = "$cgi?id=${id}&pad=space";
	print "<h4>Example 2: Pad with spaces</h4>\n";
	print "<center>\n";
	print "<font size=2>&lt;img border=1 src=\"$src\"&gt;</font><br>\n";
	print "<img border=1 src=\"$src$extra\">";
	print "</center>\n";

	$src = "$cgi?id=${id}&pad=space&align=center";
	print "<h4>Example 3: Center</h4>\n";
	print "<center>\n";
	print "<font size=2>&lt;img border=1 src=\"$src\"&gt;</font><br>\n";
	print "<img border=1 src=\"$src$extra\">";
	print "</center>\n";

	$src = "$cgi?id=${id}&num_digits=12";
	print "<h4>Example 4: Change the width</h4>\n";
	print "<center>\n";
	print "<font size=2>&lt;img border=1 src=\"$src\"&gt;</font><br>\n";
	print "<img border=1 src=\"$src$extra\">";
	print "</center>\n";

	$src = "$cgi?id=${id}&fg=255,0,0&bg=0,255,0";
	print "<h4>Example 5: Change the foreground and background colors</h4>\n";
	print "<center>\n";
	print "<font size=2>&lt;img border=1 src=\"$src\"&gt;</font><br>\n";
	print "<img border=1 src=\"$src$extra\">";
	print "</center>\n";

	$src = "$cgi?id=${id}&font=16x24";
	print "<h4>Example 6: Change the font size</h4>\n";
	print "<center>\n";
	print "<font size=2>&lt;img border=1 src=\"$src\"&gt;</font><br>\n";
	print "<img border=1 src=\"$src$extra\">";
	print "</center>\n";

	$src = "$cgi?id=${id}&commas&pad=space&width=10";
	print "<h4>Example 7: Embed commas</h4>\n";
	print "<center>\n";
	print "<font size=2>&lt;img border=1 src=\"$src\"&gt;</font><br>\n";
	print "<img border=1 src=\"$src$extra\">";
	print "</center>\n";

	$src = "$cgi?id=${id}&commas&pad=space&width=14&text=1234567890&font=16x24&align=center";
	print "<h4>Example 8: Write a fixed number</h4>\n";
	print "<center>\n";
	print "<font size=2>&lt;img border=1 src=\"$src\"&gt;</font><br>\n";
	print "<img border=1 src=\"$src$extra\">";
	print "</center>\n";

	# ================================================
	# Usage Data
	# ================================================
	print "<hr>\n";
	print "<center>\n";
	print "<h3>Used Ids for $counter_file</h3>\n";
	print "<pre>\n";
	if( -e $counter_file ) {
	    my $cnt = 0;
	    print "Id                               Count\n";
	    print "================================ ======\n";
	    open HND,"$counter_file";
	    while( <HND> ) {
		if( /^\s*(\S+)\s+(\d+)\s*$/ ) {
		    printf "%-32s %6d\n",$1,$2;
		    $cnt++;
		}
	    }
	    printf "\n%-32s %6d\n","TOTAL",$cnt;
	    close HND;
	}
	print "</pre>\n";
	print "</center>\n";


	# ================================================
	# License
	# ================================================
	print "<hr>\n";
	print "<center>\n";
	print "<h3>License</h3>\n";
	print "</center>\n";
	print "Copyright (C) 1998-2003 by Joe Linoff (http://www.joelinoff.com)<br>\n";
	print "<p>\n";
	print "Permission is hereby granted, free of charge, to any person obtaining\n";
	print "a copy of this software and associated documentation files (the\n";
	print "\"Software\"), to deal in the Software without restriction, including\n";
	print "without limitation the rights to use, copy, modify, merge, publish,\n";
	print "distribute, sublicense, and/or sell copies of the Software, and to\n";
	print "permit persons to whom the Software is furnished to do so, subject to\n";
	print "the following conditions:\n";
	print "</p>\n";
	print "<p>\n";
	print "The above copyright notice and this permission notice shall be\n";
	print "included in all copies or substantial portions of the Software.\n";
	print "</p>\n";
	print "<p>\n";
	print "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n";
	print "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n";
	print "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n";
	print "IN NO EVENT SHALL JOE LINOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n";
	print "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n";
	print "ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n";
	print "OTHER DEALINGS IN THE SOFTWARE.\n";
	print "</p>\n";
	print "<p>\n";
	print "Comments and suggestions are always welcome.\n";
	print "</p>\n";

	# ================================================
	# CGI Environment Variables
	# ================================================
	if( $env_flag ) {
	    print "<hr>\n";
	    print "<center>\n";
	    print "<h3>CGI Environment Variables</h3>\n";
	    print "</center>\n";
	    print "<pre>\n";
	    my $key;
	    foreach $key ( sort keys %ENV ) {
		my $e = $ENV{$key};
		$e =~ s/&/&amp;/g;
		$e =~ s/</&lt;/g;
		$e =~ s/>/&gt;/g;
		printf "%-20s \"%s\"\n",$key,$e;
	    }
	    print "</pre>\n";
	}

	print "</body>\n";
	print "</html>\n";
    }
    else {
	print $errmsg;
	my $tmp = "\$";
	print "\$Id: counter.pl,v 1.3 2004/09/30 07:34:13 jlinoff Exp $tmp\n";
	print $msg;
    }
}

