uw Wed Mar 28 04:48:36 2001 EDT
Added files:
/php4/pear/Experimental/Image gtext.php
Log:
Creates graphical text images using Cache/Graphics.
Although it's unlikely that the API will changed use it with care some
features seen to be broken :/.
Index: php4/pear/Experimental/Image/gtext.php
+++ php4/pear/Experimental/Image/gtext.php
<?php
// +----------------------------------------------------------------------+
// | PHP version 4.0 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | [EMAIL PROTECTED] so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Ulf Wendel <[EMAIL PROTECTED]> |
// +----------------------------------------------------------------------+
require_once("Image/color_helper.php");
/**
* Creates graphical texts.
*
* FIXME - Usage example missing - FIXME
*
* @author Ulf Wendel <[EMAIL PROTECTED]>
* @version $Id: gtext.php,v 1.1 2001/03/28 12:48:36 uw Exp $
*/
class gtext extends ColorHelper {
/**
* Characters with descenders.
*
* ImageTTFBox() gives you the correct width of a text but not the correct height.
* It does not return the body size but the size from the baseline to the ascender.
* For correct vertical alignment of texts that contain characters which have
* descenders (font dependend) you must perform an extra test to get the correct
* height.
*
* The string is used as a regular expression. Make sure, that you escaped all
* special characters.
*
* @var string All characters with descenders in the font you use.
* Default should work with Arial.
* @access public
*/
var $descenders = "fgjpq�yGJY�|�,;{}\[\]�_@������������";
/**
* String used to measure the size of descenders.
*
* buildImage() uses a dirty hack to measure the size of descenders.
* It generates an image with the descender sign and scans the image...
*
* @var string String used to draw a test image to find the size of
* descenders.
*/
var $descender_string = "gG";
/**
* Graphics cache object used to create the images
*
* FIXME - description missing
*
* @var object Cache_Graphics
*/
var $cache;
/**
* Properties of the gtext image.
*
*
* @var array
*/
var $properties = array(
"fontsize" => 20,
"fgcolor" => "black",
"fontdir" => "",
"font" => "arial",
"transparent" => "white",
"bgcolor" => "white",
"padding" => 4
);
/**
* Unused list of allowed properties
*
* FIXME - It's does not have any meaning - just to give you an idea on the
features.
*
* @var array
*/
var $allowed = array(
"fontsize" => "integer",
"fgcolor" => "mixed",
"fontdir" => "strint",
"bold" => "boolean",
"italic" => "boolean",
"transparent" => "mixed",
"padding" => "integer",
"bgcolor" => "mixed",
"background" => "string",
"bgstrech" => "boolean",
"bgcenter" => "boolean",
"bevel" => "integer",
"bevelcolor" => "mixed",
"statusbar" => "string",
"name" => "string",
"href" => "string"
);
/**
* Creates an instance of the graphics_cache class.
*
* @see $cache
*/
function gtext() {
$this->cache = new Cache_Graphics();
} // end constructor
/**
* Creates a graphical text and returns the image stream.
*
* @param string Text of the image
* @param mixed List of properties that describe the image.
* FIXME description missing
* @param string Image format: gif, jpg, png, wbmp - make sure
* that your gd library features the requested format
* @return string Image stream
* @see getImageTag()
* @access public
*/
function getImage($text, $properties = "", $format = "png") {
$properties = array_merge($this->properties, $properties);
$id = $this->cache->generateID($text, $properties);
if ($image = $cache->getImage($id, $format))
return $image;
$img = $this->buildImage($id, $text, $properties);
return $this->cache->cacheImage($id, $img, $format);
} // end func getImage
/**
* Creates a graphical text and returns it as a <img>-Tag, optionally as hyperlink.
*
* @brother getImage()
*/
function getImageTag($text, $properties = "", $format = "png") {
$properties = array_merge($this->properties, $properties);
$id = $this->cache->generateID(array($text, $properties));
$link = $this->cache->getImageLink($id, $format);
if (count($link))
return $this->createImageTag($id, $text, $properties, $link);
$img = $this->buildImage($id, $text, $properties);
$link = $this->cache->cacheImageLink($id, $img, $format);
return $this->createImageTag($id, $text, $properties, $link);
} // end func getImageTag
/**
* Creates a grahical text and return it as a <img>-Tag, optionally as hyperlink.
*
* Private function that does all the work. getImageTag() is only the
* public wrapper for this and some other functions.
*
* @param string Image-ID used with the cache
* @param string Image text
* @param array Array that describes the properties of the image
* @param array Array with the URL and the total file path to the image
* @return string HTML tag.
*/
function createImageTag($id, $text, &$properties, $link) {
$size = @getImageSize($link[0]);
$name = (isset($properties["name"])) ? $properties["name"] : "";
$js_over = "";
$js_out = "";
if (isset($properties["statusbar"])) {
$name = $id;
$js_over .= sprintf("window.status = '%s';", str_replace('"', "'",
$properties["statusbar"]));
$js_out .= "window.status = '';";
}
$js = "";
if ($js_over)
$js .= sprintf(' onMouseOver="%s";', $js_over);
if ($js_out)
$js .= sprintf(' onMouseOut="%s";', $js_out);
$html = sprintf('<img src="%s" %s alt="%s"%s%s>',
$link[1],
$size[3],
(isset($properties["alt"])) ? $properties["alt"] : $text,
($name) ? ' name="' . $name . '"' : "",
($js) ? $js : ""
);
if (isset($properties["url"])) {
$html = sprintf('<a href="%s"%s>%s</a>',
$properties["url"],
(isset($properties["target"])) ? ' target="' .
$properties["target"] . '"' : ""
);
}
return $html;
} // end func createImageTag
/**
* Image creation function. The magic happens here.
*
* @param string Image-ID
* @param string Image text
* @param array Array that describes the properties of the image
* @return resource Image handler of the created image.
*/
function buildImage($id, $text, &$properties) {
static $descender_cache = array();
$font_dir = (isset($properties["fontdir"])) ? $properties["fontdir"] :
$this->font_dir;
// WARNING: this is a so called ulfismus - I always add a slash to the given
path
if ($font_dir && "/" != substr($font_dir, -1))
$font_dir .= "/";
$font = $font_dir . $properties["font"];
// KLUDGE: This is not a proper test but better than nothing and
// windows seems to follow this naming convention widely.
if (isset($properties["italic"])) {
if (isset($properties["bold"]) && file_exists($font . "bi.ttf"))
$font .= "bi";
else if (file_exists($font . "i.tff"))
$font .= "i";
} else {
if (isset($properties["bold"])) {
if (file_exists($font . "bd.ttf"))
$font .= "bd";
else if (file_exists($font . "b.ttf"))
$font .= "b";
}
}
// TODO: Make gtext work with Postscript fonts.
$font .= ".ttf";
// size of the bounding text box
$textsize = ImageTTFBBox($properties["fontsize"], 0, $font , $text);
$font_width = abs($textsize[2] - $textsize[0]);
$font_height = abs($textsize[3] - $textsize[5]);
// TRICKY but WARNING: check for descenders, see below
if (preg_match("/[" . $this->descenders . "]+/", $text)) {
// Ok, this is really dirty code. Sit down before you go over it.
// To find the size of the desecenders of the current font a new
// image is created that's filled from left to right with the
// configurable "descender_string". After that the image gets scanned
// to find the size of the string... The scan starts at the lower left
// corner and hopefully the script will soon find a pixel with the
// foreground color because the time it takes to scan it image is
// with * height...
if (isset($descender_cache[$font][$properties["fontsize"]])) {
$descender_diff =
$descender_cache[$font][$properties["fontsize"]]["diff"];
$font_height =
$descender_cache[$font][$properties["fontsize"]]["total"];
} else {
// descender size
$size = ImageTTFBBox($properties["fontsize"], 0, $font,
$this->descender_string);
$font_height = abs($size[3] - $size[5]);
$descender_width = abs($size[2] - $size[0]);
$descender_height = 2 * $font_height;
// temporary image to draw on
$tmp_img = @ImageCreate($descender_width, $descender_height);
$bg_color = $this->allocateColor($tmp_img, "white");
$fg_color = $this->allocateColor($tmp_img, "black");
for ($x = -$descender_width; $x < $descender_width; $x++)
ImageTTFText($tmp_img, $properties["fontsize"], 0, $x,
round($descender_height) / 1.5, $fg_color, $font, $this->descender_string);
for ($x = -$descender_width; $x < $descender_width; $x++)
ImageTTFText($tmp_img, $properties["fontsize"], 0, $x,
round($descender_height) / 1.5, $bg_color, $font, "I");
// scanning the height of a descender
// search the lower right edge
for ($yb = $descender_height; $yb >= 0; $yb--)
for ($x = $descender_width; $x >= 0; $x--)
if (ImageColorAt($tmp_img, $x, $yb) == $fg_color)
break 2;
for ($yt = 0; $yt <= $descender_height; $yt++)
for ($x = $descender_width; $x >= 0; $x--)
if (ImageColorAt($tmp_img, $x, $yt) == $fg_color)
break 2;
$descender_diff = abs($yb - $yt);
$font_height += $descender_diff;
// cache the result
$descender_cache[$font][$properties["fontsize"]]["total"] =
$font_height;
$descender_cache[$font][$properties["fontsize"]]["diff"] =
$descender_diff;
}
}
// image size = text size + special effects (borders...) size
$image_width = $font_width + 2 * $properties["padding"];;
$image_height = $font_height + 2 * $properties["padding"];;
if (isset($properties["bevel"])) {
$image_width += $properties["bevel"];
$image_height += $properties["bevel"];
}
$img = @ImageCreate($image_width, $image_height);
if (!$img)
return new gerror("Can't allocate a new image.");
// draw the background
$bg_color = $this->allocateColor($img, $properties["bgcolor"]);
ImageFilledRectangle($img, 0, 0, $image_width, $image_height, $bg_color);
// background image?
if (isset($properties["background"]) &&
file_exists($properties["background"])) {
if (!$size = @getimagesize($properties["background"]))
return new gerror("Can't access the background image. Must be GIF, JPG
or PNG format.");
$format = array("", "GIF", "JPG", "PNG");
$format = strtoupper($format[$size[1]]);
if ("JPG" == $format)
$format = "JPEG";
// use a variable function to create an image from JPG, PNG; GIF, WMBP
etc.
$func = "ImageCreateFrom$format";
if (function_exists($func)) {
$bg_img = $func($properties["background"]);
if ($bg_img) {
// center the background image in the middle of the new image
if (isset($properties["bgcenter"])) {
// range checks otherwise the results may be unpredictable
if ($size[0] >= $image_width) {
$x = 0;
$w = $image_width;
} else {
$x = ($image_width - $size[0]) / 2;
$w = $size[0];
}
if ($size[1] >= $image_height) {
$y = 0;
$h = $image_height;
} else {
$y = ($image_height - $size[1]) / 2;
$h = $size[1];
}
ImageCopy($img, $bg_img, $x, $y, 0, 0, $w, $h);
} else if (isset($properties["bgstretch"])) {
// strech the background image to the size of the new image
ImageCopyResized($img, $bg_img, 0, 0, 0, 0, $image_width,
$image_height, $size[0], $size[1]);
} else {
for ($x = 0; $x <= $image_width; $x += $size[0]) {
for ($y = 0; $y <= $image_height; $y += $size[1])
ImageCopy($img, $bg_img, $x, $y, 0, 0, $size[0],
$size[1]);
if ($y > $image_height)
ImageCopy($img, $bg_img, $x, $y - $size[0], 0, 0,
$size[0], $y - $image_height);
}
if ($x > $image_width) {
$dx = $x - $image_width;
$x -= $size[0];
for ($y = 0; $y < $image_height; $y += $size[1])
ImageCopy($img, $bg_img, $x, $y, 0, 0, $dx, $size[1]);
if ($y > $image_height)
ImageCopy($img, $bg_img, $x, $y - $size[0], 0, 0, $dx,
$y - $image_height);
}
}
ImageDestroy($bg_img);
} else {
return new gerror("Can't create background image");
}
}
}
// draw the bevel
if (isset($properties["bevel"])) {
$bevel_color = (isset($properties["bevelcolor"])) ?
$properties["bevelcolor"] : $properties["fgcolor"];
$bevel_color = $this->allocateColor($img, $bevel_color);
for ($i = 1; $i <= $properties["bevel"]; $i++) {
ImageLine($img, $i - 1, $image_height - $i, $image_width,
$image_height - $i, $bevel_color);
ImageLine($img, $image_width - $i, $i, $image_width - $i,
$image_height, $bevel_color);
}
}
// the text itself
$fg_color = $this->allocateColor($img, $properties["fgcolor"]);
if (isset($properties["padding"]) && $properties["padding"] > 0)
$padding = round($properties["padding"] / 2);
else
$padding = 0;
$middle = ($image_height / 2) + ($font_height / 2) - $descender_diff;
ImageTTFText($img, $properties["fontsize"], 0, $padding, $middle, $fg_color,
$font, $text);
// set the transparent color
if (isset($properties["transparent"])) {
$trans = $this->allocateColor($img, $properties["transparent"]);
ImageColorTransparent($img, $trans);
}
return $img;
} // end func buildImage
} // end class gtext
?>
--
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
To contact the list administrators, e-mail: [EMAIL PROTECTED]