You are here

textimage.module in Textimage 7.2

Provides text to image manipulations.

File

textimage.module
View source
<?php

/**
 * @file
 * Provides text to image manipulations.
 */

/**
 * Include additional files.
 */
foreach (module_list() as $module) {
  if (file_exists($file = dirname(__FILE__) . "/includes/{$module}.inc")) {
    require_once $file;
  }
}

/**
 * Matches all 'P' Unicode character classes (punctuation).
 */
if (!defined('PREG_CLASS_PUNCTUATION')) {
  define('PREG_CLASS_PUNCTUATION', '\\x{21}-\\x{23}\\x{25}-\\x{2a}\\x{2c}-\\x{2f}\\x{3a}\\x{3b}\\x{3f}\\x{40}\\x{5b}-\\x{5d}' . '\\x{5f}\\x{7b}\\x{7d}\\x{a1}\\x{ab}\\x{b7}\\x{bb}\\x{bf}\\x{37e}\\x{387}\\x{55a}-\\x{55f}' . '\\x{589}\\x{58a}\\x{5be}\\x{5c0}\\x{5c3}\\x{5f3}\\x{5f4}\\x{60c}\\x{60d}\\x{61b}\\x{61f}' . '\\x{66a}-\\x{66d}\\x{6d4}\\x{700}-\\x{70d}\\x{964}\\x{965}\\x{970}\\x{df4}\\x{e4f}' . '\\x{e5a}\\x{e5b}\\x{f04}-\\x{f12}\\x{f3a}-\\x{f3d}\\x{f85}\\x{104a}-\\x{104f}\\x{10fb}' . '\\x{1361}-\\x{1368}\\x{166d}\\x{166e}\\x{169b}\\x{169c}\\x{16eb}-\\x{16ed}\\x{1735}' . '\\x{1736}\\x{17d4}-\\x{17d6}\\x{17d8}-\\x{17da}\\x{1800}-\\x{180a}\\x{1944}\\x{1945}' . '\\x{2010}-\\x{2027}\\x{2030}-\\x{2043}\\x{2045}-\\x{2051}\\x{2053}\\x{2054}\\x{2057}' . '\\x{207d}\\x{207e}\\x{208d}\\x{208e}\\x{2329}\\x{232a}\\x{23b4}-\\x{23b6}\\x{2768}-' . '\\x{2775}\\x{27e6}-\\x{27eb}\\x{2983}-\\x{2998}\\x{29d8}-\\x{29db}\\x{29fc}\\x{29fd}' . '\\x{3001}-\\x{3003}\\x{3008}-\\x{3011}\\x{3014}-\\x{301f}\\x{3030}\\x{303d}\\x{30a0}' . '\\x{30fb}\\x{fd3e}\\x{fd3f}\\x{fe30}-\\x{fe52}\\x{fe54}-\\x{fe61}\\x{fe63}\\x{fe68}' . '\\x{fe6a}\\x{fe6b}\\x{ff01}-\\x{ff03}\\x{ff05}-\\x{ff0a}\\x{ff0c}-\\x{ff0f}\\x{ff1a}' . '\\x{ff1b}\\x{ff1f}\\x{ff20}\\x{ff3b}-\\x{ff3d}\\x{ff3f}\\x{ff5b}\\x{ff5d}\\x{ff5f}-' . '\\x{ff65}');
}

/**
 * Matches all 'Z' Unicode character classes (separators).
 */
define('PREG_CLASS_SEPARATOR', '\\x{20}\\x{a0}\\x{1680}\\x{180e}\\x{2000}-\\x{200a}\\x{2028}\\x{2029}\\x{202f}\\x{205f}\\x{3000}');
define('ALIGN_LEFT', 1);
define('ALIGN_CENTER', 2);
define('ALIGN_RIGHT', 3);

/**
 * Implements hook_help().
 */
