makeimage.gif



makeimage.pl - An Perl code submission for TWiT's Coding 101

A Perl script to create a dynamic image (JPG, PNG, OR GIF) that says "Hello PersonsName".

By Joe C. Hecht A.K.A. TJoe

TJoe's splot at CODE4SALE, LLC

TJoe's Coding 101 Page

Contact TJoe



NOTE: Written for Windows with the ActiveState Perl installed - See notes below for Non-Windows instructions.

Download the zip (With formatted line endings for Windows, Mac and Linux)



#!/usr/bin/perl -w
#-------------------------------------------------------------------------------
# The line above is the "SheBang" line and must be the first line of the script.
# The "SheBang line" instructs unix like systems (such as the Mac) on where to
# find the Perl interpreter to use to run this script, and may need to be adjusted
# for your system. We have added "-w" to the SheBang as a parameter to ask the
# Perl interpreter to emit additional warnings should our source code have flaws.
# In general, the "SheBang" line is ignored under Windows, however, some Perl
# versions may parse the line and utilize any parameters found (such as the -w).
#
# A "SheBang" line that works on my Mac OSX system looks like this:
#
# #!/usr/local/ActivePerl-5.16/bin/perl -w
#
# A "SheBang" line that works on my Linux Mint17 system looks like this:
#
# #!/opt/asperl/bin/perl -w
#
# A "SheBang" line that works on my generic linux/unix systems, using the system
# installed Perl looks like this:
#
# #!/usr/bin/perl -w
#
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# NOTE: Comments in Perl can be added to your source file by using the hash symbol
# ("#"). Anything following the "#" on a line are ignored by the interpreter.
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# This program was written for Windows with the ActiveState Perl interpreter
# ActivePerl-5.16.3.1604 default installation. It can be tweaked to run under
# other operating systems and other Perl installations. Please see the section
# below "Running this program on NON-Windows systems".
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# What this program does:
#-------------------------------------------------------------------------------
# This program is an example of using Perl to create a dynamic image file (such
# as a JPEG, PNG, OR GIF) that says "Hello PersonsName", and can be very useful
# when used by a webserver to generate a web page containing a custom greeting
# when the name of the person is known (for example, in response for when a user
# logs onto a site. Note that this source may depart from customary semantics
# of the Perl language, as it is intended as a teaching example for the beginning
# programmer. Those wishing to learn the proper and customary semantics for the
# Perl programming language are encouraged to seek additional references.
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Editing this program under Windows:
#-------------------------------------------------------------------------------
# As an alternative to Notepad, the Notepad++ editor is highly recommended.
# The Notepad++ editor is available at http://notepad-plus-plus.org/
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Running this program under Windows:
#-------------------------------------------------------------------------------
# Copy this script to a text editor, and save it as "makeimage.pl".
# You will need to open a command (or terminal) window, and type:
#
# makeimage.pl > myimage.gif
#
# and the image data will be piped into a newly created file "myimage.gif".
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Running this program on NON-Windows systems:
# Running this program on NON-ActiveState Perl installations:
#-------------------------------------------------------------------------------
# This program was written for Windows with the ActiveState Perl interpreter
# ActivePerl-5.16.3.1604 default installation. It can be tweaked to run under
# other operating systems and other Perl installations, providing that the
# "GD" package is installed (please see instructions for adding packages to
# your version of Perl. Further, the font path and file name to use must be
# correctly set to the path of a valid TrueType font on your system. Finally,
# you may need to issue the command "chmod +x ThisScriptsName.pl" in your
# terminal window to set the execution rights to allow the script to run:
#
# chmod +x makeimage.pl
#
# ./makeimage.pl > myimage.gif
#
# and the image data will be piped into a newly created file "myimage.gif".
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Note on source formatting of this script:
#-------------------------------------------------------------------------------
# This source has been verified critic free using ActiveState PerlCritic version
# 10.0 with the critique level set to "brutal" (the highest setting). The
# placement of documentation, comments, and the source formatting may stray a bit
# from commonly accepted Perl practices only for the purpose of a "first look"
# when introducing the language to newcomers. Programmers are encouraged to
# seek out accepted reference material to assist them in a better understanding
# of commonly accepted Perl programming practices. The POD (Plain Old Documentation)
# section was omitted on purpose.
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# How this program works:
#-------------------------------------------------------------------------------
# The program calls a single function "outputcustomimage()" passing:
# (a) The image file type to make (.gif, .png, or .jpg).
# (b) The persons name to write into the image.
# (c) The width of the image to make.
# (d) The path and file name of the font to use.
#
# The persons name will be written into the image as a two line string:
#
# Hello
# Padre
#
# using white lettering onto a black background.
#
# This string will be sized as large as possible to fit the width (95% of the width).
# The height of the image will be dynamically calculated as needed, making the image
# ideal (for example) to embed into a free flowing web page containing table columns.
#
# To be able to calculate the sizing of the font to fit the width, and also
# calculate the height of the image to create, we will need to first create
# a temporary image and output the text at some arbitrary size (10 pixels).
# We can then obtain the bounding box of the string that was output, and from
# that information, we can calculate both the actual font size needed to fill
# the desired width, and the height required for the actual image we will output.
# We then will create the actual image, draw the text onto the image, then
# output the image data to the STDOUT I/O channel, allowing the image data
# to be piped to a file, or elsewhere as desired.
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Output File Format Note:
#-------------------------------------------------------------------------------
# This program uses the GD graphics engine, and is capable of creating graphics
# in a number of different formats. While we are providing a code example for
# the jpeg format, it is worth noting that this format is intended for use only
# with photographic images due to it's use of lossy compression is generally
# less suitable for use with computer generated graphics containing shapes and
# text than other lossless formats such as the gif and png formats.
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# The following lines instruct the Perl interpreter that we wish to use additional
# installed modules. The "strict" and "warnings" modules are part of the standard
# Perl installation, and help to catch programming errors. The "GD" module is
# a graphics module that is not part of the most standard Perl distros (although
# it is installed with the ActiveState ActivePerl-5.16.3.1604 default installation.
# The "GD" module is used to create graphics images. The ReadOnly module is used
# to assign constant values and bypass warnings given by the PerlCritic test suite.
#-------------------------------------------------------------------------------
use strict;
use warnings;
use GD;
use Readonly;
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Properly define some constants so we do not get beat up by critics for hard
# coding commonly used "magic numbers" in the code. The keyword "my" declares
# variables to be local (lexically) to the enclosing block, file, or eval.
#-------------------------------------------------------------------------------
Readonly my $VALUE_THIRTYTWO => 32;
Readonly my $VALUE_TWO_HUNDRED_FIFTY_FIVE => 255;
Readonly my $VALUE_TEN_POINT_ZERO => 10.0;
Readonly my $ZERO_POINT_NINE_FIVE => 0.95;
Readonly my $ONE_POINT_TWO_FIVE => 1.25;
Readonly my $ZERO  => 0;
Readonly my $ONE   => 1;
Readonly my $TWO   => 2;
Readonly my $THREE => 3;
Readonly my $FOUR  => 4;
Readonly my $FIVE  => 5;
Readonly my $SIX   => 6;
Readonly my $SEVEN => 7;
Readonly my $EIGHT => 8;
Readonly my $NINE  => 9;
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Give this file a version number to avoid PerlCritic warnings.
#-------------------------------------------------------------------------------
our $VERSION = 1.0;
#-------------------------------------------------------------------------------



