Subversion Repositories Applications.annuaire

Rev

Rev 66 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
66 aurelien 1
<?php
2
//=======================================================================
3
// File:        JPGRAPH_LEGEND.INC.PHP
4
// Description: Class to handle the legend box in the graph that gives
5
//              names on the data series. The number of rows and columns
6
//              in the legend are user specifyable.
7
// Created:     2001-01-08 (Refactored to separate file 2008-08-01)
8
// Ver:         $Id: jpgraph_legend.inc.php 1926 2010-01-11 16:33:07Z ljp $
9
//
10
// Copyright (c) Aditus Consulting. All rights reserved.
11
//========================================================================
12
 
13
DEFINE('_DEFAULT_LPM_SIZE',8); // Default Legend Plot Mark size
14
 
15
 
16
//===================================================
17
// CLASS Legend
18
// Description: Responsible for drawing the box containing
19
// all the legend text for the graph
20
//===================================================
21
 
22
class Legend {
23
    public $txtcol=array();
24
    public $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12;
25
    private $color=array(0,0,0); // Default fram color
26
    private $fill_color=array(235,235,235); // Default fill color
27
    private $shadow=true; // Shadow around legend "box"
28
    private $shadow_color='darkgray';
29
    private $mark_abs_hsize=_DEFAULT_LPM_SIZE,$mark_abs_vsize=_DEFAULT_LPM_SIZE;
30
    private $xmargin=10,$ymargin=0,$shadow_width=2;
31
    private $xlmargin=4;
32
    private $ylinespacing=5;
33
 
34
     // We need a separate margin since the baseline of the last text would coincide with the bottom otherwise
35
    private $ybottom_margin = 8;
36
 
37
    private $xpos=0.05, $ypos=0.15, $xabspos=-1, $yabspos=-1;
38
    private $halign="right", $valign="top";
39
    private $font_color='black';
40
    private $hide=false,$layout_n=1;
41
    private $weight=1,$frameweight=1;
42
    private $csimareas='';
43
    private $reverse = false ;
44
    private $bkg_gradtype=-1, $bkg_gradfrom='lightgray', $bkg_gradto='gray';
45
 
46
    //---------------
47
    // CONSTRUCTOR
48
    function __construct() {
49
        // Empty
50
    }
51
    //---------------
52
    // PUBLIC METHODS
53
    function Hide($aHide=true) {
54
        $this->hide=$aHide;
55
    }
56
 
57
    function SetHColMargin($aXMarg) {
58
        $this->xmargin = $aXMarg;
59
    }
60
 
61
    function SetVColMargin($aSpacing) {
62
        $this->ylinespacing = $aSpacing ;
63
    }
64
 
65
    function SetLeftMargin($aXMarg) {
66
        $this->xlmargin = $aXMarg;
67
    }
68
 
69
    // Synonym
70
    function SetLineSpacing($aSpacing) {
71
        $this->ylinespacing = $aSpacing ;
72
    }
73
 
74
    function SetShadow($aShow='gray',$aWidth=4) {
75
        if( is_string($aShow) ) {
76
            $this->shadow_color = $aShow;
77
            $this->shadow=true;
78
        }
79
        else {
80
            $this->shadow = $aShow;
81
        }
82
        $this->shadow_width = $aWidth;
83
    }
84
 
85
    function SetMarkAbsSize($aSize) {
86
        $this->mark_abs_vsize = $aSize ;
87
        $this->mark_abs_hsize = $aSize ;
88
    }
89
 
90
    function SetMarkAbsVSize($aSize) {
91
        $this->mark_abs_vsize = $aSize ;
92
    }
93
 
94
    function SetMarkAbsHSize($aSize) {
95
        $this->mark_abs_hsize = $aSize ;
96
    }
97
 
98
    function SetLineWeight($aWeight) {
99
        $this->weight = $aWeight;
100
    }
101
 
102
    function SetFrameWeight($aWeight) {
103
        $this->frameweight = $aWeight;
104
    }
105
 
106
    function SetLayout($aDirection=LEGEND_VERT) {
107
        $this->layout_n = $aDirection==LEGEND_VERT ? 1 : 99 ;
108
    }
109
 
110
    function SetColumns($aCols) {
111
        $this->layout_n = $aCols ;
112
    }
113
 
114
    function SetReverse($f=true) {
115
        $this->reverse = $f ;
116
    }
117
 
118
    // Set color on frame around box
119
    function SetColor($aFontColor,$aColor='black') {
120
        $this->font_color=$aFontColor;
121
        $this->color=$aColor;
122
    }
123
 
124
    function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
125
        $this->font_family = $aFamily;
126
        $this->font_style = $aStyle;
127
        $this->font_size = $aSize;
128
    }
