An Image is Worth a Thousand Words in PHP

This article, the first of two parts, describes a fun little project that will help you sharpen your image manipulation skills. The completed application takes an image and converts it to a string of text that resembles the original image. Brian Vaughn gets you started.

Sometimes programming is work, but sometimes… it’s fun.

Today we’re going to work on a fun programming project involving image manipulation. This project will require that PHP be installed with the GD library (available at http://www.boutell.com/gd/, or bundled along with PHP 4.3 and later).

The purpose of the program we are about to create is to sharpen up our image-manipulation skills. Some of you may never have worked with images before, but that’s okay. This tutorial is still a good thinking exercise, and it will come in handy if you ever find yourself needing to create or manipulate images using PHP.

Because of the complexity of the task at hand, we’re going to break this article into two separate pieces. The first part, as you’re about to see, will give an overview of our project and then jump right into setting up our conversion class. We’ll create several basic accessor methods, and we’ll also tackle the issue of cross-browser compatibility with the help of a little CSS.

Part two will then pick up where we left off and spend most of the remainder of our time working on the actual conversion process itself.

But enough talking; let’s get started!

{mospagebreak title=Project Overview}

Admittedly, the program we are about to develop may lack an immediate, real-world application. So rather than contrive a situation in which we, as developers, would be asked to create such an application, let’s simply talk about what we will be designing our program to do. The benefits of such an exercise will then become clear as we work through the various stages of development.

The basic goal of our application is simple: receive as a parameter a string designating the location of an image file (this can be a local image or a remote one), load the image, and convert it to a string of colored text that resembles the original picture. To go a step beyond that, we will also allow for multiple output modes including: color, grayscale, monochrome, and matrix. This will allow users to convert images to a variety of text-only formats. Below you’ll see a couple of ‘teaser’ sample images, displaying conversions in each of our various output formats:

 

We’re going to be using an object-oriented programming approach, so we will start off by creating a class to contain our image manipulation methods. Let’s call this class ‘img_to_txt’, and let’s store it in a file named ‘img_to_txt.php’. For now its contents should be as follows:

<?php
# used to convert images to text-only format
class img_to_txt {

} # END img_to_txt class
?>

Although there may not be an immediate need to re-use the methods we are about to create, it is still a good programming practice to make them as portable as possible. (After all, you never know when you may want to re-use something!) Because of this, we’ve chosen to bundle all of our code inside a simple helper class – but this class needs a few get-and-set methods in order to function properly. That’s what we’ll spend the first part of this article focusing on. Each of these methods will be pretty self-explanatory, and will simply allow us to define certain attributes of our image (such as its location, and the desired output format).

{mospagebreak title=Setting Up Our Class}

Since our program will be working with images, which may easily be quite large, it is a good first step for us to increase the maximum script-execution time allotted for our PHP page. The default amount of time a PHP script is allowed to execute is 30 seconds. For our example application, we will probably be well under that limit. Still, just to be on the safe side let’s increase it to a minute. To do that, let’s add the following constructor to our class:

 # constructor: allow for longer max-execution script time
 function img_to_text() {
      set_time_limit ( 100 );  
 } # END img_to_text()

Let’s also add a few attributes and their associated helper ‘set’ methods to our class:

 # set default configurable values for class
 var $char_mode = ‘NORMAL’;
 var $chars  = ‘IMGTOTEXT’;
 var $return_format = ‘COLOR’;
 var $aspect_ratio = 1;

 # used to track position of current char in $chars string
 # (only if user has selected ‘NORMAL’ char order mode)
 var $char_index = 0;

 # designates whether to display chars in ‘random’ or ‘ascending’ order
 function set_char_mode( $char_mode ) {
  $this->char_mode = ( strtoupper( $char_mode ) == ‘RANDOM’ ) ? ‘RANDOM’ : ‘NORMAL’;
 } # END set_char_mode()
 
 # chars used for return text-image (not applicable for ‘matrix’ return format)
 function set_chars( $chars ) {
    $chars  = str_replace( ‘ ‘, ”, $chars );
    $this->chars = ( strlen( $chars ) > 0 ) ? $chars : $this->chars;
 } # END set_char_mode()
 
 # return format for our image: ‘color’ (default), ‘grayscale’, ‘monochrome’, or ‘matrix’
 function set_return_format( $return_format ) {
  $this->return_format = strtoupper( $return_format );
 } # END set_return_format()
 
 # specify image aspect ratio (0.5 = half size, 1 = the same size, 2 = twice size, etc)
 function set_aspsect_ratio( $aspect_ratio ) {
  $this->aspect_ratio = $aspect_ratio;
 } # END set_aspsect_ratio()

Each of the above attributes and methods pertain to a specific area of our ‘img_to_txt’ class. Although they are pretty straightforward, we’ll take a brief look at each before proceeding. The first, ‘set_char_mode’, will allow users to specify whether they want the text that will comprise the converted picture to be printed in a linear order, or randomly. The second and related function, ‘set_chars’, allows the user to specify the text they want their image to be made of.

For instance, if we called ‘set_chars’ and passed the string “THIS IS A PICTURE” – our resulting image would be made up of the repeating phrase, “THISISAPICTURE”. However, if we then called ‘set_char_mode’ and passed it the string “random”, the resulting image would be a random arrangement of the letters “ACEHIPRSTU” (the letters found within the string, “THISISAPICTURE”). You may notice that before we set the allowable characters within the ‘set_chars’ function, we removed any blank space from the user’s string. This is because an empty space character is of very little use to our program, (which the exception of the ‘monochrome’ format), so it is best to remove any spaces up front.

Next, we have the ‘set_return_format’ function. This allows users to choose an output format for their images. The default mode is ‘color’, but other possible modes are ‘grayscale’, ‘monochrome’, and ‘matrix’. We’ll see what each of these modes looks like in a bit, so for now we’ll move on.

The last function we just added is called ‘set_aspect_ratio’. This function allows users to choose the size of their text-only image relative to the size of their original image. (An aspect ratio of 0.5 would result in a converted image that’s half of the size of the original, and so on.)

{mospagebreak title=The Browser Problem}

There are a couple of major hurdles we’ll need to overcome in order to develop our application. One of the largest is the issue of browser compatibility. Developers and designers have dealt with this issue for a very long time – and I’m sure that most of you have probably had your fair share of experiences with it as well. The good news for our app, though, is that the browser compatibility issue really only pertains to CSS, and even then is relatively simple. Let’s add the following method to our ‘img_to_txt’ class and then we’ll talk a bit about it:

 # return browser-specific, CSS code for text formatting
 function get_css() {
 
  # first, properly detect user’s browser-type
  if ( strpos($_SERVER['HTTP_USER_AGENT'], ‘Gecko’) ) {
    if ( strpos($_SERVER['HTTP_USER_AGENT'], ‘Netscape’) )
     $browser = ‘NETSCAPE’;
    else if ( strpos($_SERVER['HTTP_USER_AGENT'], ‘Firefox’) )
     $browser = ‘FIREFOX’;
    else if ( strpos($_SERVER['HTTP_USER_AGENT'], ‘Safari’) )
     $browser = ‘SAFARI’;
    else
     $browser = ‘MOZILLA’;
  } else if ( strpos($_SERVER['HTTP_USER_AGENT'], ‘MSIE’) ) {
    if ( strpos($_SERVER['HTTP_USER_AGENT'], ‘Opera’) )
     $browser = ‘OPERA’;
    else
     $browser = ‘INTERNET_EXPLORER’;
  } else {
    $browser = ‘OTHER’;
  }
  
  # next, return appropriate browser-specific CSS code
  switch( $browser ) {
  
   case ‘MOZILLA’:
   case ‘FIREFOX’:
   case ‘NETSCAPE’:
    $line_height  = 7.5;
    $letter_spacing = -2.5;
    $font_size   = 13;
   break;
   
   case ‘OPERA’:
    $line_height  = 7.5;
    $letter_spacing = -1;
    $font_size   = 10;
   break;
   
   case ‘SAFARI’:
    $line_height  = 7.75;
    $letter_spacing = -2;
    $font_size   = 11;
   break;
   
   case ‘INTERNET_EXPLORER’:
   default:
    $line_height  = 8;
    $letter_spacing = -1.5;
    $font_size   = 11;
   break;
  
  } # END browser switch-case
  
  # return CSS
  return ‘style=”line-height: ‘ . ( $line_height * $this->aspect_ratio ) . ‘px; letter-spacing: ‘ . ( $letter_spacing* $this->aspect_ratio ) . ‘px; font-family: Courier New; font-size: ‘ . ( $font_size* $this->aspect_ratio ) . ‘px;”‘;’px;’;

 } # END get_css()

Our main concern here deals with our image’s aspect ratio. In other words, if we are converting an image that is 100 pixels wide by 50 pixels tall, we would like our resulting text image to be approximately those same dimensions. If we’re a little off it won’t make a big difference, but it would obviously not suit for our converted image to end up being 50 pixels wide by 100 pixels tall.

So how do we solve this? Simple, right? Wrong. Many of you may guess that by using a monospace font (such as Courier New) we would eliminate this issue. And you are partially correct. Using a monospace font will help greatly, as it will keep each of the text characters approximately the same width (an important fact to consider for our app). However, we still have to face the fact that most browsers will render a block of text that is 6 chars x 6 chars to be much taller than it is wide. This is due to a property known as “line-height” (or the amount of space between different, horizontal lines of text). For our purposes, many browsers will also render characters a bit too far apart from each other within lines. To correct this we can specify another display attribute, “letter-spacing”.

Unfortunately, this is not enough. Even on the same computer, each of the major browsers will render a block of text slightly differently. Our function, ‘get_css’, compensates for this by returning a string of CSS text that is appropriate for the browser being used to display our text-image. This means that our image should be roughly the same size (and aspect ratio) when viewed in each of the major web browsers.

(Note: for the purposes of this application, CSS has been given for Firefox, Netscape, IE, and Opera (Windows OS), as well as Safari and Firefox (Mac). If you wish to add additional browsers, however, you may do so easily, simply by modifying the ‘get_css’ method.)

What Have We Done So Far?

Admittedly, we haven’t started any actual conversion yet – but what we have done has been important just the same. So far we’ve set up our basic image conversion class, and defined its accessor methods. This is a good beginning, although a bit of a cliffhanger. Fortunately, it will allow us to jump right into the fun stuff when we pick up with part two of this lesson.

If you have any questions about the material we have covered so far, please feel free to contact me. Otherwise I hope to see you again in part two!

Google+ Comments

Google+ Comments