#*******************************************************************************
# Note: The main block of the program starts here.
#*******************************************************************************



#-------------------------------------------------------------------------------
# Declare a variable called $fontpathandfilename. We will set it up to first
# look for a common font found in Windows, and test to see if the file exists.
# If that file does not exist, we will set the variable to the path of a commonly
# found font on the Mac OSX operating system, and test to see if that file exists.
# If that file does not exist, we will set the variable to the path of a commonly
# found font on Ubuntu like Linux operating systems. If that file does not exist,
# then we will exit the program, returning an exit code to the operating system
# indicating failure, and no image (or error message) will be written.
#-------------------------------------------------------------------------------
# NOTE: You many need to adjust the font used for your operating system!
#-------------------------------------------------------------------------------
my($fontpathandfilename) = 'c:/Windows/Fonts/comic.ttf';
if (!(-e $fontpathandfilename)) {
  $fontpathandfilename = '/Library/Fonts/AppleGothic.ttf';
}
if (!(-e $fontpathandfilename)) {
  $fontpathandfilename = '/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-BI.ttf';
}
if (!(-e $fontpathandfilename)) {
  exit 1;
}
#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Declare a variable to hold the return value (zero if successful - non-zero if not)
# and call our outputcustomimage() function, passing:
# (a) The type of image to make 'gif', 'jpg', or 'png'.
# (b) The persons name to write.
# (c) The width of the image to make.
# (d) The path to the font to use.
#-------------------------------------------------------------------------------
my($retval) = outputcustomimage('gif', #Image type 'gif', 'jpg', or 'png'
                                'Padre',
                                $VALUE_TWO_HUNDRED_FIFTY_FIVE,
                                $fontpathandfilename);

#-------------------------------------------------------------------------------



#-------------------------------------------------------------------------------
# Exit the program, passing the return value back to the operating system.
#-------------------------------------------------------------------------------
exit $retval;
#-------------------------------------------------------------------------------