129
 
130
    function SetPos($aX,$aY,$aHAlign='right',$aVAlign='top') {
131
        $this->Pos($aX,$aY,$aHAlign,$aVAlign);
132
    }
133
 
134
    function SetAbsPos($aX,$aY,$aHAlign='right',$aVAlign='top') {
135
        $this->xabspos=$aX;
136
        $this->yabspos=$aY;
137
        $this->halign=$aHAlign;
138
        $this->valign=$aVAlign;
139
    }
140
 
141
    function Pos($aX,$aY,$aHAlign='right',$aVAlign='top') {
142
        if( !($aX<1 && $aY<1) ) {
143
            JpGraphError::RaiseL(25120);//(" Position for legend must be given as percentage in range 0-1");
144
        }
145
        $this->xpos=$aX;
146
        $this->ypos=$aY;
147
        $this->halign=$aHAlign;
148
        $this->valign=$aVAlign;
149
    }
150
 
151
    function SetFillColor($aColor) {
152
        $this->fill_color=$aColor;
153
    }
154
 
155
    function Clear() {
156
        $this->txtcol = array();
157
    }
158
 
159
    function Add($aTxt,$aColor,$aPlotmark='',$aLinestyle=0,$csimtarget='',$csimalt='',$csimwintarget='') {
160
        $this->txtcol[]=array($aTxt,$aColor,$aPlotmark,$aLinestyle,$csimtarget,$csimalt,$csimwintarget);
161
    }
162
 
163
    function GetCSIMAreas() {
164
        return $this->csimareas;
165
    }
166
 
167
    function SetBackgroundGradient($aFrom='navy',$aTo='silver',$aGradType=2) {
168
        $this->bkg_gradtype=$aGradType;
169
        $this->bkg_gradfrom = $aFrom;
170
        $this->bkg_gradto = $aTo;
171
    }
172
 
