19 May 2010

Watermark Images with Text Using PHP and GD Library

Hi

This code snippet allows you to watermark uploaded images on the fly. The script saves the uploaded image in the specified location, the prints the specified message (for example a copyright notice) on the image and saves the watermarked image as a JPEG file in another location. You can use this script to copy-protect your images by permanently imprinting a copyright notice on the image. Though the example script covers most general needs, PHP programmers should customize the script for their needs.

The Complete Example

The working code sample consists of the following items:
  • HTML form that posts an image to the PHP script
  • The PHP Script

HTML Form
The HTML form needs a file upload element :<input type="file">. You must also specify the correct encoding type: enctype="multipart/form-data" for the form.

<form action="watermark-image.php" method="post" enctype="multipart/form-data">
 Select a file to upload for processing<br>
 <input type="file" name="File1"><br>
 <input type="submit" value="Submit File">
</form>

The PHP Script


<?php
 //----------------------------------------------------------------
 // render_text_on_gd_image
 // THE FUNCTION THAT ACTUALLY RENDERS THE STRING
 //----------------------------------------------------------------

 define( 'WATERMARK_MARGIN_ADJUST', 5 );
 define( 'WATERMARK_FONT_REALPATH', 'c:\\windows\\fonts\\' );

 function render_text_on_gd_image( &$source_gd_image, $text, $font, $size, $color, $opacity, $rotation, $align )
 {
  $source_width = imagesx( $source_gd_image );
  $source_height = imagesy( $source_gd_image );

  $bb = imagettfbbox_fixed( $size, $rotation, $font, $text );

  $x0 = min( $bb[ 0 ], $bb[ 2 ], $bb[ 4 ], $bb[ 6 ] ) - WATERMARK_MARGIN_ADJUST;
  $x1 = max( $bb[ 0 ], $bb[ 2 ], $bb[ 4 ], $bb[ 6 ] ) + WATERMARK_MARGIN_ADJUST;
  $y0 = min( $bb[ 1 ], $bb[ 3 ], $bb[ 5 ], $bb[ 7 ] ) - WATERMARK_MARGIN_ADJUST;
  $y1 = max( $bb[ 1 ], $bb[ 3 ], $bb[ 5 ], $bb[ 7 ] ) + WATERMARK_MARGIN_ADJUST;

  $bb_width = abs( $x1 - $x0 );
  $bb_height = abs( $y1 - $y0 );

  switch ( $align )
  {
   case 11:
    $bpy = -$y0;
    $bpx = -$x0;
    break;
   case 12:
    $bpy = -$y0;
    $bpx = $source_width / 2 - $bb_width / 2 - $x0;
    break;
   case 13:
    $bpy = -$y0;
    $bpx = $source_width - $x1;
    break;
   case 21:
    $bpy = $source_height / 2 - $bb_height / 2 - $y0;
    $bpx = -$x0;
    break;
   case 22:
    $bpy = $source_height / 2 - $bb_height / 2 - $y0;
    $bpx = $source_width / 2 - $bb_width / 2 - $x0;
    break;
   case 23:
    $bpy = $source_height / 2 - $bb_height / 2 - $y0;
    $bpx = $source_width - $x1;
    break;
   case 31:
    $bpy = $source_height - $y1;
    $bpx = -$x0;
    break;
   case 32:
    $bpy = $source_height - $y1;
    $bpx = $source_width / 2 - $bb_width / 2 - $x0;
    break;
   case 33;
    $bpy = $source_height - $y1;
    $bpx = $source_width - $x1;
    break;
  }

  $alpha_color = imagecolorallocatealpha(
   $source_gd_image,
   hexdec( substr( $color, 0, 2 ) ),
   hexdec( substr( $color, 2, 2 ) ),
   hexdec( substr( $color, 4, 2 ) ),
   127 * ( 100 - $opacity ) / 100
  );

  return imagettftext( $source_gd_image, $size, $rotation, $bpx, $bpy, $alpha_color, WATERMARK_FONT_REALPATH . $font, $text );
 }

 //----------------------------------------------------------------
 // imagettfbbox_fixed
 // FIX FOR THE BUGGY IMAGETTFBBOX IMPLEMENTATION IN GD LIBRARY
 //----------------------------------------------------------------

 function imagettfbbox_fixed( $size, $rotation, $font, $text )
 {
  $bb = imagettfbbox( $size, 0, WATERMARK_FONT_REALPATH . $font, $text );
  $aa = deg2rad( $rotation );
  $cc = cos( $aa );
  $ss = sin( $aa );
  $rr = array( );
  for( $i = 0; $i < 7; $i += 2 )
  {
   $rr[ $i + 0 ] = round( $bb[ $i + 0 ] * $cc + $bb[ $i + 1 ] * $ss );
   $rr[ $i + 1 ] = round( $bb[ $i + 1 ] * $cc - $bb[ $i + 0 ] * $ss );
  }
  return $rr;
 }

 //----------------------------------------------------------------
 // CREATE WATERMARK FUNCTION
 //----------------------------------------------------------------

 define( 'WATERMARK_OUTPUT_QUALITY', 90 );

 function create_watermark_from_string( $source_file_path, $output_file_path, $text, $font, $size, $color, $opacity, $rotation, $align )
 {
  list( $source_width, $source_height, $source_type ) = getimagesize( $source_file_path );

  if ( $source_type === NULL )
  {
   return false;
  }

  switch ( $source_type )
  {
   case IMAGETYPE_GIF:
    $source_gd_image = imagecreatefromgif( $source_file_path );
    break;
   case IMAGETYPE_JPEG:
    $source_gd_image = imagecreatefromjpeg( $source_file_path );
    break;
   case IMAGETYPE_PNG:
    $source_gd_image = imagecreatefrompng( $source_file_path );
    break;
   default:
    return false;
  }

  render_text_on_gd_image( $source_gd_image, $text, $font, $size, $color, $opacity, $rotation, $align );

  imagejpeg( $source_gd_image, $output_file_path, WATERMARK_OUTPUT_QUALITY );
  imagedestroy( $source_gd_image );
 }

 //----------------------------------------------------------------
 // FILE PROCESSING FUNCTION
 //----------------------------------------------------------------

 define( 'UPLOADED_IMAGE_DESTINATION', 'originals/' );
 define( 'PROCESSED_IMAGE_DESTINATION', 'images/' );

 function process_image_upload( $Field )
 {
  $temp_file_path = $_FILES[ $Field ][ 'tmp_name' ];
  $temp_file_name = $_FILES[ $Field ][ 'name' ];
  
  list( , , $temp_type ) = getimagesize( $temp_file_path );
  
  if ( $temp_type === NULL )
  {
   return false;
  }
  
  switch ( $temp_type )
  {
   case IMAGETYPE_GIF:
    break;
   case IMAGETYPE_JPEG:
    break;
   case IMAGETYPE_PNG:
    break;
   default:
    return false;
  }
  
  $uploaded_file_path = UPLOADED_IMAGE_DESTINATION . $temp_file_name;
  $processed_file_path = PROCESSED_IMAGE_DESTINATION . preg_replace( '/\\.[^\\.]+$/', '.jpg', $temp_file_name );
  
  move_uploaded_file( $temp_file_path, $uploaded_file_path );

  //----------------------------------------------------------------
  // PARAMETER DESCRIPTION
  // (1) SOURCE FILE PATH
  // (2) OUTPUT FILE PATH
  // (3) THE TEXT TO RENDER
  // (4) FONT NAME -- MUST BE A *FILE* NAME
  // (5) FONT SIZE IN POINTS
  // (6) FONT COLOR AS A HEX STRING
  // (7) OPACITY -- 0 TO 100
  // (8) TEXT ANGLE -- 0 TO 360
  // (9) TEXT ALIGNMENT CODE -- POSSIBLE VALUES ARE 11, 12, 13, 21, 22, 23, 31, 32, 33
  //----------------------------------------------------------------

  $result = create_watermark_from_string(
   $uploaded_file_path,
   $processed_file_path,
   'Copyrights (c) 2010',
   'arial.ttf',
   14,
   'CCCCCC',
   75,
   0,
   32
  );
  
  if ( $result === false )
  {
   return false;
  }
  else
  {
   return array( $uploaded_file_path, $processed_file_path );
  }
 }

 //----------------------------------------------------------------
 // END OF FUNCTIONS
 //----------------------------------------------------------------

 $result = process_image_upload( 'File1' );

 if ( $result === false )
 {
  echo '<br>An error occurred during file processing.';
 }
 else
 {
  echo '<br>Original image saved as <a href="' . $result[ 0 ] . '" target="_blank">' . $result[ 0 ] . '</a>';
  echo '<br>Watermarked image saved as <a href="' . $result[ 1 ] . '" target="_blank">' . $result[ 1 ] . '</a>';
 }