#*******************************************************************************
# Note: The main block of the program ends here.
#*******************************************************************************



#-------------------------------------------------------------------------------
# The outputcustomimage() function. Takes in:
# (a) The type of image to make 'gif', 'jpg', or 'png'.
# (b) The persons name to write.
# (c) The width of the image to make.
# (d) The path to the font to use.
# creates and pipes an image to STDOUT, returns zero/non-zero for success/fail.
#-------------------------------------------------------------------------------
# outputcustomimage($imagetype,
#                   $personsname,
#                   $imagewidth,
#                   $fontfilename);
#-------------------------------------------------------------------------------
sub outputcustomimage {
#-------------------------------------------------------------------------------
  # Retrieve the parameters passed to our function and place into variables.
  # Note: my($VariableName) declares a variable, and shift retrieves a
  # parameter from the stack in the order passed to the function.
  my($imagetype) = shift;
  my($personsname) = shift;
  my($imagewidth) = shift;
  my($fontfilename) = shift;
  #-----------------------------------------------------------------------------
  # Declare a status variable and set it to non-zero (an error)
  # we will set it to zero (good) when we prove all went well.
  my($status) = 1;
  #-----------------------------------------------------------------------------



  #-----------------------------------------------------------------------------
  # Note: an eval block "evaluates" code to see if it blows up.
  # If it does blow up, we will catch it in a code block marked below
  # and recover somewhat gracefully, rather than locking things up.
  #-----------------------------------------------------------------------------
  eval{
  #-----------------------------------------------------------------------------

    # Create a string containing "Hello", a NewLine command , and the $personsname.
    # Note: "\n" creates a new line command and "." will concatenate two strings.
    my($texttodraw) = "Hello\n" . $personsname;

    # Create a small temporary image (32 by 32 pixels) to use to calculate the size of our text,
    # by calling the GD::Image->new() constructor that will return a newly created GD::Image object.
    my($tempimage) = GD::Image->new($VALUE_THIRTYTWO,
                                    $VALUE_THIRTYTWO);

    # Create the color object we will use for our text. The color is given as an
    # additive color of red, green, and blue (RGB), where each component can range
    # from none (0) to all (255), so black is (0, 0, 0) and white is (255, 255, 255).
    my($tempimagewhite) = $tempimage->colorAllocate($VALUE_TWO_HUNDRED_FIFTY_FIVE,  # red
                                                    $VALUE_TWO_HUNDRED_FIFTY_FIVE,  # green
                                                    $VALUE_TWO_HUNDRED_FIFTY_FIVE); # blue

    # Draw our text ten pixels high, retrieving the bounding area of the text that was drawn.
    my($fontsize) = $VALUE_TEN_POINT_ZERO;
    my(@bounds) = $tempimage->stringFT($tempimagewhite,
                                       $fontfilename,
                                       $fontsize,
                                       0, # draw angle
                                       0, # draw at x
                                       0, # draw at y
                                       $texttodraw);

    # Note: bounds contains the bounding box of the text we drew.
    # The corners of the box are at:
    # LowerLeftX  = $bounds[0] and LowerLeftY  = $bounds[1]
    # LowerRightX = $bounds[2] and LowerRightY = $bounds[3]
    # UpperRightX = $bounds[4] and UpperRightY = $bounds[5]
    # UpperLeftX  = $bounds[6] and UpperLeftY  = $bounds[7]
    my($bboxleft)   = $bounds[$SIX];
    my($bboxtop)    = $bounds[$SEVEN];
    my($bboxright)  = $bounds[$TWO];
    my($bboxbottom) = $bounds[$THREE];

    # Calculate the absolute (positive) bounds width and height
    my($bboxwidth)  = abs $bboxright  - $bboxleft;
    my($bboxheight) = abs $bboxbottom - $bboxtop;

    # Make sure the width is at least one pixel.
    if ($bboxwidth < 1.0) {
      $bboxwidth = 1;
    }

    # Make sure the height is at least one pixel.
    if ($bboxheight < 1.0) {
      $bboxheight = 1;
    }

    # Rescale the fontsize so the text will be just a little smaller
    # than the width of our output image to allow for a border.
    $fontsize = $fontsize * (($imagewidth * $ZERO_POINT_NINE_FIVE) / $bboxwidth);

    # Because the font that is output is not always exactly as specified,
    # to be precise, we must get the bounds of the rescaled text, so that
    # we can adjust the placement of the text to allow for a small border.
    @bounds = $tempimage->stringFT($tempimagewhite,
                                   $fontfilename,
                                   $fontsize,
                                   0, #Angle
                                   0, #x
                                   0, #y
                                   $texttodraw);

    # Note: bounds contains the bounding box of the text we drew.
    # The corners of the box are at:
    # LowerLeftX  = $bounds[0] and LowerLeftY  = $bounds[1]
    # LowerRightX = $bounds[2] and LowerRightY = $bounds[3]
    # UpperRightX = $bounds[4] and UpperRightY = $bounds[5]
    # UpperLeftX  = $bounds[6] and UpperLeftY  = $bounds[7]
    $bboxleft   = $bounds[$SIX];
    $bboxtop    = $bounds[$SEVEN];
    $bboxright  = $bounds[$TWO];
    $bboxbottom = $bounds[$THREE];

    # Calculate the absolute (positive) bounds width and height
    $bboxwidth  = abs $bboxright  - $bboxleft;
    $bboxheight = abs $bboxbottom - $bboxtop;

    # Make sure the width is at least one pixel.
    if ($bboxwidth < 1.0) {
      $bboxwidth = 1;
    }

    # Make sure the height is at least one pixel.
    if ($bboxheight < 1.0) {
      $bboxheight = 1;
    }

    # Create the output image height as 125% of the
    # adjusted text height (to allow for a border).
    my($imageheight) = $bboxheight * $ONE_POINT_TWO_FIVE;


    # Create the output image.
    my($outimage) = GD::Image->new($imagewidth,
                                   $imageheight);

    # Create the color object we will use for our text. The color is given as an
    # additive color of red, green, and blue (RGB), where each component can range
    # from none (0) to all (255), so black is (0, 0, 0) and white is (255, 255, 255).
    my($outimagewhite) = $outimage->colorAllocate($VALUE_TWO_HUNDRED_FIFTY_FIVE,  #red
                                                  $VALUE_TWO_HUNDRED_FIFTY_FIVE,  #green
                                                  $VALUE_TWO_HUNDRED_FIFTY_FIVE); #blue

    my($outimageblack) = $outimage->colorAllocate(0,  #red
                                                  0,  #green
                                                  0); #blue

    # Fill the background of the image.
    $outimage->filledRectangle(0,
                               0,
                               $imagewidth,
                               $imageheight,
                               $outimageblack);


    # Calculate the text placement to center the text horizontally.
    my($xout) = (-($bboxleft)) + (($imagewidth / 2.0) - ($bboxwidth / 2.0));


    # Calculate the text placement to center the text vertically.
    my($yout) = (-($bboxtop)) + (($imageheight / 2.0) - ($bboxheight / 2.0));


    # Output the centered text - no need to capture the bounds.
    $outimage->stringFT($outimagewhite,
                        $fontfilename,
                        $fontsize,
                        0, #Angle
                        $xout,
                        $yout,
                        $texttodraw);

    # Draw a white 1 pixel rectangle border around the edge of the image.
    # Note that since we start drawing from zero, the location of the last
    # pixel row and column in the image is one less than the width and height.
    # If you do not subtract one from the width and height, the rectangle
    # you draw will be one pixel wider and higher than the width and height
    # of the image :)
    $outimage->rectangle(0,
                         0,
                         $imagewidth - 1,
                         $imageheight - 1,
                         $outimagewhite);

    # OK! We are done. We just need to grab the image data in the correct format and output it.
    my($imagedata);
    if ($imagetype eq 'jpg') {
      $imagedata = $outimage->jpeg,
                              # optional (number) compression quality (0..100-best) goes here
    } else {
      if ($imagetype eq 'gif') {
        $imagedata = $outimage->gif,
      } else {
        if ($imagetype eq 'png') {
          $imagedata = $outimage->png,
                                  # optional (number) compression amount (0..9-most) goes here
        } else {
          return($status);
        }
      }
    }


    # Set the STDOUT Channel to binary (non-text) mode
    binmode STDOUT;

    # Output the image data
    if (print $imagedata) {
      # All went well - set our status to show ok (zero).
      $status = 0;
    }

    # Return true (1) from the eval indicating all went ok.
    1;
  #-----------------------------------------------------------------------------
  # eval block end
  #-----------------------------------------------------------------------------
  } or do {
  #-----------------------------------------------------------------------------
    # The code in the eval blew up and we catch it here.
  #-----------------------------------------------------------------------------
  };
  #-----------------------------------------------------------------------------

  #-----------------------------------------------------------------------------
  # We are done - return the status from this function.
  return($status);
  #-----------------------------------------------------------------------------
}
#-------------------------------------------------------------------------------




TJoe's splot at CODE4SALE, LLC

TJoe's Coding 101 Page

Contact TJoe

Code4Sale Main Page
DMCA (Digital Millennium Copyright Act) Contact Information
Contact Our Webmaster

Copyright © 2016 by CODE4SALE, LLC - All rights reserved.
Product names, trademarks, and servicemarks mentioned are owned by their respective owners.

Valid HTML 4.0!