function textimage_help($path, $arg) {
  $output = "";
  switch ($path) {
    case 'admin/help#textimage':
      $output = '
        <h2 id="textimage-introduction">The dynamic text to image generator!</h2>
        <p>Textimage adds text to image functionality using GD2 and Freetype, enabling users to create crisp images on the fly for use as theme objects, headings or limitless other possibilities.</p>

        <h2>Installing Fonts</h2>
        <p>Before you can begin using Textimage you must upload at least one TrueType or OpenType font to the server and tell Textimage where you uploaded it. Fonts must have a .tff or .otf extension to be seen by Textimage. If you do not have any TrueType or OpenType fonts, you can download some free GNU fonts from the <a href="http://savannah.nongnu.org/projects/freefont/">Free UCS Outline Fonts Project</a>. Once the fonts are uploaded, enter the UNIX-style path the fonts on the <a href="!config">configuration page</a>.</p>

        <h2 id="textimage-configuration">Configuration</h2>
        <p>The basis of Textimage is made of configuration options called <em>presets</em>. A preset defines what font, size, color, etc. should be used in the generated image. You can create new presets on the <a href="!presets">presets page</a>. Most options are pretty self explanatory, but background images can get pretty complicated if you begin to use other presets as backgrounds. Let\'s run through an example.</p>
        <p>If you specified a backgrounds directory on the main configuration page, a list of backgrounds is automatically popuplated into the Background Image select list. Let\'s say there\'s a image called &quot;header.png&quot; in the image list that looks like this:</p>
        <p><img src="!example1" alt="example1" /></p>
        <p>Now we\'ll create a preset called &quot;preset1&quot;. In this preset, set the font to Braggadocio (not included), 54px, #FFFFFF (white) color. Select header.png from the background list and position the text at x-offset 14 and y-offset 22 (in pixels). After the preset is saved, Textimage is now ready to automatically generate images based on text strings. You could directly visit the image at {files}/textimage/preset1/Hello.png (where {files} is your sites file directory) and get the following result:</p>
            <p><img src="!example2" alt="example2" /></p>
        <p>To get crazy now, create a new preset called &quot;preset2&quot;. In this preset, set the font to Century (also not included), 20px, #000000 (black) color. Select <em>preset1</em> from the background list and position the text at x-offset 14 and y-offset 94. Save the preset then visit {files}/textimage/preset2/Hello/world!.png and get the following result:</p>
            <p><img src="!example3" alt="example3" /></p>
            <p>The entire preset1 is generated using the first argument <em>Hello</em>. Then preset2 is generated using the name of the file <em>world!.png</em>. You could continue chaining presets together over and over again.</p>

        <h2>File Names and Storage</h2>
        <p>Textimage supports .png, .gif, and .jpg input and output files. You can change the output format of the image simply by changing the extension of the last file. In the above example we made PNG images. If we had appended it with .jpg, a JPG image would have been created. Only PNG and GIF files support transparent backgrounds.</p>
          ';
      $output = t($output, array(
        '!config' => url('admin/config/media/textimage/settings'),
        '!presets' => url('admin/config/media/textimage/presets'),
        '!example1' => base_path() . drupal_get_path('module', 'textimage') . '/misc/example1.png',
        '!example2' => base_path() . drupal_get_path('module', 'textimage') . '/misc/example2.png',
        '!example3' => base_path() . drupal_get_path('module', 'textimage') . '/misc/example3.png',
      ));
      break;
    case 'admin/config/media/modules#description':
    case 'admin/config/textimage':
    case 'admin/config/media/modules/textimage':
      $output = t('Provides text to image manipulations.');
      break;
  }
  return $output;
}

/**
 * Implements hook_theme().
 */
function textimage_theme() {
  $theme = array(
    'textimage_image' => array(
      'variables' => array(
        'preset' => '',
        'text' => '',
        'additional_text' => array(),
        'format' => 'png',
        'file_path' => '',
        'alt' => '',
        'title' => '',
        'attributes' => array(),
        'getsize' => TRUE,
        'image' => TRUE,
      ),
    ),
    'textimage_preset_edit' => array(
      'variables' => array(
        'form' => array(),
      ),
    ),
  );
  foreach (textimage_get_presets() as $preset) {
    $theme['textimage_formatter_textimage_' . $preset->name] = array(
      'variables' => array(
        // Selected preset name.
        'preset' => $preset->name,
        // Selected preset ID.
        'pid' => $preset->pid,
        // Image object.
        'image' => new stdClass(),
        // Text for new creation of textimage.
        'text' => NULL,
        // Additional text for new creation of textimage.
        'additional_text' => array(),
      ),
      'function' => 'textimage_preset_formatter',
    );
  }
  return $theme;
}