173
    function Stroke($aImg) {
174
        // Constant
175
        $fillBoxFrameWeight=1;
176
 
177
        if( $this->hide ) return;
178
 
179
        $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
180
 
181
        if( $this->reverse ) {
182
            $this->txtcol = array_reverse($this->txtcol);
183
        }
184
 
185
        $n=count($this->txtcol);
186
        if( $n == 0 ) return;
187
 
188
        // Find out the max width and height of each column to be able
189
        // to size the legend box.
190
        $numcolumns = ($n > $this->layout_n ? $this->layout_n : $n);
191
        for( $i=0; $i < $numcolumns; ++$i ) {
192
            $colwidth[$i] = $aImg->GetTextWidth($this->txtcol[$i][0]) +
193
                            2*$this->xmargin + 2*$this->mark_abs_hsize;
194
            $colheight[$i] = 0;
195
 
196
        }
197
 
198
        // Find our maximum height in each row
199
        $rows = 0 ; $rowheight[0] = 0;
200
        for( $i=0; $i < $n; ++$i ) {
201
            $h = max($this->mark_abs_vsize,$aImg->GetTextHeight($this->txtcol[$i][0]))+$this->ylinespacing;
202
 
203
            // Makes sure we always have a minimum of 1/4 (1/2 on each side) of the mark as space
204
            // between two vertical legend entries
205
            //$h = round(max($h,$this->mark_abs_vsize+$this->ymargin));
206
            //echo "Textheight #$i: tetxheight=".$aImg->GetTextHeight($this->txtcol[$i][0]).', ';
207
            //echo "h=$h ({$this->mark_abs_vsize},{$this->ymargin})<br>";
208
            if( $i % $numcolumns == 0 ) {
209
                $rows++;
210
                $rowheight[$rows-1] = 0;
211
            }
212
            $rowheight[$rows-1] = max($rowheight[$rows-1],$h);
213
        }
214
 
215
        $abs_height = 0;
216
        for( $i=0; $i < $rows; ++$i ) {
217
            $abs_height += $rowheight[$i] ;
218
        }
219
 
220
        // Make sure that the height is at least as high as mark size + ymargin
221
        $abs_height = max($abs_height,$this->mark_abs_vsize);
222
        $abs_height += $this->ybottom_margin;
223
 
224
        // Find out the maximum width in each column
225
        for( $i=$numcolumns; $i < $n; ++$i ) {
226
            $colwidth[$i % $numcolumns] = max(
227
                $aImg->GetTextWidth($this->txtcol[$i][0])+2*$this->xmargin+2*$this->mark_abs_hsize,
228
                $colwidth[$i % $numcolumns]);
229
        }
230
 
231
        // Get the total width
232
        $mtw = 0;
233
        for( $i=0; $i < $numcolumns; ++$i ) {
234
            $mtw += $colwidth[$i] ;
235
        }
236
 
237
        // remove the last rows interpace margin (since there is no next row)
238
        $abs_height -= $this->ylinespacing;
239
 
240
 
241
        // Find out maximum width we need for legend box
242
        $abs_width = $mtw+$this->xlmargin+($numcolumns-1)*$this->mark_abs_hsize;
243
 
244
        if( $this->xabspos === -1  && $this->yabspos === -1 ) {
245
            $this->xabspos = $this->xpos*$aImg->width ;
246
            $this->yabspos = $this->ypos*$aImg->height ;
247
        }
248
 
249
        // Positioning of the legend box
250
        if( $this->halign == 'left' ) {
251
        	$xp = $this->xabspos;
252
        }
253
        elseif( $this->halign == 'center' ) {
254
        	$xp = $this->xabspos - $abs_width/2;
255
        }
256
        else {
257
        	$xp = $aImg->width - $this->xabspos - $abs_width;
258
        }
259
 
260
        $yp=$this->yabspos;
261
        if( $this->valign == 'center' ) {
262
        	$yp-=$abs_height/2;
263
        }
264
        elseif( $this->valign == 'bottom' ) {
265
        	$yp-=$abs_height;
266
        }
267
 
268
        // Stroke legend box
269
        $aImg->SetColor($this->color);
270
        $aImg->SetLineWeight($this->frameweight);
271
        $aImg->SetLineStyle('solid');
272
 
273
        if( $this->shadow ) {
274
        	$aImg->ShadowRectangle($xp,$yp,
275
                                   $xp+$abs_width+$this->shadow_width+2,
276
                                   $yp+$abs_height+$this->shadow_width+2,
277
                                   $this->fill_color,$this->shadow_width+2,$this->shadow_color);
278
        }
279
        else {
280
            $aImg->SetColor($this->fill_color);
281
            $aImg->FilledRectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
282
            $aImg->SetColor($this->color);
283
            $aImg->Rectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
284
        }
285
 
286
        if( $this->bkg_gradtype >= 0 ) {
287
            $grad = new Gradient($aImg);
288
            $grad->FilledRectangle($xp+1, $yp+1,
289
                                   $xp+$abs_width-3, $yp+$abs_height-3,
290
                                   $this->bkg_gradfrom, $this->bkg_gradto,
291
                                   $this->bkg_gradtype);
292
        }
293
 
294
        // x1,y1 is the position for the legend marker + text
295
        // The vertical position is the baseline position for the text
296
        // and every marker is adjusted acording to that.
297
 
298
        // For multiline texts this get more complicated.
299
 
300
        $x1 = $xp + $this->xlmargin;
301
        $y1 = $yp + $rowheight[0] - $this->ylinespacing + 2 ; // The ymargin is included in rowheight
302
 
303
        // Now, y1 is the bottom vertical position of the first legend, i.e if
304
        // the legend has multiple lines it is the bottom line.
305
 
306
        $grad = new Gradient($aImg);
307
        $patternFactory = null;
308
 
309
        // Now stroke each legend in turn
310
        // Each plot has added the following information to  the legend
311
        // p[0] = Legend text
312
        // p[1] = Color,
313
        // p[2] = For markers a reference to the PlotMark object
314
        // p[3] = For lines the line style, for gradient the negative gradient style
315
        // p[4] = CSIM target
316
        // p[5] = CSIM Alt text
317
        $i = 1 ; $row = 0;
318
        foreach($this->txtcol as $p) {
319
 
320
            // STROKE DEBUG BOX
321
            if( _JPG_DEBUG ) {
322
                $aImg->SetLineWeight(1);
323
                $aImg->SetColor('red');
324
                $aImg->SetLineStyle('solid');
325
                $aImg->Rectangle($x1,$y1,$xp+$abs_width-1,$y1-$rowheight[$row]);
326
            }
327
 
328
            $aImg->SetLineWeight($this->weight);
329
            $x1 = round($x1)+1; // We add one to not collide with the border
330
            $y1=round($y1);
331
 
332
            // This is the center offset up from the baseline which is
333
            // considered the "center" of the marks. This gets slightly complicated since
334
            // we need to consider if the text is a multiline paragraph or if it is only
335
            // a single line. The reason is that for single line the y1 corresponds to the baseline
336
            // and that is fine. However for a multiline paragraph there is no single baseline
337
            // and in that case the y1 corresponds to the lowest y for the bounding box. In that
338
            // case we center the mark in the middle of the paragraph
339
            if( !preg_match('/\n/',$p[0]) ) {
340
                // Single line
341
                $marky = ceil($y1-$this->mark_abs_vsize/2)-1;
342
            } else {
343
                // Paragraph
344
                $marky = $y1 - $aImg->GetTextHeight($p[0])/2;
345
 
346
              //  echo "y1=$y1, p[o]={$p[0]}, marky=$marky<br>";
347
            }
348
 
349
            //echo "<br>Mark #$i: marky=$marky<br>";
350
 
351
            $x1 += $this->mark_abs_hsize;
352
 
353
            if ( !empty($p[2]) && $p[2]->GetType() > -1 ) {
354
 
355
 
356
                // Make a plot mark legend. This is constructed with a mark which
357
                // is run through with a line
358
 
359
                // First construct a bit of the line that looks exactly like the
360
                // line in the plot
361
                $aImg->SetColor($p[1]);
362
                if( is_string($p[3]) || $p[3]>0 ) {
363
                    $aImg->SetLineStyle($p[3]);
364
                    $aImg->StyleLine($x1-$this->mark_abs_hsize,$marky,$x1+$this->mark_abs_hsize,$marky);
365
                }
366
 
367
                // Stroke a mark with the standard size
368
                // (As long as it is not an image mark )
369
                if( $p[2]->GetType() != MARK_IMG ) {
370
 
371
                    // Clear any user callbacks since we ont want them called for
372
                    // the legend marks
373
                    $p[2]->iFormatCallback = '';
374
                    $p[2]->iFormatCallback2 = '';
375
 
376
                    // Since size for circles is specified as the radius
377
                    // this means that we must half the size to make the total
378
                    // width behave as the other marks
379
                    if( $p[2]->GetType() == MARK_FILLEDCIRCLE || $p[2]->GetType() == MARK_CIRCLE ) {
380
                        $p[2]->SetSize(min($this->mark_abs_vsize,$this->mark_abs_hsize)/2);
381
                        $p[2]->Stroke($aImg,$x1,$marky);
382
                    }
383
                    else {
384
                        $p[2]->SetSize(min($this->mark_abs_vsize,$this->mark_abs_hsize));
385
                        $p[2]->Stroke($aImg,$x1,$marky);
386
                    }
387
                }
388
            }
389
            elseif ( !empty($p[2]) && (is_string($p[3]) || $p[3]>0 ) ) {
390
                // Draw a styled line
391
                $aImg->SetColor($p[1]);
392
                $aImg->SetLineStyle($p[3]);
393
                $aImg->StyleLine($x1-$this->mark_abs_hsize,$marky,$x1+$this->mark_abs_hsize,$marky);
394
                $aImg->StyleLine($x1-$this->mark_abs_hsize,$marky+1,$x1+$this->mark_abs_hsize,$marky+1);
395
            }
396
            else {
397
                // Draw a colored box
398
                $color = $p[1] ;
399
 
400
                // We make boxes slightly larger to better show
401
                $boxsize = max($this->mark_abs_vsize,$this->mark_abs_hsize) + 2 ;
402
 
403
                $ym = $marky-ceil($boxsize/2) ; // Marker y-coordinate
404
 
405
                // We either need to plot a gradient or a
406
                // pattern. To differentiate we use a kludge.
407
                // Patterns have a p[3] value of < -100
408
                if( $p[3] < -100 ) {
409
                    // p[1][0] == iPattern, p[1][1] == iPatternColor, p[1][2] == iPatternDensity
410
                    if( $patternFactory == null ) {
411
                        $patternFactory = new RectPatternFactory();
412
                    }
413
                    $prect = $patternFactory->Create($p[1][0],$p[1][1],1);
414
                    $prect->SetBackground($p[1][3]);
415
                    $prect->SetDensity($p[1][2]+1);
416
                    $prect->SetPos(new Rectangle($x1,$ym,$boxsize,$boxsize));
417
                    $prect->Stroke($aImg);
418
                    $prect=null;
419
                }
420
                else {
421
                    if( is_array($color) && count($color)==2 ) {
422
                        // The client want a gradient color
423
                        $grad->FilledRectangle($x1-$boxsize/2,$ym,
424
                                               $x1+$boxsize/2,$ym+$boxsize,
425
                                               $color[0],$color[1],-$p[3]);
426
                    }
427
                    else {
428
                        $aImg->SetColor($p[1]);
429
                        $aImg->FilledRectangle($x1-$boxsize/2,$ym,
430
                                               $x1+$boxsize/2,$ym+$boxsize);
431
                    }
432
                    $aImg->SetColor($this->color);
433
                    $aImg->SetLineWeight($fillBoxFrameWeight);
434
                    $aImg->Rectangle($x1-$boxsize/2,$ym,
435
                                     $x1+$boxsize/2,$ym+$boxsize);
436
                }
437
            }
438
            $aImg->SetColor($this->font_color);
439
            $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
440
            $aImg->SetTextAlign('left','baseline');
441
 
442
            $debug=false;
443
            $aImg->StrokeText($x1+$this->mark_abs_hsize+$this->xmargin,$y1,$p[0],
444
                0,'left',$debug);
445
 
446
            // Add CSIM for Legend if defined
447
            if( !empty($p[4]) ) {
448
 
449
                $xs = $x1 - $this->mark_abs_hsize ;
450
                $ys = $y1 + 1 ;
451
                $xe = $x1 + $aImg->GetTextWidth($p[0]) + $this->mark_abs_hsize + $this->xmargin ;
452
                $ye = $y1-$rowheight[$row]+1;
453
                $coords = "$xs,$ys,$xe,$y1,$xe,$ye,$xs,$ye";
454
                if( ! empty($p[4]) ) {
455
                    $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".htmlentities($p[4])."\"";
456
 
457
                    if( !empty($p[6]) ) {
458
                        $this->csimareas .= " target=\"".$p[6]."\"";
459
                    }
460
 
461
                    if( !empty($p[5]) ) {
462
                        $tmp=sprintf($p[5],$p[0]);
463
                        $this->csimareas .= " title=\"$tmp\" alt=\"$tmp\" ";
464
                    }
465
                    $this->csimareas .= " />\n";
466
                }
467
            }
468
 
469
            if( $i >= $this->layout_n ) {
470
                $x1 = $xp+$this->xlmargin;
471
                $row++;
472
                if( !empty($rowheight[$row]) )
473
                    $y1 += $rowheight[$row];
474
                $i = 1;
475
            }
476
            else {
477
                $x1 += $colwidth[($i-1) % $numcolumns] ;
478
                ++$i;
479
            }
480
        }
481
    }
482
} // Class
483
 
484
?>