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;
}