/**
 * Implements hook_menu().
 */
function textimage_menu() {
  $items = array();
  $items[file_build_uri('/textimage')] = array(
    'page callback' => 'textimage_image',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/config/media/textimage'] = array(
    'title' => 'Textimage',
    'description' => 'Configure text to image preset functions.',
    'page callback' => 'textimage_preset_list',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'textimage.admin.inc',
  );
  $items['admin/config/media/textimage/preset'] = array(
    'title' => 'Presets',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/config/media/textimage/preset/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/config/media/textimage/preset/new'] = array(
    'title' => 'New',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'textimage_preset_edit',
      'new',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'weight' => 1,
    'type' => MENU_LOCAL_TASK,
    'file' => 'textimage.admin.inc',
  );
  $items['admin/config/media/textimage/preset/%/edit'] = array(
    'title' => 'Edit Preset',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'textimage_preset_edit',
      'edit',
      5,
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_VISIBLE_IN_BREADCRUMB,
    'file' => 'textimage.admin.inc',
  );
  $items['admin/config/media/textimage/preset/%/delete'] = array(
    'title' => 'Delete Preset',
    'load arguments' => array(
      5,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'textimage_preset_delete_confirm',
      5,
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_VISIBLE_IN_BREADCRUMB,
    'file' => 'textimage.admin.inc',
  );
  $items['admin/config/media/textimage/preset/%/flush'] = array(
    'title' => 'Flush Preset Cache',
    'load arguments' => array(
      5,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'textimage_preset_flush_confirm',
      5,
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_VISIBLE_IN_BREADCRUMB,
    'file' => 'textimage.admin.inc',
  );
  $items['admin/config/media/textimage/settings'] = array(
    'title' => 'Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'textimage_settings_form',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'weight' => 0,
    'type' => MENU_LOCAL_TASK,
    'file' => 'textimage.admin.inc',
  );

  // Generate image derivatives of publicly available files, via
  // URL request path. For local default filesystem only.
  $stream_wrapper = file_stream_wrapper_get_instance_by_scheme(variable_get('file_default_scheme', 'public'));
  if ($stream_wrapper instanceof DrupalLocalStreamWrapper) {
    $directory_path = $stream_wrapper
      ->getDirectoryPath();
    $directory_count = count(explode('/', $directory_path));
    $items[$directory_path . '/textimage'] = array(
      'title' => 'Generate textimage',
      'page callback' => 'textimage_deliver',
      'page arguments' => array(
        $directory_count + 1,
        $directory_count + 2,
      ),
      'access arguments' => array(
        'create textimages',
      ),
      'type' => MENU_CALLBACK,
    );
  }
  return $items;
}

/**
 * Implements hook_permission().
 */
function textimage_permission() {
  return array(
    'create textimages' => array(
      'title' => t('Create Textimages'),
      'description' => t('Create Textimages'),
    ),
  );
}

/**
 * Menu Callback function.
 *
 * Converts the current textimage path into an image. On
 * first request, the image is returned to the browser from Drupal and then
 * saved to the disk. On subsequent requests (with Clean URLs enabled), the
 * cached image is loaded directly.
 *
 * This function takes a dynamic number of arguments.
 *
 * +@param $preset_name
 *   The name of the preset to be used in this textimage
 * +@param ...
 *   An unlimited number of additional text parameters to be used as the
 *   display text for textimages displayed on top of one another. Only used
 *   if the current preset has the its Background Image option set to the
 *   result of another preset. Text is used in reverse order. So the last
 *   directory will be the first chained preset used.
 * +@param $text
 *   The text to be displayed in this preset with the output format
 *   appended as the file extension. For example, 'sample.png' will output a
 *   PNG with the text 'sample'. 'sample.jpg' will output the same image but
 *   in JPG format.
 */
function textimage_image() {
  $pattern = '/' . str_replace('/', '\\/', base_path() . '(\\?q=)?' . variable_get('file_default_scheme', 'public') . '://textimage/') . '/';
  $args = explode('/', preg_replace($pattern, '', request_uri()));
  $preset = array_shift($args);
  if (is_numeric($preset)) {
    drupal_not_found();
    exit;
  }
  $filename = urldecode(array_pop($args));
  $additional_text = $args;

  // Determine our output format.
  preg_match('/\\.([a-z]+)$/i', $filename, $matches);
  $format = $matches[1];
  if ($format == 'jpg') {
    $format = 'jpeg';
  }

  // Determine the text to display.
  $text = preg_replace('/\\.([a-z]+)$/i', '', $filename);
  if (!($img = textimage_build_image('url', $preset, $text, $additional_text, $format))) {
    return FALSE;
  }
  drupal_goto($img);
}

/**
 * Todo.
 */
function textimage_build_image($method, $preset, $text, $additional_text = array(), $format = 'png', $file_uri = NULL) {

  // Integrety check.
  $output_function = 'image' . $format;
  if (!function_exists($output_function)) {
    $message = t('Unable to generate Textimage as the file extension is unsupported on this system. Files must have a .png, .jpg, or .gif extension.');
    watchdog('textimage', $message, array(), WATCHDOG_ERROR);
    drupal_set_message($message, 'error');
    return FALSE;
  }
  if (is_numeric($preset)) {
    $preset = _textimage_preset_load($preset);
  }
  elseif (!is_array($preset)) {

    // This will use value from static variable
    // and will possibly avoid database query.
    $presets = textimage_get_presets();
    $preset = (array) $presets[$preset];
  }

  // Load preset.
  if (!is_array($preset) && !$preset) {
    $message = t('Unable to generate Textimage as the preset %preset is not defined.', array(
      '%preset' => $preset,
    ));
    watchdog('textimage', $message, array(), WATCHDOG_ERROR);
    drupal_set_message($message, 'error');
    return FALSE;
  }
  $directory = variable_get('file_default_scheme', 'public') . '://textimage/' . $preset['pid'] . '/';
  if (empty($file_uri)) {
    $result = db_query("SELECT file FROM {textimage_image} WHERE pid = :pid AND data = :data", array(
      ':pid' => $preset['pid'],
      ':data' => serialize(array(
        'format' => $format,
        'text' => $text,
        'additional_text' => $additional_text,
      )),
    ))
      ->fetchObject();
  }
  else {
    $filename = explode('/', $file_uri);
    $filename = $filename[count($filename) - 1];
    $file_path = "{$directory}{$filename}.{$format}";
    $result = (object) array(
      'file' => $file_path,
    );
  }

  // CHECK IF FILE EXIST.
  $file_missing = FALSE;
  if (empty($result) || !empty($result) && !is_file(drupal_realpath($result->file))) {
    $file_missing = TRUE;
  }
  if (!$result && (user_access('create textimages') || $method == 'theme') || $preset['pid'] == 0 || $file_missing) {

    // Generate the image resource.
    $img = textimage_image_from_preset($preset, $text, $additional_text);
    if (empty($file_uri)) {
      $filename = check_plain(str_replace('/', '', $text));
      if (empty($filename)) {
        $filename = REQUEST_TIME . rand(1000, 9999) . '.' . $format;
      }

      // Save the result so we don't have to recreate.
      $file_path = $directory . $filename;
      $file_path = textimage_save_image($img, $file_path, $format);
    }
    else {
      $file_uri = textimage_save_image($img, $file_uri, $format);
      $path = file_stream_wrapper_uri_normalize($directory);
      file_prepare_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
      if ("{$path}/{$filename}.{$format}" != $file_uri) {
        file_unmanaged_copy($file_uri, $file_path, FILE_EXISTS_REPLACE);
      }
    }
    if ($file_missing && $preset['pid'] !== 0) {
      try {
        db_insert('textimage_image')
          ->fields(array(
          'pid' => $preset['pid'],
          'file' => $file_path,
          'data' => serialize(array(
            'format' => $format,
            'text' => $text,
            'additional_text' => $additional_text,
          )),
        ))
          ->execute();
      } catch (Exception $error) {
        db_update('textimage_image')
          ->fields(array(
          'data' => serialize(array(
            'format' => $format,
            'text' => $text,
            'additional_text' => $additional_text,
          )),
        ))
          ->condition('pid', $preset['pid'])
          ->condition('file', $file_path)
          ->execute();
      }
    }
    return $file_path;
  }
  elseif (!$result) {
    drupal_access_denied();
    return FALSE;
  }
  else {
    return $result->file;
  }
}

/**
 * Todo.
 */
function textimage_build_preview($variables) {
  $image = textimage_image_from_preset($variables['preset'], $variables['text'], $variables['additional_text']);
  $file_path = variable_get('file_default_scheme', 'public') . '://textimage/preview';
  return textimage_save_image($image, $file_path, 'png');
}

/**
 * Todo.
 */
function textimage_save_image($image, $file_path, $format) {
  global $user;
  $file_path = "{$file_path}.{$format}";
  textimage_directory_check($file_path);
  $file = file_save_data('', $file_path, FILE_EXISTS_REPLACE);
  $wrapper = file_stream_wrapper_get_instance_by_uri($file->uri);
  $image_function = "image{$format}";
  if (function_exists($image_function)) {
    $image_function($image, $wrapper
      ->realpath());
    imagedestroy($image);
    clearstatcache();
  }
  $file->uid = $user->uid;
  $file->filemime = "image/{$format}";
  $file = file_save($file);
  return $file_path;
}

/**
 * Loads the Textimage preset and generates the GD image resource.
 *
 * @param array $preset
 *   The preset to be used in this textimage
 * @param string $text
 *   The text to be displayed in this preset
 * @param array $additional_text
 *   An array of text to be used in subsequent textimages. Only used if this
 *   preset uses the result of another preset as its background image.
 */
function textimage_image_from_preset($preset, $text, $additional_text = array()) {
  module_load_include('inc', 'textimage', 'textimage.utils');
  foreach ($preset['settings'] as $key => $settings) {
    if (is_array($settings)) {
      foreach ($settings as $name => $value) {
        ${$key . '_' . $name} = $value;
      }
    }
  }
  $text_fixed_width = isset($text_fixed_width) ? $text_fixed_width : 0;

  // Set font path.
  $font_file = variable_get('textimage_fonts_path', drupal_get_path('module', 'textimage') . '/fonts') . '/' . $font_file;

  // Convert text case.
  switch ($text_case) {
    case 'upper':
      $text = drupal_strtoupper($text);
      break;
    case 'lower':
      $text = drupal_strtolower($text);
      break;
    case 'ucfirst':
      $text = drupal_ucfirst($text);
      break;
    case 'ucwords':
      $text = preg_replace_callback('/(\\w+)/', '_textimage_ucwords', $text);
      break;
  }

  // Generate the textimage.
  $img = textimage_text_to_image($text, $font_size, $font_file, $font_color, $text_angle, $text_maximum_width, $text_fixed_width, $text_align);

  // Add a border.
  if ($text_stroke['width'] && $text_stroke['color']) {
    $img = textimage_image_add_stroke($img, $text_stroke['width'], $text_stroke['color'], $font_color['opacity']);
  }

  // Add margin.
  if ($text_margin['top'] || $text_margin['right'] || $text_margin['bottom'] || $text_margin['left']) {
    $img = textimage_image_add_margin($img, $text_margin);
  }

  // Place result on top of another preset's result.
  if (is_numeric($background_image) && ($background_image = _textimage_preset_load($background_image))) {
    $next_preset = $background_image;
    $next_text = array_pop($additional_text);
    if (empty($next_text)) {
      $next_text = $text;
    }
    $background_resource = textimage_image_from_preset($next_preset, $next_text, $additional_text);
    $text_width = imagesx($img);
    $text_height = imagesy($img);
    imagealphablending($background_resource, TRUE);
    imagecopy($background_resource, $img, $background_xoffset, $background_yoffset, 0, 0, $text_width, $text_height);
    imagesavealpha($background_resource, TRUE);
    $img = $background_resource;
  }
  elseif (is_file($background_image)) {

    // Place result on background image if available.
    $info = image_get_info($background_image);
    $imagecreate = 'imagecreatefrom' . $info['extension'];
    if (function_exists($imagecreate)) {
      $background_resource = $imagecreate($background_image);
      $text_width = imagesx($img);
      $text_height = imagesy($img);
      imagealphablending($background_resource, TRUE);
      imagecopy($background_resource, $img, $background_xoffset, $background_yoffset, 0, 0, $text_width, $text_height);
      imagesavealpha($background_resource, TRUE);
      $img = $background_resource;
    }
  }
  else {
    $background_resource = imagecreatetruecolor(imagesx($img), imagesy($img));
    $alpha = 0;
    if (!$background_color) {
      $alpha = 127;
      $background_color = '#FFFFFF';
    }
    list($r, $g, $b) = _textimage_hex2rgb($background_color);
    $back = imagecolorallocatealpha($background_resource, $r, $g, $b, $alpha);
    imagefill($background_resource, 0, 0, $back);
    imagealphablending($background_resource, TRUE);
    imagecopy($background_resource, $img, 0, 0, 0, 0, imagesx($img), imagesy($img));
    imagesavealpha($background_resource, TRUE);
    $img = $background_resource;
  }
  return $img;
}

/**
 * Callback for preg_replace_callback.
 */
function _textimage_ucwords($matches) {
  return drupal_ucfirst($matches[1]);
}

/**
 * Stroke function.
 *
 * Adds a solid color stroke around an image with a transparent
 * background.
 *
 * @param resource $img
 *   The gd image resource of the image to modify
 * @param int $thickness
 *   The width of the stroke to apply
 * @param hex $color
 *   The color of the stroke to apply
 *
 * @todo Add $position parameter to allow the stroke to be applied 'inside',
 * 'middle', or 'outside'. outside is the only current behavior.
 */
function textimage_image_add_stroke($img, $thickness, $color, $text_opacity) {
  if ($thickness > 0) {
    $width = imagesx($img);
    $height = imagesy($img);

    // Create a new image which we'll lay over the original.
    $border_img = imagecreatetruecolor($width, $height);
    $back = imagecolorallocatealpha($border_img, 0, 0, 0, 127);
    imagefill($border_img, 0, 0, $back);
    for ($x = 0; $x < $width; $x++) {
      for ($y = 0; $y < $height; $y++) {
        $c = imagecolorsforindex($img, imagecolorat($img, $x, $y));

        // Outside only modify pixels which are less opaque than the text
        // opacity.
        if ($c['alpha'] > -($text_opacity - 100) / 100 * 127) {
          textimage_image_stroke_change_pixels($img, $border_img, $thickness, $color, $x, $y, $width, $height);
        }
      }
    }

    // Merge the images.
    imagealphablending($img, TRUE);
    imagecopy($img, $border_img, 0, 0, 0, 0, $width, $height);
  }
  return $img;
}

/**
 * Create the image directory relative to the 'files' dir.
 *
 * (if user specified one)
 * Won't allow form submit unless the directory exists & is writable
 *
 * @param string $directory_path
 *   String containing the path of the directory to check.
 */
function textimage_directory_check($directory_path) {

  // Create each directory necessary if it doesn't exist.
  $directory_path = drupal_dirname($directory_path);
  if (!file_prepare_directory($directory_path, FILE_CREATE_DIRECTORY)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Load a preset by id or name.
 *
 * @param mixed $preset
 *   preset id or name.
 */
function _textimage_preset_load($preset) {
  if (is_numeric($preset)) {

    // Load preset by id.
    $preset = db_query('SELECT * FROM {textimage_preset} WHERE pid = :pid', array(
      ':pid' => $preset,
    ))
      ->fetchAssoc();
  }
  else {

    // Load preset by name.
    $preset = db_query("SELECT * FROM {textimage_preset} WHERE name = :name", array(
      ':name' => $preset,
    ))
      ->fetchAssoc();
  }
  if (empty($preset)) {
    return FALSE;
  }
  else {
    $preset['settings'] = unserialize($preset['settings']);
    return $preset;
  }
}

/**
 * Implements hook_field_formatter_info().
 */
function textimage_field_formatter_info() {
  $formatters = array();
  foreach (textimage_get_presets() as $preset) {
    $formatters['textimage_' . $preset->name] = array(
      'label' => t('Textimage:') . ' ' . $preset->name,
      'field types' => array(
        'text',
        'email',
      ),
    );
  }
  return $formatters;
}

/**
 * Implements hook_field_formatter_view().
 */
function textimage_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  if ('textimage' !== drupal_substr($display['type'], 0, 9)) {
    return array();
  }
  $element = array();
  foreach ($items as $delta => $item) {
    $variables = array();
    $variables['preset'] = drupal_substr($display['type'], 10);
    $variables['text'] = $item['value'];
    $variables['additional_text'] = array();
    $variables['format'] = 'png';
    $variables['file_path'] = '';
    $variables['alt'] = htmlentities($item['value']);
    $variables['title'] = htmlentities($item['value']);
    $variables['image'] = TRUE;
    $element[$delta]['#markup'] = theme('textimage_image', $variables);
  }
  return $element;
}

/**
 * Todo.
 */
function theme_textimage_formatter($element) {
  $alt = $title = $element['#item']['safe'];
  if (isset($element['#item']['email'])) {
    $alt = $title = '';
  }
  return theme('textimage_image', drupal_substr($element['#formatter'], 10), $element['#item']['safe'], array(), 'png', $alt, $title);
}

/**
 * Todo.
 */
function textimage_deliver($pid, $filename, $display_img = TRUE) {
  $stream_wrapper = file_stream_wrapper_get_instance_by_scheme(variable_get('file_default_scheme', 'public'));
  if ($stream_wrapper instanceof DrupalLocalStreamWrapper) {
    $directory_path = $stream_wrapper
      ->getDirectoryPath();
  }
  else {
    drupal_not_found();
  }
  $public_file_path = base_path() . $directory_path . '/textimage/';
  $pattern = '/' . str_replace('/', '\\/', $public_file_path) . '/';
  $trail = urldecode(preg_replace($pattern, '', request_uri()));
  $args = explode('/', $trail);
  $preset = array_shift($args);
  $filename = array_pop($args);
  $additional_text = $args;

  // Determine our output format.
  $offset = strrpos($filename, '.');
  if ($offset) {
    $format = substr($filename, $offset + 1);
  }
  else {
    $format = 'png';
  }
  if ($format == 'jpg') {
    $format = 'jpeg';
  }
  $filemime = 'image/' . $format;

  // Determine the text to display.
  $text = preg_replace('/\\.([a-z]+)$/i', '', $filename);
  $img_uri = textimage_preset_formatter(array(
    'method' => 'url',
    'pid' => $pid,
    'preset' => $pid,
    'text' => $text,
    'additional_text' => $additional_text,
    'image' => array(
      'uri' => variable_get('file_default_scheme', 'public') . "://textimage/{$pid}/{$filename}",
      'filename' => $filename,
      'filemime' => $filemime,
    ),
  ));
  if (!$img_uri) {
    drupal_not_found();
  }
  if ($display_img) {
    $img_info = image_get_info(drupal_realpath($img_uri));
    file_transfer($img_uri, array(
      'Content-Type' => $filemime,
    ));
  }
}

/**
 * Default theme callback for all preset.
 */
function textimage_preset_formatter($variables) {
  $method = isset($variables['method']) ? $variables['method'] : 'theme';
  $image = (array) $variables['image'];
  if (!empty($image)) {
    $path = variable_get('file_default_scheme', 'public') . '://textimage/' . $variables['pid'] . '/' . $image['filename'];
    if (is_file(drupal_realpath($path))) {
      return $path;
    }
    else {

      // Image was flushed, recreate the textimage from its data.
      $result = db_query('SELECT pid, data FROM {textimage_image} WHERE file LIKE :file', array(
        ':file' => $path,
      ))
        ->fetchObject();
      if (!empty($result)) {
        $format = str_replace('image/', '', $image['filemime']);
        $uri = preg_replace('/(.' . $format . '$)/', '', $image['uri']);
        $data = unserialize($result->data);
        return textimage_build_image($method, $variables['preset'], $data['text'], $data['additional_text'], $format, $uri);
      }
      else {

        // Try to replace the cache of previously
        // assigned preset for this image.
        $result = db_query('SELECT pid, data, file FROM {textimage_image} WHERE file LIKE :file', array(
          ':file' => '%/' . $image['filename'],
        ))
          ->fetchObject();
        if (!empty($result)) {
          $format = str_replace('image/', '', $image['filemime']);
          $uri = preg_replace('/(.' . $format . '$)/', '', $image['uri']);
          file_unmanaged_delete($result->file);
          db_delete('textimage_image')
            ->condition('pid', $result->pid)
            ->condition('file', $result->file)
            ->condition('data', $result->data)
            ->execute();
          $data = unserialize($result->data);
          return textimage_build_image($method, $variables['preset'], $data['text'], $data['additional_text'], $format, $uri);
        }
        elseif (!empty($variables['text'])) {
          $format = str_replace('image/', '', $image['filemime']);
          $uri = preg_replace('/(.' . $format . '$)/', '', $image['uri']);

          // Try to create new textimage from defined 'text'
          return textimage_build_image($method, $variables['preset'], $variables['text'], !empty($variables['additional_text']) ? $variables['additional_text'] : array(), $format, $uri);
        }
      }
    }
  }
  $message = t('Cached Textimage image does not exist or has been flushed.');
  watchdog('textimage', $message, array(), WATCHDOG_ERROR);
  return '';
}

/**
 * Theme function for displaying textimages.
 */
function theme_textimage_image($variables) {
  if (!($path = textimage_build_image('theme', $variables['preset'], $variables['text'], $variables['additional_text'], $variables['format'], $variables['file_path']))) {
    return FALSE;
  }
  if (isset($variables['image'])) {
    return theme('image', array(
      'path' => $path,
      'alt' => $variables['alt'],
      'title' => $variables['title'],
      'attributes' => $variables['attributes'],
    ));
  }
  return $path;
}

/**
 * Textimage preview AJAX functionality.
 */
function textimage_js_preview($form, $form_state) {
  $preview['settings'] = $form_state['values']['settings'];
  $preview['name'] = $form_state['values']['name'];
  $preview['description'] = $form_state['values']['description'];
  $preview['pid'] = 0;
  $preview_text = isset($preview['settings']['preview']['text']['default']) ? $preview['settings']['preview']['text']['default'] : t('Preview');
  $additional_text = array();
  if (isset($preset['settings']['preview']['text']['additional'])) {
    $additional_text = $preset['settings']['preview']['text']['additional'];
    rsort($additional_text);
  }
  $preview = textimage_build_preview(array(
    'preset' => $preview,
    'text' => $preview_text,
    'additional_text' => $additional_text,
  ));
  $commands = array();
  $commands[] = ajax_command_html('#textimage-preview', '<img src="' . file_create_url($preview) . '?' . time() . '">');
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Todo.
 */
function textimage_get_presets() {
  $presets =& drupal_static(__FUNCTION__);
  if (!isset($presets)) {
    if (module_exists('ctools')) {
      module_load_include('inc', 'ctools', 'includes/export');
      foreach (ctools_export_load_object('textimage_preset') as $preset) {
        if (isset($preset->in_code_only) && $preset->in_code_only) {
          module_load_include('inc', 'textimage', 'textimage.admin');
          _textimage_preset_create($preset->name, $preset->description, $preset->settings);
        }
      }
    }
    unset($preset);
    $result = db_query('SELECT pid, name, description, settings FROM {textimage_preset} ORDER BY name');
    $presets = array();
    foreach ($result as $preset) {
      $preset->settings = unserialize($preset->settings);
      $presets[$preset->name] = $preset;
    }
  }
  return $presets;
}

Functions

Namesort descending Description
textimage_build_image Todo.
textimage_build_preview Todo.
textimage_deliver Todo.
textimage_directory_check Create the image directory relative to the 'files' dir.
textimage_field_formatter_info Implements hook_field_formatter_info().
textimage_field_formatter_view Implements hook_field_formatter_view().
textimage_get_presets Todo.
textimage_help Implements hook_help().
textimage_image Menu Callback function.
textimage_image_add_stroke Stroke function.
textimage_image_from_preset Loads the Textimage preset and generates the GD image resource.
textimage_js_preview Textimage preview AJAX functionality.
textimage_menu Implements hook_menu().
textimage_permission Implements hook_permission().
textimage_preset_formatter Default theme callback for all preset.
textimage_save_image Todo.
textimage_theme Implements hook_theme().
theme_textimage_formatter Todo.
theme_textimage_image Theme function for displaying textimages.
_textimage_preset_load Load a preset by id or name.
_textimage_ucwords Callback for preg_replace_callback.

Constants

Namesort descending Description
ALIGN_CENTER
ALIGN_LEFT
ALIGN_RIGHT
PREG_CLASS_SEPARATOR Matches all 'Z' Unicode character classes (separators).