?>

How it Works

The example is divided into many functions. But there is only one that needs explanation:
render_text_on_gd_image
Important: Before using this function, modify the constant WATERMARK_FONT_REALPATH to point to a directory that contains fonts in TTF format. On Windows platform, this is usually the C:\Windows\Fonts\ directory. It is also possible to use TTF fonts from other locations, or you can copy required fonts with the script.
The primary function used in this example is the imagettftext function. The function renders the given text into the image using true-type fonts. The important parameters of this function include:
  • font size -- usually specified in "points"
  • text rotation -- specified in degrees
  • color -- may or may not contain transparency
  • a valid true-type font file path/name
While this looks easy, it becomes very complicated when you need rotated text. Adding to the complication, the function accepts the (x, y) coordinates of the text's basepoint and baseline, not the absolute lower left corner of the bounding box of the text.
The function therefore has to calculate the bounding box of the text that needs to be rendered, taking the angle into consideration. A modified version of the imagettfbbox does exactly that.
Note: This function was extracted from user contributed notes in the PHP manual.
Next, the placement of the bounding box on the source image is calculated and immediately the coordinates are converted to the x and y values that the imagettfbbox expects.
Finally, the imagettfbbox function is called to render the text on the source image.

Picture before water marking......................................

.

 Picture after water marking...........................................



You can play around with the parameters to the function render_text_on_gd_image to get different variations of watermarks. In particular, this example allows you to generate watermarks with various combinations of alignment, color, opacity, angle, font and font size.






No comments:

Post a Comment