12, 'angle' => 0, 'xpos' => 'left+10', 'ypos' => 'bottom-10', 'RGB' => array( 'red' => 51, 'green' => 51, 'blue' => 51, 'HEX' => '#333333', ), 'alpha' => 100, 'fontfile' => 'liberation-fonts-1.04/LiberationSans-Regular.ttf', 'text' => 'Hello World!', 'evaluate_text' => FALSE, ); $action = array_merge($defaults, (array)$action); $form = array( 'size' => array( '#type' => 'textfield', '#title' => t('Size'), '#default_value' => $action['size'], '#description' => t('Size: The font size. Depending on your version of GD, this should be specified as the pixel size (GD1) or point size (GD2).'), '#size' => 3, ), 'angle' => array( '#type' => 'textfield', '#title' => t('Angle'), '#default_value' => $action['angle'], '#description' => t('Angle: The angle in degrees, with 0 degrees being left-to-right reading text. Higher values represent a counter-clockwise rotation. For example, a value of 90 would result in bottom-to-top reading text.'), '#size' => 3, ), 'alpha' => array( '#type' => 'textfield', '#title' => t('Opacity'), '#default_value' => $action['alpha'], '#size' => 3, '#description' => t('Opacity: 1-100.'), '#element_validate' => array('imagecache_actions_validate_alpha'), ), 'xpos' => array( '#type' => 'textfield', '#title' => t('X offset'), '#default_value' => $action['xpos'], '#description' => t('Enter an offset in pixels or use a keyword: left, center, or right. Syntax like right-20 is also valid.'), '#size' => 10, ), 'ypos' => array( '#type' => 'textfield', '#title' => t('Y offset'), '#default_value' => $action['ypos'], '#description' => t('Enter an offset in pixels or use a keyword: top, center, or bottom. Syntax like bottom-20 is also valid.'), '#size' => 10, ), 'RGB' => imagecache_rgb_form($action['RGB']), 'fontfile' => array( '#type' => 'textfield', '#title' => t('Font file name'), '#default_value' => $action['fontfile'], '#description' => t('Font file is either the full system path (eg /usr/share/fonts/truetype/ttf-bitstream-vera/VeraMono.ttf), a font file inside the in the module dir "%moduledir" or the files "%filedir" folder). Example: "arial.ttf". You might find a list of fonts available at !helplink if your system supports it.', array('%moduledir' => drupal_get_path('module', 'imagecache_textactions'), '%filedir' => file_directory_path(), '!helplink' => l('fonts help', 'admin/help/imagecache_textactions') )), '#element_validate' => array('textactions_text2canvas_validate_fontfile'), ), 'text' => array( '#type' => 'textarea', '#rows' => 7, '#title' => t('Text'), '#default_value' => $action['text'], '#description' => t('The text string. If you are using a WYSIWYG editor, you should disable it for this field!'), '#wysiwyg' => FALSE, ), 'evaluate_text' => array( '#type' => 'checkbox', '#title' => t('Evaluate text as PHP code'), '#default_value' => $action['evaluate_text'], '#description' => t('If selected, the text will be treated as PHP code.

Enter PHP code that will return your dynamic text. Do not use %php tags.
EG return format_date(time());
return $file_data->description ? $file_data->description : $node->title;

Note that executing incorrect PHP-code can break your Drupal site.

If it\'s an image.module image then a $node object with its values may be available.
return $node->title;
return format_date($node->created);

If it\'s an image that has been attached to a node using CCK-filefield-imagefield (or just filefield) then as well as the parent $node object, a $file_data object that may contain a file description from that file field.
return $file_data->description; So far that seems to be the only available \'data\' provided by filefield, but you can investigate the node structure using devel.module or print_r() to see what else this array actually contains.

If it\'s a file that\'s just been attached using upload.module, a $file_data object may also have a description.
return $file_data->description;

If the image path is detected as belonging to more than one node, just the data for the first one found is returned.

An "$image" object is also available, but that usually contains only technical data, including
return $image->source;
return basename($image->source);
return $image->info["filesize"];

', array('%php' => '')) ), ); if (! user_access('administer site configuration')) { $form['evaluate_text']['#disabled'] = TRUE; $form['text']['#disabled'] = $action['evaluate_text']; // lock this if an admin has enabled evaluation. $form['evaluate_text']['#description'] = 'requires administer site configuration permissions.'; } return $form; } /** * Check if the named font is available */ function textactions_text2canvas_validate_fontfile(&$element, &$form_status) { if (! $fontfile = textactions_find_font($element['#value'], TRUE)) { // Just warn, don't prevent drupal_set_message(t("Unable to confirm that the font '%fontfile' is available on your system. This may fail, or your system may provide an appropriate fallback, depending on your toolkit behaviour.", array('%fontfile' => $element['#value'])), 'warning' ); } else { drupal_set_message(t("Font was found OK at %fontfile", array('%fontfile' => $fontfile)), 'notice'); } } /** * Implementation of theme_hook() for imagecache_ui.module */ function theme_textactions_text2canvas($element) { $data = $element['#value']; return "". $data['text'] ."" . t("
%sizepx #%color %alpha% %angle° (%xpos, %ypos)", array( '%size' => $data['size'], '%color' => strtoupper($data['RGB']['HEX']), '%alpha' => $data['alpha'], '%angle' => $data['angle'], '%xpos' => $data['xpos'], '%ypos' => $data['ypos'], ) ) ; } /** * Place the source image on the current background * * Implementation of hook_image() * * @param $image * @param $action */ function textactions_text2canvas_image(&$image, $action = array()) { $fontpath = textactions_find_font($action['fontfile']); if (! $fontpath) { drupal_set_message(t("Failed to locate the requested font %fontfile. Cannot overlay text onto image.", array('%fontfile' => $action['fontfile']))); return FALSE; } if ($action['evaluate_text']) { $text = textactions_evaluate_text($image, $action); } else { $text = $action['text']; } // Calculate position by first creating a temp image containing the text and accessing its dimensions $temp = textactions_create_font_image($action['size'], $action['angle'], $fontpath, $text ); if(! $temp) { drupal_set_message(t('Failed to generate text image. Cannot calculate text dimensions. Not overlaying text.'), 'error'); return; } // parse keywords $x_ins = textactions_keyword_filter($action['xpos'], $image->info['width'], $temp['width'], $temp['bx'], 'x'); $y_ins = textactions_keyword_filter($action['ypos'], $image->info['height'], $temp['height'], $temp['by'], 'y'); // convert color from hex (as it is stored in the UI) if($action['RGB']['HEX'] && $deduced = hex_to_rgb($action['RGB']['HEX'])) { $action['RGB'] = array_merge($action['RGB'], $deduced); } $action['alpha'] = ($action['alpha'] / 100); //Convert to decimal between 0 and 1 $action['RGB']['alpha'] = ((1 - $action['alpha']) * 127); //convert opacity to proper alpha value (0 = opaque, 127 = transparent) return imageapi_image_overlaytext_alpha($image, $text, $action['size'], $x_ins, $y_ins, $action['RGB'], $fontpath, $action['angle']); } /** * Generate the dynamic text for this image. * Was textactions caption - now merged as an option of text2canvas * * TODO further code review for safety etc * * @param $image object, such as it is * @param $action definition * * @return $text Plain string to be placed on the imagecache process. */ function textactions_evaluate_text($image, $action) { // Try to load the attached node - if any // This file_data object MAY be filled up with extra data (by ref) // when doing the lookup, eg // $file_data->description, $file_data->alt, $file_data->timestamp // HOOK_metadata from file attempts to glean info from any direction possible - EXIF, XMP, DB, description.txt // @see the meta_* project $meta = module_invoke_all('metadata_from_file', $image->source); $file_data = (object) $meta; static $panic; #This can trigger recursion! build-load-build-etc if ($panic) return; $panic = TRUE; $node = imagecache_actions_node_from_filepath($image->source, $file_data) ; $panic = FALSE; // Process the php using drupal_eval (rather than eval), // but with GLOBAL variables, so they can be passed successfully $GLOBALS['image'] = $image; $GLOBALS['node'] = $node; $GLOBALS['file_data'] = $file_data; $text = @drupal_eval('<'.'?php global $node; global $image; global $file_data; '.$action['text'].' ?'.'>'); return $text; } /** * Creates an image containing only the text - used to calculate the true * bounding box. */ function textactions_create_font_image( $size, $angle, $font, $char ) { $rect = imagettfbbox( $size, 0, $font, $char ); if(!$rect) { return NULL; } if( 0 == $angle ) { $imh = $rect[1] - $rect[7]; $imw = $rect[2] - $rect[0]; $bx = -1 - $rect[0]; $by = -1 - $rect[7]; } else { $rad = deg2rad( $angle ); $sin = sin( $rad ); $cos = cos( $rad ); if( $angle > 0 ) { $tmp = $rect[6] * $cos + $rect[7] * $sin; $bx = -1 - round( $tmp ); $imw = round( $rect[2] * $cos + $rect[3] * $sin - $tmp ); $tmp = $rect[5] * $cos - $rect[4] * $sin; $by = -1 - round( $tmp ); $imh = round( $rect[1] * $cos - $rect[0] * $sin - $tmp ); } else { $tmp = $rect[0] * $cos + $rect[1] * $sin; $bx = -1 - round( $tmp ); $imw = round( $rect[4] * $cos + $rect[5] * $sin - $tmp ); $tmp = $rect[7] * $cos - $rect[6] * $sin; $by = -1 - round( $tmp ); $imh = round( $rect[3] * $cos - $rect[2] * $sin - $tmp ); } } return array('width'=>$imw, 'height'=>$imh, 'bx'=>$bx, 'by'=>$by); } /** * Accept a keyword (center, top, left, etc) and return it as an offset in pixels. * Called on either the x or y values. * * @param $value * string or int value. May be something like "20", "center", "left+20" * @param $current_size * int size in pixels of the range this item is to be placed in * @param $object_size * int size in pixels of the object to be placed * * @param $xy * Whether the axis is on the x or the y (unused) * */ function textactions_keyword_filter($value, $current_pixels, $new_pixels, $adj, $xy) { // check if we have plus or minus values $v = explode('+', $value); $v2 = explode('-', $value); if (!empty($v2[1])) {$v[1]=-intval($v2[1]); $v[0]=$v2[0];} switch ($v[0]) { case 'top': case 'left': $value = 0; break; case 'bottom': case 'right': $value = $current_pixels - $new_pixels; break; case 'center': $value = $current_pixels/2 - $new_pixels/2; break; } // perform the adjustment $value=$value+$adj; // add any extra negative or positive if (!empty($v[1])) {$value=$value+$v[1];} #print $current_pixels.' . '.$new_pixels.' . '.$adj.' . '.$xy.'
';if ($xy=='y'){exit;} return $value; } /** * Place text on an image. * * @ingroup imageapi */ function imageapi_image_overlaytext_alpha(&$image, $text, $size = 12, $x = 0, $y = 0, $RGB = 0, $fontfile = 'MgOpenModernaBold', $angle = 0) { return imageapi_toolkit_invoke('overlaytext_alpha', $image, array($text, $size, $x, $y, $RGB, $fontfile, $angle)); } /** * Place text on an image. * * @ingroup imageapi */ function imageapi_gd_image_overlaytext_alpha(&$image, $text, $size = 12, $x = 0, $y = 0, $RGB, $fontfile = 'MgOpenModernaBold', $angle = 0) { $color = imagecolorallocatealpha($image->resource, $RGB['red'], $RGB['green'], $RGB['blue'], $RGB['alpha']); // Merging images where one or the other is partially transparent needs to be supported or we get black blocks. // EG text over PNG // This flag will be lost again in jpegs, but that's after we've merged, so it's OK. imagesavealpha($image->resource, TRUE); imagealphablending($image->resource, TRUE); $bounds = imagettftext($image->resource, $size, $angle, $x, $y, $color, $fontfile, $text); if (empty($bounds)) { return FALSE; } return TRUE; } /** * UTILITY */ /** * Given a font name or path, return the full real path. * Because the toolkit doesn't scan too well, we need to look ahead to avoid * problems and validate. * * Look for the named font file relative to Drupal, the module, and the files * dir. * * @param $fontpath a font file name, eg 'arial.ttf' * @param $strict bool. if set, the func will return nothing on failure. Normal * behaviour is to return the input fontfile name, trusting the system to be * able to find it for you. Strict is used to trigger a validation alert. * * @return the FULL filepath of the font so the image toolkit knows exactly * where it is. */ function textactions_find_font($fontpath, $strict = FALSE) { // Track down the font. if (is_file($fontpath)) { return realpath($fontpath); } $fontpath = ltrim($fontpath, '/'); // Try local $tryfontpath = drupal_get_path('module', 'imagecache_textactions') .'/fonts/'. $fontpath; if (is_file($tryfontpath)) { return realpath($tryfontpath); } // Try files $tryfontpath = file_create_path($fontpath); if (is_file($tryfontpath)) { return realpath($tryfontpath); } if ($strict) return FALSE; // otherwise, just return what we had and hope it's in a system library return $fontpath; }