diff options
Diffstat (limited to 'html/jpgraph/gd_image.inc.php')
| -rw-r--r-- | html/jpgraph/gd_image.inc.php | 2304 |
1 files changed, 2304 insertions, 0 deletions
diff --git a/html/jpgraph/gd_image.inc.php b/html/jpgraph/gd_image.inc.php new file mode 100644 index 0000000..fecc401 --- /dev/null +++ b/html/jpgraph/gd_image.inc.php @@ -0,0 +1,2304 @@ +<?php +//======================================================================= +// File: GD_IMAGE.INC.PHP +// Description: PHP Graph Plotting library. Low level image drawing routines +// Created: 2001-01-08, refactored 2008-03-29 +// Ver: $Id: gd_image.inc.php 1922 2010-01-11 11:42:50Z ljp $ +// +// Copyright (c) Asial Corporation. All rights reserved. +//======================================================================== + +require_once 'jpgraph_rgb.inc.php'; +require_once 'jpgraph_ttf.inc.php'; +require_once 'imageSmoothArc.php'; + +// Line styles +define('LINESTYLE_SOLID',1); +define('LINESTYLE_DOTTED',2); +define('LINESTYLE_DASHED',3); +define('LINESTYLE_LONGDASH',4); + +// The DEFAULT_GFORMAT sets the default graphic encoding format, i.e. +// PNG, JPG or GIF depending on what is installed on the target system +// in that order. +if( !DEFINED("DEFAULT_GFORMAT") ) { + define("DEFAULT_GFORMAT","auto"); +} + +//======================================================================== +// CLASS Image +// Description: The very coor image drawing class that encapsulates all +// calls to the GD library +// Note: The class used by the library is the decendant +// class RotImage which extends the Image class with transparent +// rotation. +//========================================================================= +class Image { + public $img=null; + public $rgb=null; + public $img_format; + public $ttf=null; + public $line_style=LINESTYLE_SOLID; + public $current_color,$current_color_name; + public $original_width=0, $original_height=0; + public $plotwidth=0,$plotheight=0; + + // for __get, __set + private $_left_margin=30,$_right_margin=30,$_top_margin=20,$_bottom_margin=30; + //private $_plotwidth=0,$_plotheight=0; + private $_width=0, $_height=0; + private $_line_weight=1; + + protected $expired=true; + protected $lastx=0, $lasty=0; + protected $obs_list=array(); + protected $font_size=12,$font_family=FF_DEFAULT, $font_style=FS_NORMAL; + protected $font_file=''; + protected $text_halign="left",$text_valign="bottom"; + protected $use_anti_aliasing=false; + protected $quality=null; + protected $colorstack=array(),$colorstackidx=0; + protected $canvascolor = 'white' ; + protected $langconv = null ; + protected $iInterlace=false; + protected $bbox_cache = array(); // STore the last found tetx bounding box + protected $ff_font0; + protected $ff_font0_bold; + protected $ff_font1; + protected $ff_font1_bold; + protected $ff_font2; + protected $ff_font2_bold; + + + //--------------- + // CONSTRUCTOR + function __construct($aWidth=0,$aHeight=0,$aFormat=DEFAULT_GFORMAT,$aSetAutoMargin=true) { + + $this->original_width = $aWidth; + $this->original_height = $aHeight; + $this->CreateImgCanvas($aWidth, $aHeight); + + if( $aSetAutoMargin ) { + $this->SetAutoMargin(); + } + + if( !$this->SetImgFormat($aFormat) ) { + JpGraphError::RaiseL(25081,$aFormat);//("JpGraph: Selected graphic format is either not supported or unknown [$aFormat]"); + } + $this->ttf = new TTF(); + $this->langconv = new LanguageConv(); + + $this->ff_font0 = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT0.gdf"); + $this->ff_font1 = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT1.gdf"); + $this->ff_font2 = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT2.gdf"); + $this->ff_font1_bold = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT1-Bold.gdf"); + $this->ff_font2_bold = imageloadfont(dirname(__FILE__) . "/fonts/FF_FONT2-Bold.gdf"); + } + + // Enable interlacing in images + function SetInterlace($aFlg=true) { + $this->iInterlace=$aFlg; + } + + // Should we use anti-aliasing. Note: This really slows down graphics! + function SetAntiAliasing($aFlg=true) { + $this->use_anti_aliasing = $aFlg; + if( function_exists('imageantialias') ) { + imageantialias($this->img,$aFlg); + } + else { + JpGraphError::RaiseL(25128);//('The function imageantialias() is not available in your PHP installation. Use the GD version that comes with PHP and not the standalone version.') + } + } + + function GetAntiAliasing() { + return $this->use_anti_aliasing ; + } + + function CreateRawCanvas($aWidth=0,$aHeight=0) { + + $aWidth *= SUPERSAMPLING_SCALE; + $aHeight *= SUPERSAMPLING_SCALE; + + if( $aWidth <= 1 || $aHeight <= 1 ) { + JpGraphError::RaiseL(25082,$aWidth,$aHeight);//("Illegal sizes specified for width or height when creating an image, (width=$aWidth, height=$aHeight)"); + } + + $this->img = @imagecreatetruecolor($aWidth, $aHeight); + if( $this->img < 1 ) { + JpGraphError::RaiseL(25126); + //die("Can't create truecolor image. Check that you really have GD2 library installed."); + } + $this->SetAlphaBlending(); + + if( $this->iInterlace ) { + imageinterlace($this->img,1); + } + if( $this->rgb != null ) { + $this->rgb->img = $this->img ; + } + else { + $this->rgb = new RGB($this->img); + } + } + + function CloneCanvasH() { + $oldimage = $this->img; + $this->CreateRawCanvas($this->width,$this->height); + imagecopy($this->img,$oldimage,0,0,0,0,$this->width,$this->height); + return $oldimage; + } + + function CreateImgCanvas($aWidth=0,$aHeight=0) { + + $old = array($this->img,$this->width,$this->height); + + $aWidth = round($aWidth); + $aHeight = round($aHeight); + + $this->width=$aWidth; + $this->height=$aHeight; + + + if( $aWidth==0 || $aHeight==0 ) { + // We will set the final size later. + // Note: The size must be specified before any other + // img routines that stroke anything are called. + $this->img = null; + $this->rgb = null; + return $old; + } + + $this->CreateRawCanvas($aWidth,$aHeight); + // Set canvas color (will also be the background color for a + // a pallett image + $this->SetColor($this->canvascolor); + $this->FilledRectangle(0,0,$this->width-1,$this->height-1); + + return $old ; + } + + function CopyCanvasH($aToHdl,$aFromHdl,$aToX,$aToY,$aFromX,$aFromY,$aWidth,$aHeight,$aw=-1,$ah=-1) { + if( $aw === -1 ) { + $aw = $aWidth; + $ah = $aHeight; + $f = 'imagecopyresized'; + } + else { + $f = 'imagecopyresampled'; + } + $f($aToHdl,$aFromHdl,$aToX,$aToY,$aFromX,$aFromY, $aWidth,$aHeight,$aw,$ah); + } + + function Copy($fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth=-1,$fromHeight=-1) { + $this->CopyCanvasH($this->img,$fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth,$fromHeight); + } + + function CopyMerge($fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth=-1,$fromHeight=-1,$aMix=100) { + if( $aMix == 100 ) { + $this->CopyCanvasH($this->img,$fromImg, + $toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth,$fromHeight); + } + else { + if( ($fromWidth != -1 && ($fromWidth != $toWidth)) || ($fromHeight != -1 && ($fromHeight != $fromHeight)) ) { + // Create a new canvas that will hold the re-scaled original from image + if( $toWidth <= 1 || $toHeight <= 1 ) { + JpGraphError::RaiseL(25083);//('Illegal image size when copying image. Size for copied to image is 1 pixel or less.'); + } + + $tmpimg = @imagecreatetruecolor($toWidth, $toHeight); + + if( $tmpimg < 1 ) { + JpGraphError::RaiseL(25084);//('Failed to create temporary GD canvas. Out of memory ?'); + } + $this->CopyCanvasH($tmpimg,$fromImg,0,0,0,0, + $toWidth,$toHeight,$fromWidth,$fromHeight); + $fromImg = $tmpimg; + } + imagecopymerge($this->img,$fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$aMix); + } + } + + static function GetWidth($aImg=null) { + if( $aImg === null ) { + $aImg = $this->img; + } + return imagesx($aImg); + } + + static function GetHeight($aImg=null) { + if( $aImg === null ) { + $aImg = $this->img; + } + return imagesy($aImg); + } + + static function CreateFromString($aStr) { + $img = imagecreatefromstring($aStr); + if( $img === false ) { + JpGraphError::RaiseL(25085); + //('An image can not be created from the supplied string. It is either in a format not supported or the string is representing an corrupt image.'); + } + return $img; + } + + function SetCanvasH($aHdl) { + $this->img = $aHdl; + $this->rgb->img = $aHdl; + } + + function SetCanvasColor($aColor) { + $this->canvascolor = $aColor ; + } + + function SetAlphaBlending($aFlg=true) { + ImageAlphaBlending($this->img,$aFlg); + } + + function SetAutoMargin() { + $min_bm=5; + $lm = min(40,$this->width/7); + $rm = min(20,$this->width/10); + $tm = max(5,$this->height/7); + $bm = max($min_bm,$this->height/6); + $this->SetMargin($lm,$rm,$tm,$bm); + } + + //--------------- + // PUBLIC METHODS + + function SetFont($family,$style=FS_NORMAL,$size=10) { + $this->font_family=$family; + $this->font_style=$style; + $this->font_size=$size*SUPERSAMPLING_SCALE; + $this->font_file=''; + if( ($this->font_family==FF_FONT1 || $this->font_family==FF_FONT2) && $this->font_style==FS_BOLD ){ + ++$this->font_family; + } + if( $this->font_family > FF_FONT2+1 ) { // A TTF font so get the font file + + // Check that this PHP has support for TTF fonts + if( !function_exists('imagettfbbox') ) { + // use internal font when php is configured without '--with-ttf' + $this->font_family = FF_FONT1; +// JpGraphError::RaiseL(25087);//('This PHP build has not been configured with TTF support. You need to recompile your PHP installation with FreeType support.'); + } else { + $this->font_file = $this->ttf->File($this->font_family,$this->font_style); + } + } + } + + // Get the specific height for a text string + function GetTextHeight($txt="",$angle=0) { + $tmp = preg_split('/\n/',$txt); + $n = count($tmp); + $m=0; + for($i=0; $i< $n; ++$i) { + $m = max($m,strlen($tmp[$i])); + } + + if( $this->font_family <= FF_FONT2+1 ) { + if( $angle==0 ) { + $h = imagefontheight($this->font_family); + if( $h === false ) { + JpGraphError::RaiseL(25088);//('You have a misconfigured GD font support. The call to imagefontwidth() fails.'); + } + + return $n*$h; + } + else { + $w = @imagefontwidth($this->font_family); + if( $w === false ) { + JpGraphError::RaiseL(25088);//('You have a misconfigured GD font support. The call to imagefontwidth() fails.'); + } + + return $m*$w; + } + } + else { + $bbox = $this->GetTTFBBox($txt,$angle); + return $bbox[1]-$bbox[5]+1; + } + } + + // Estimate font height + function GetFontHeight($angle=0) { + $txt = "XOMg"; + return $this->GetTextHeight($txt,$angle); + } + + // Approximate font width with width of letter "O" + function GetFontWidth($angle=0) { + $txt = 'O'; + return $this->GetTextWidth($txt,$angle); + } + + // Get actual width of text in absolute pixels. Note that the width is the + // texts projected with onto the x-axis. Call with angle=0 to get the true + // etxt width. + function GetTextWidth($txt,$angle=0) { + + $tmp = preg_split('/\n/',$txt); + $n = count($tmp); + if( $this->font_family <= FF_FONT2+1 ) { + + $m=0; + for($i=0; $i < $n; ++$i) { + $l=strlen($tmp[$i]); + if( $l > $m ) { + $m = $l; + } + } + + if( $angle==0 ) { + $w = @imagefontwidth($this->font_family); + if( $w === false ) { + JpGraphError::RaiseL(25088);//('You have a misconfigured GD font support. The call to imagefontwidth() fails.'); + } + return $m*$w; + } + else { + // 90 degrees internal so height becomes width + $h = @imagefontheight($this->font_family); + if( $h === false ) { + JpGraphError::RaiseL(25089);//('You have a misconfigured GD font support. The call to imagefontheight() fails.'); + } + return $n*$h; + } + } + else { + // For TTF fonts we must walk through a lines and find the + // widest one which we use as the width of the multi-line + // paragraph + $m=0; + for( $i=0; $i < $n; ++$i ) { + $bbox = $this->GetTTFBBox($tmp[$i],$angle); + $mm = $bbox[2] - $bbox[0]; + if( $mm > $m ) + $m = $mm; + } + return $m; + } + } + + + // Draw text with a box around it + function StrokeBoxedText($x,$y,$txt,$dir=0,$fcolor="white",$bcolor="black", + $shadowcolor=false,$paragraph_align="left", + $xmarg=6,$ymarg=4,$cornerradius=0,$dropwidth=3) { + + $oldx = $this->lastx; + $oldy = $this->lasty; + + if( !is_numeric($dir) ) { + if( $dir=="h" ) $dir=0; + elseif( $dir=="v" ) $dir=90; + else JpGraphError::RaiseL(25090,$dir);//(" Unknown direction specified in call to StrokeBoxedText() [$dir]"); + } + + if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) { + $width=$this->GetTextWidth($txt,$dir) ; + $height=$this->GetTextHeight($txt,$dir) ; + } + else { + $width=$this->GetBBoxWidth($txt,$dir) ; + $height=$this->GetBBoxHeight($txt,$dir) ; + } + + $height += 2*$ymarg; + $width += 2*$xmarg; + + if( $this->text_halign=="right" ) $x -= $width; + elseif( $this->text_halign=="center" ) $x -= $width/2; + + if( $this->text_valign=="bottom" ) $y -= $height; + elseif( $this->text_valign=="center" ) $y -= $height/2; + + $olda = $this->SetAngle(0); + + if( $shadowcolor ) { + $this->PushColor($shadowcolor); + $this->FilledRoundedRectangle($x-$xmarg+$dropwidth,$y-$ymarg+$dropwidth, + $x+$width+$dropwidth,$y+$height-$ymarg+$dropwidth, + $cornerradius); + $this->PopColor(); + $this->PushColor($fcolor); + $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg, + $x+$width,$y+$height-$ymarg, + $cornerradius); + $this->PopColor(); + $this->PushColor($bcolor); + $this->RoundedRectangle($x-$xmarg,$y-$ymarg, + $x+$width,$y+$height-$ymarg,$cornerradius); + $this->PopColor(); + } + else { + if( $fcolor ) { + $oc=$this->current_color; + $this->SetColor($fcolor); + $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height-$ymarg,$cornerradius); + $this->current_color=$oc; + } + if( $bcolor ) { + $oc=$this->current_color; + $this->SetColor($bcolor); + $this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height-$ymarg,$cornerradius); + $this->current_color=$oc; + } + } + + $h=$this->text_halign; + $v=$this->text_valign; + $this->SetTextAlign("left","top"); + + $debug=false; + $this->StrokeText($x, $y, $txt, $dir, $paragraph_align,$debug); + + $bb = array($x-$xmarg,$y+$height-$ymarg,$x+$width,$y+$height-$ymarg, + $x+$width,$y-$ymarg,$x-$xmarg,$y-$ymarg); + $this->SetTextAlign($h,$v); + + $this->SetAngle($olda); + $this->lastx = $oldx; + $this->lasty = $oldy; + + return $bb; + } + + // Draw text with a box around it. This time the box will be rotated + // with the text. The previous method will just make a larger enough non-rotated + // box to hold the text inside. + function StrokeBoxedText2($x,$y,$txt,$dir=0,$fcolor="white",$bcolor="black", + $shadowcolor=false,$paragraph_align="left", + $xmarg=6,$ymarg=4,$cornerradius=0,$dropwidth=3) { + + // This version of boxed text will stroke a rotated box round the text + // thta will follow the angle of the text. + // This has two implications: + // 1) This methos will only support TTF fonts + // 2) The only two alignment that makes sense are centered or baselined + + if( $this->font_family <= FF_FONT2+1 ) { + JpGraphError::RaiseL(25131);//StrokeBoxedText2() Only support TTF fonts and not built in bitmap fonts + } + + $oldx = $this->lastx; + $oldy = $this->lasty; + $dir = $this->NormAngle($dir); + + if( !is_numeric($dir) ) { + if( $dir=="h" ) $dir=0; + elseif( $dir=="v" ) $dir=90; + else JpGraphError::RaiseL(25090,$dir);//(" Unknown direction specified in call to StrokeBoxedText() [$dir]"); + } + + $width=$this->GetTextWidth($txt,0) + 2*$xmarg; + $height=$this->GetTextHeight($txt,0) + 2*$ymarg ; + $rect_width=$this->GetBBoxWidth($txt,$dir) ; + $rect_height=$this->GetBBoxHeight($txt,$dir) ; + + $baseline_offset = $this->bbox_cache[1]-1; + + if( $this->text_halign=="center" ) { + if( $dir >= 0 && $dir <= 90 ) { + + $x -= $rect_width/2; + $x += sin($dir*M_PI/180)*$height; + $y += $rect_height/2; + + } elseif( $dir >= 270 && $dir <= 360 ) { + + $x -= $rect_width/2; + $y -= $rect_height/2; + $y += cos($dir*M_PI/180)*$height; + + } elseif( $dir >= 90 && $dir <= 180 ) { + + $x += $rect_width/2; + $y += $rect_height/2; + $y += cos($dir*M_PI/180)*$height; + + } + else { + // $dir > 180 && $dir < 270 + $x += $rect_width/2; + $x += sin($dir*M_PI/180)*$height; + $y -= $rect_height/2; + } + } + + // Rotate the box around this point + $this->SetCenter($x,$y); + $olda = $this->SetAngle(-$dir); + + // We need to use adjusted coordinats for the box to be able + // to draw the box below the baseline. This cannot be done before since + // the rotating point must be the original x,y since that is arounbf the + // point where the text will rotate and we cannot change this since + // that is where the GD/GreeType will rotate the text + + + // For smaller <14pt font we need to do some additional + // adjustments to make it look good + if( $this->font_size < 14 ) { + $x -= 2; + $y += 2; + } + else { + // $y += $baseline_offset; + } + + if( $shadowcolor ) { + $this->PushColor($shadowcolor); + $this->FilledRectangle($x-$xmarg+$dropwidth,$y+$ymarg+$dropwidth-$height, + $x+$width+$dropwidth,$y+$ymarg+$dropwidth); + //$cornerradius); + $this->PopColor(); + $this->PushColor($fcolor); + $this->FilledRectangle($x-$xmarg, $y+$ymarg-$height, + $x+$width, $y+$ymarg); + //$cornerradius); + $this->PopColor(); + $this->PushColor($bcolor); + $this->Rectangle($x-$xmarg,$y+$ymarg-$height, + $x+$width,$y+$ymarg); + //$cornerradius); + $this->PopColor(); + } + else { + if( $fcolor ) { + $oc=$this->current_color; + $this->SetColor($fcolor); + $this->FilledRectangle($x-$xmarg,$y+$ymarg-$height,$x+$width,$y+$ymarg);//,$cornerradius); + $this->current_color=$oc; + } + if( $bcolor ) { + $oc=$this->current_color; + $this->SetColor($bcolor); + $this->Rectangle($x-$xmarg,$y+$ymarg-$height,$x+$width,$y+$ymarg);//,$cornerradius); + $this->current_color=$oc; + } + } + + if( $this->font_size < 14 ) { + $x += 2; + $y -= 2; + } + else { + + // Restore the original y before we stroke the text + // $y -= $baseline_offset; + + } + + $this->SetCenter(0,0); + $this->SetAngle($olda); + + $h=$this->text_halign; + $v=$this->text_valign; + if( $this->text_halign == 'center') { + $this->SetTextAlign('center','basepoint'); + } + else { + $this->SetTextAlign('basepoint','basepoint'); + } + + $debug=false; + $this->StrokeText($x, $y, $txt, $dir, $paragraph_align,$debug); + + $bb = array($x-$xmarg, $y+$height-$ymarg, + $x+$width, $y+$height-$ymarg, + $x+$width, $y-$ymarg, + $x-$xmarg, $y-$ymarg); + + $this->SetTextAlign($h,$v); + $this->SetAngle($olda); + + $this->lastx = $oldx; + $this->lasty = $oldy; + + return $bb; + } + + // Set text alignment + function SetTextAlign($halign,$valign="bottom") { + $this->text_halign=$halign; + $this->text_valign=$valign; + } + + function _StrokeBuiltinFont($x,$y,$txt,$dir,$paragraph_align,&$aBoundingBox,$aDebug=false) { + + if( is_numeric($dir) && $dir!=90 && $dir!=0) + JpGraphError::RaiseL(25091);//(" Internal font does not support drawing text at arbitrary angle. Use TTF fonts instead."); + + $h=$this->GetTextHeight($txt); + $fh=$this->GetFontHeight(); + $w=$this->GetTextWidth($txt); + + if( $this->text_halign=="right") { + $x -= $dir==0 ? $w : $h; + } + elseif( $this->text_halign=="center" ) { + // For center we subtract 1 pixel since this makes the middle + // be prefectly in the middle + $x -= $dir==0 ? $w/2-1 : $h/2; + } + if( $this->text_valign=="top" ) { + $y += $dir==0 ? $h : $w; + } + elseif( $this->text_valign=="center" ) { + $y += $dir==0 ? $h/2 : $w/2; + } + + $use_font = $this->font_family; + + if( $dir==90 ) { + imagestringup($this->img,$use_font,$x,$y,$txt,$this->current_color); + $aBoundingBox = array(round($x),round($y),round($x),round($y-$w),round($x+$h),round($y-$w),round($x+$h),round($y)); + if( $aDebug ) { + // Draw bounding box + $this->PushColor('green'); + $this->Polygon($aBoundingBox,true); + $this->PopColor(); + } + } + else { + if( preg_match('/\n/',$txt) ) { + $tmp = preg_split('/\n/',$txt); + for($i=0; $i < count($tmp); ++$i) { + $w1 = $this->GetTextWidth($tmp[$i]); + if( $paragraph_align=="left" ) { + imagestring($this->img,$use_font,$x,$y-$h+1+$i*$fh,$tmp[$i],$this->current_color); + } + elseif( $paragraph_align=="right" ) { + imagestring($this->img,$use_font,$x+($w-$w1),$y-$h+1+$i*$fh,$tmp[$i],$this->current_color); + } + else { + imagestring($this->img,$use_font,$x+$w/2-$w1/2,$y-$h+1+$i*$fh,$tmp[$i],$this->current_color); + } + } + } + else { + //Put the text + imagestring($this->img,$use_font,$x,$y-$h+1,$txt,$this->current_color); + } + if( $aDebug ) { + // Draw the bounding rectangle and the bounding box + $p1 = array(round($x),round($y),round($x),round($y-$h),round($x+$w),round($y-$h),round($x+$w),round($y)); + + // Draw bounding box + $this->PushColor('green'); + $this->Polygon($p1,true); + $this->PopColor(); + + } + $aBoundingBox=array(round($x),round($y),round($x),round($y-$h),round($x+$w),round($y-$h),round($x+$w),round($y)); + } + } + + function AddTxtCR($aTxt) { + // If the user has just specified a '\n' + // instead of '\n\t' we have to add '\r' since + // the width will be too muchy otherwise since when + // we print we stroke the individually lines by hand. + $e = explode("\n",$aTxt); + $n = count($e); + for($i=0; $i<$n; ++$i) { + $e[$i]=str_replace("\r","",$e[$i]); + } + return implode("\n\r",$e); + } + + function NormAngle($a) { + // Normalize angle in degrees + // Normalize angle to be between 0-360 + while( $a > 360 ) + $a -= 360; + while( $a < -360 ) + $a += 360; + if( $a < 0 ) + $a = 360 + $a; + return $a; + } + + function imagettfbbox_fixed($size, $angle, $fontfile, $text) { + + + if( ! USE_LIBRARY_IMAGETTFBBOX ) { + + $bbox = @imagettfbbox($size, $angle, $fontfile, $text); + if( $bbox === false ) { + JpGraphError::RaiseL(25092,$this->font_file); + //("There is either a configuration problem with TrueType or a problem reading font file (".$this->font_file."). Make sure file exists and is in a readable place for the HTTP process. (If 'basedir' restriction is enabled in PHP then the font file must be located in the document root.). It might also be a wrongly installed FreeType library. Try uppgrading to at least FreeType 2.1.13 and recompile GD with the correct setup so it can find the new FT library."); + } + $this->bbox_cache = $bbox; + return $bbox; + } + + // The built in imagettfbbox is buggy for angles != 0 so + // we calculate this manually by getting the bounding box at + // angle = 0 and then rotate the bounding box manually + $bbox = @imagettfbbox($size, 0, $fontfile, $text); + if( $bbox === false ) { + JpGraphError::RaiseL(25092,$this->font_file); + //("There is either a configuration problem with TrueType or a problem reading font file (".$this->font_file."). Make sure file exists and is in a readable place for the HTTP process. (If 'basedir' restriction is enabled in PHP then the font file must be located in the document root.). It might also be a wrongly installed FreeType library. Try uppgrading to at least FreeType 2.1.13 and recompile GD with the correct setup so it can find the new FT library."); + } + + $angle = $this->NormAngle($angle); + + $a = $angle*M_PI/180; + $ca = cos($a); + $sa = sin($a); + $ret = array(); + + // We always add 1 pixel to the left since the left edge of the bounding + // box is sometimes coinciding with the first pixel of the text + //$bbox[0] -= 1; + //$bbox[6] -= 1; + + // For roatated text we need to add extra width for rotated + // text since the kerning and stroking of the TTF is not the same as for + // text at a 0 degree angle + + if( $angle > 0.001 && abs($angle-360) > 0.001 ) { + $h = abs($bbox[7]-$bbox[1]); + $w = abs($bbox[2]-$bbox[0]); + + $bbox[0] -= 2; + $bbox[6] -= 2; + // The width is underestimated so compensate for that + $bbox[2] += round($w*0.06); + $bbox[4] += round($w*0.06); + + // and we also need to compensate with increased height + $bbox[5] -= round($h*0.1); + $bbox[7] -= round($h*0.1); + + if( $angle > 90 ) { + // For angles > 90 we also need to extend the height further down + // by the baseline since that is also one more problem + $bbox[1] += round($h*0.15); + $bbox[3] += round($h*0.15); + + // and also make it slighty less height + $bbox[7] += round($h*0.05); + $bbox[5] += round($h*0.05); + + // And we need to move the box slightly top the rright (from a tetx perspective) + $bbox[0] += round($w*0.02); + $bbox[6] += round($w*0.02); + + if( $angle > 180 ) { + // And we need to move the box slightly to the left (from a text perspective) + $bbox[0] -= round($w*0.02); + $bbox[6] -= round($w*0.02); + $bbox[2] -= round($w*0.02); + $bbox[4] -= round($w*0.02); + + } + + } + for($i = 0; $i < 7; $i += 2) { + $ret[$i] = round($bbox[$i] * $ca + $bbox[$i+1] * $sa); + $ret[$i+1] = round($bbox[$i+1] * $ca - $bbox[$i] * $sa); + } + $this->bbox_cache = $ret; + return $ret; + } + else { + $this->bbox_cache = $bbox; + return $bbox; + } + } + + // Deprecated + function GetTTFBBox($aTxt,$aAngle=0) { + $bbox = $this->imagettfbbox_fixed($this->font_size,$aAngle,$this->font_file,$aTxt); + return $bbox; + } + + function GetBBoxTTF($aTxt,$aAngle=0) { + // Normalize the bounding box to become a minimum + // enscribing rectangle + + $aTxt = $this->AddTxtCR($aTxt); + + if( !is_readable($this->font_file) ) { + JpGraphError::RaiseL(25093,$this->font_file); + //('Can not read font file ('.$this->font_file.') in call to Image::GetBBoxTTF. Please make sure that you have set a font before calling this method and that the font is installed in the TTF directory.'); + } + $bbox = $this->imagettfbbox_fixed($this->font_size,$aAngle,$this->font_file,$aTxt); + + if( $aAngle==0 ) return $bbox; + + if( $aAngle >= 0 ) { + if( $aAngle <= 90 ) { //<=0 + $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1], + $bbox[2],$bbox[5],$bbox[6],$bbox[5]); + } + elseif( $aAngle <= 180 ) { //<= 2 + $bbox = array($bbox[4],$bbox[7],$bbox[0],$bbox[7], + $bbox[0],$bbox[3],$bbox[4],$bbox[3]); + } + elseif( $aAngle <= 270 ) { //<= 3 + $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5], + $bbox[6],$bbox[1],$bbox[2],$bbox[1]); + } + else { + $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3], + $bbox[4],$bbox[7],$bbox[0],$bbox[7]); + } + } + elseif( $aAngle < 0 ) { + if( $aAngle <= -270 ) { // <= -3 + $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1], + $bbox[2],$bbox[5],$bbox[6],$bbox[5]); + } + elseif( $aAngle <= -180 ) { // <= -2 + $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3], + $bbox[4],$bbox[7],$bbox[0],$bbox[7]); + } + elseif( $aAngle <= -90 ) { // <= -1 + $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5], + $bbox[6],$bbox[1],$bbox[2],$bbox[1]); + } + else { + $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3], + $bbox[4],$bbox[7],$bbox[0],$bbox[7]); + } + } + return $bbox; + } + + function GetBBoxHeight($aTxt,$aAngle=0) { + $box = $this->GetBBoxTTF($aTxt,$aAngle); + return abs($box[7]-$box[1]); + } + + function GetBBoxWidth($aTxt,$aAngle=0) { + $box = $this->GetBBoxTTF($aTxt,$aAngle); + return $box[2]-$box[0]+1; + } + + + function _StrokeTTF($x,$y,$txt,$dir,$paragraph_align,&$aBoundingBox,$debug=false) { + + // Setup default inter line margin for paragraphs to be + // 3% of the font height. + $ConstLineSpacing = 0.03 ; + + // Remember the anchor point before adjustment + if( $debug ) { + $ox=$x; + $oy=$y; + } + + if( !preg_match('/\n/',$txt) || ($dir>0 && preg_match('/\n/',$txt)) ) { + // Format a single line + + $txt = $this->AddTxtCR($txt); + $bbox=$this->GetBBoxTTF($txt,$dir); + $width = $this->GetBBoxWidth($txt,$dir); + $height = $this->GetBBoxHeight($txt,$dir); + + // The special alignment "basepoint" is mostly used internally + // in the library. This will put the anchor position at the left + // basepoint of the tetx. This is the default anchor point for + // TTF text. + + if( $this->text_valign != 'basepoint' ) { + // Align x,y ot lower left corner of bbox + + + if( $this->text_halign=='right' ) { + $x -= $width; + $x -= $bbox[0]; + } + elseif( $this->text_halign=='center' ) { + $x -= $width/2; + $x -= $bbox[0]; + } + elseif( $this->text_halign=='baseline' ) { + // This is only support for text at 90 degree !! + // Do nothing the text is drawn at baseline by default + } + + if( $this->text_valign=='top' ) { + $y -= $bbox[1]; // Adjust to bottom of text + $y += $height; + } + elseif( $this->text_valign=='center' ) { + $y -= $bbox[1]; // Adjust to bottom of text + $y += $height/2; + } + elseif( $this->text_valign=='baseline' ) { + // This is only support for text at 0 degree !! + // Do nothing the text is drawn at baseline by default + } + } + ImageTTFText ($this->img, $this->font_size, $dir, $x, $y, + $this->current_color,$this->font_file,$txt); + + // Calculate and return the co-ordinates for the bounding box + $box = $this->imagettfbbox_fixed($this->font_size,$dir,$this->font_file,$txt); + $p1 = array(); + + for($i=0; $i < 4; ++$i) { + $p1[] = round($box[$i*2]+$x); + $p1[] = round($box[$i*2+1]+$y); + } + $aBoundingBox = $p1; + + // Debugging code to highlight the bonding box and bounding rectangle + // For text at 0 degrees the bounding box and bounding rectangle are the + // same + if( $debug ) { + // Draw the bounding rectangle and the bounding box + + $p = array(); + $p1 = array(); + + for($i=0; $i < 4; ++$i) { + $p[] = $bbox[$i*2]+$x ; + $p[] = $bbox[$i*2+1]+$y; + $p1[] = $box[$i*2]+$x ; + $p1[] = $box[$i*2+1]+$y ; + } + + // Draw bounding box + $this->PushColor('green'); + $this->Polygon($p1,true); + $this->PopColor(); + + // Draw bounding rectangle + $this->PushColor('darkgreen'); + $this->Polygon($p,true); + $this->PopColor(); + + // Draw a cross at the anchor point + $this->PushColor('red' |
