GanttScale   F
last analyzed

Complexity

Total Complexity 179

Size/Duplication

Total Lines 1221
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 658
dl 0
loc 1221
ccs 0
cts 860
cp 0
rs 1.942
c 1
b 0
f 0
wmc 179

46 Methods

Rating   Name   Duplication   Size   Complexity  
B GetMonthLabel() 0 36 8
A SetDateLocale() 0 3 1
A SetVertLayout() 0 3 1
A GetWeekNbr() 0 35 2
B GetHeaderHeight() 0 30 7
A IsDisplayDay() 0 3 1
A Stroke() 0 44 5
A IsDisplayWeek() 0 3 1
A GetVertSpacing() 0 3 1
A UseWeekendBackground() 0 3 1
A GetDayWidth() 0 3 1
A GetYear() 0 3 1
A IsDisplayMonth() 0 3 1
B NormalizeDate() 0 19 7
A ShowHeaders() 0 16 3
A GetHourWidth() 0 3 1
A GetMonthNbr() 0 3 1
A SetLabelWidth() 0 3 1
A GetMonthDayNbr() 0 3 1
C StrokeHours() 0 98 16
A GetNumDaysInYear() 0 7 2
A SetTableTitleBackground() 0 3 1
A __construct() 0 42 1
A IsDisplayMinute() 0 3 1
A IsRangeSet() 0 3 2
A TranslateVertPos() 0 21 4
A SetWeekStart() 0 6 1
F StrokeDays() 0 146 24
A AdjustStartEndDay() 0 32 6
A GetMinuteWidth() 0 3 1
A StrokeTableHeaders() 0 46 5
A TimeToMinutes() 0 15 2
B StrokeYears() 0 66 8
A SetVertSpacing() 0 3 1
A GetPlotWidth() 0 5 1
B StrokeMonths() 0 73 10
D StrokeMinutes() 0 97 18
A IsDisplayHour() 0 3 1
A GetNumDaysInMonth() 0 9 2
C StrokeWeeks() 0 78 16
A SetRange() 0 4 1
A GetYearDayNbr() 0 3 1
A GetNumberOfDays() 0 3 1
A IsDisplayYear() 0 3 1
A IsLeap() 0 11 4
A TranslateDate() 0 26 3

How to fix   Complexity   

Complex Class

Complex classes like GanttScale often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GanttScale, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * JPGraph v4.0.3
5
 */
6
7
namespace Amenadiel\JpGraph\Graph;
8
9
use Amenadiel\JpGraph\Text;
10
use Amenadiel\JpGraph\Util;
11
12
/**
13
 * @class GanttScale
14
 * // Description: Responsible for calculating and showing
15
 * // the scale in a gantt chart. This includes providing methods for
16
 * // converting dates to position in the chart as well as stroking the
17
 * // date headers (days, week, etc).
18
 */
19
class GanttScale
20
{
21
    public $minute;
22
    public $hour;
23
    public $day;
24
    public $week;
25
    public $month;
26
    public $year;
27
    public $divider;
28
    public $dividerh;
29
    public $tableTitle;
30
    public $iStartDate = -1;
31
    public $iEndDate   = -1;
32
    // Number of gantt bar position (n.b not necessariliy the same as the number of bars)
33
    // we could have on bar in position 1, and one bar in position 5 then there are two
34
    // bars but the number of bar positions is 5
35
    public $actinfo;
36
    public $iTopPlotMargin    = 10;
37
    public $iBottomPlotMargin = 15;
38
    public $iVertLines        = -1;
39
    public $iVertHeaderSize   = -1;
40
    // The width of the labels (defaults to the widest of all labels)
41
    private $iLabelWidth;
42
    // Out image to stroke the scale to
43
    private $iImg;
44
    private $iTableHeaderBackgroundColor = 'white';
45
    private $iTableHeaderFrameColor      = 'black';
46
    private $iTableHeaderFrameWeight     = 1;
47
    private $iAvailableHeight            = -1;
48
    private $iVertSpacing                = -1;
49
    private $iDateLocale;
50
    private $iVertLayout               = GANTT_EVEN;
51
    private $iUsePlotWeekendBackground = true;
52
    private $iWeekStart                = 1; // Default to have weekends start on Monday
53
54
    /**
55
     * CONSTRUCTOR.
56
     *
57
     * @param mixed $aImg
58
     */
59
    public function __construct($aImg)
60
    {
61
        $this->iImg        = $aImg;
62
        $this->iDateLocale = new Util\DateLocale();
63
64
        $this->minute = new HeaderProperty();
65
        $this->minute->SetIntervall(15);
66
        $this->minute->SetLabelFormatString('i');
67
        $this->minute->SetFont(FF_FONT0);
68
        $this->minute->grid->SetColor('gray');
69
70
        $this->hour = new HeaderProperty();
71
        $this->hour->SetFont(FF_FONT0);
72
        $this->hour->SetIntervall(6);
73
        $this->hour->SetStyle(HOURSTYLE_HM24);
74
        $this->hour->SetLabelFormatString('H:i');
75
        $this->hour->grid->SetColor('gray');
76
77
        $this->day = new HeaderProperty();
78
        $this->day->grid->SetColor('gray');
79
        $this->day->SetLabelFormatString('l');
80
81
        $this->week = new HeaderProperty();
82
        $this->week->SetLabelFormatString('w%d');
83
        $this->week->SetFont(FF_FONT1);
84
85
        $this->month = new HeaderProperty();
86
        $this->month->SetFont(FF_FONT1, FS_BOLD);
87
88
        $this->year = new HeaderProperty();
89
        $this->year->SetFont(FF_FONT1, FS_BOLD);
90
91
        $this->divider  = new LineProperty();
92
        $this->dividerh = new LineProperty();
93
        $this->dividerh->SetWeight(2);
94
        $this->divider->SetWeight(6);
95
        $this->divider->SetColor('gray');
96
        $this->divider->SetStyle('fancy');
97
98
        $this->tableTitle = new Text\TextProperty();
99
        $this->tableTitle->Show(false);
100
        $this->actinfo = new GanttActivityInfo();
101
    }
102
103
    /**
104
     * PUBLIC METHODS
105
     * // Specify what headers should be visible.
106
     *
107
     * @param mixed $aFlg
108
     */
109
    public function ShowHeaders($aFlg)
110
    {
111
        $this->day->Show($aFlg & GANTT_HDAY);
112
        $this->week->Show($aFlg & GANTT_HWEEK);
113
        $this->month->Show($aFlg & GANTT_HMONTH);
114
        $this->year->Show($aFlg & GANTT_HYEAR);
115
        $this->hour->Show($aFlg & GANTT_HHOUR);
116
        $this->minute->Show($aFlg & GANTT_HMIN);
117
118
        // Make some default settings of gridlines whihc makes sense
119
        if ($aFlg & GANTT_HWEEK) {
120
            $this->month->grid->Show(false);
121
            $this->year->grid->Show(false);
122
        }
123
        if ($aFlg & GANTT_HHOUR) {
124
            $this->day->grid->SetColor('black');
125
        }
126
    }
127
128
    // Should the weekend background stretch all the way down in the plotarea
129
    public function UseWeekendBackground($aShow)
130
    {
131
        $this->iUsePlotWeekendBackground = $aShow;
132
    }
133
134
    // Have a range been specified?
135
    public function IsRangeSet()
136
    {
137
        return $this->iStartDate != -1 && $this->iEndDate != -1;
138
    }
139
140
    // Should the layout be from top or even?
141
    public function SetVertLayout($aLayout)
142
    {
143
        $this->iVertLayout = $aLayout;
144
    }
145
146
    // Which locale should be used?
147
    public function SetDateLocale($aLocale)
148
    {
149
        $this->iDateLocale->Set($aLocale);
150
    }
151
152
    // Number of days we are showing
153
    public function GetNumberOfDays()
154
    {
155
        return round(($this->iEndDate - $this->iStartDate) / SECPERDAY);
156
    }
157
158
    // The width of the actual plot area
159
    public function GetPlotWidth()
160
    {
161
        $img = $this->iImg;
162
163
        return $img->width - $img->left_margin - $img->right_margin;
164
    }
165
166
    // Specify the width of the titles(labels) for the activities
167
    // (This is by default set to the minimum width enought for the
168
    // widest title)
169
    public function SetLabelWidth($aLabelWidth)
170
    {
171
        $this->iLabelWidth = $aLabelWidth;
172
    }
173
174
    // Which day should the week start?
175
    // 0==Sun, 1==Monday, 2==Tuesday etc
176
    public function SetWeekStart($aStartDay)
177
    {
178
        $this->iWeekStart = $aStartDay % 7;
179
180
        //Recalculate the startday since this will change the week start
181
        $this->SetRange($this->iStartDate, $this->iEndDate);
182
    }
183
184
    // Do we show min scale?
185
    public function IsDisplayMinute()
186
    {
187
        return $this->minute->iShowLabels;
188
    }
189
190
    // Do we show day scale?
191
    public function IsDisplayHour()
192
    {
193
        return $this->hour->iShowLabels;
194
    }
195
196
    // Do we show day scale?
197
    public function IsDisplayDay()
198
    {
199
        return $this->day->iShowLabels;
200
    }
201
202
    // Do we show week scale?
203
    public function IsDisplayWeek()
204
    {
205
        return $this->week->iShowLabels;
206
    }
207
208
    // Do we show month scale?
209
    public function IsDisplayMonth()
210
    {
211
        return $this->month->iShowLabels;
212
    }
213
214
    // Do we show year scale?
215
    public function IsDisplayYear()
216
    {
217
        return $this->year->iShowLabels;
218
    }
219
220
    // Specify spacing (in percent of bar height) between activity bars
221
    public function SetVertSpacing($aSpacing)
222
    {
223
        $this->iVertSpacing = $aSpacing;
224
    }
225
226
    // Specify scale min and max date either as timestamp or as date strings
227
    // Always round to the nearest week boundary
228
    public function SetRange($aMin, $aMax)
229
    {
230
        $this->iStartDate = $this->NormalizeDate($aMin);
231
        $this->iEndDate   = $this->NormalizeDate($aMax);
232
    }
233
234
    // Adjust the start and end date so they fit to beginning/ending
235
    // of the week taking the specified week start day into account.
236
    public function AdjustStartEndDay()
237
    {
238
        if (!($this->IsDisplayYear() || $this->IsDisplayMonth() || $this->IsDisplayWeek())) {
239
            // Don't adjust
240
            return;
241
        }
242
243
        // Get day in week for start and ending date (Sun==0)
244
        $ds = strftime('%w', $this->iStartDate);
245
        $de = strftime('%w', $this->iEndDate);
246
247
        // We want to start on iWeekStart day. But first we subtract a week
248
        // if the startdate is "behind" the day the week start at.
249
        // This way we ensure that the given start date is always included
250
        // in the range. If we don't do this the nearest correct weekday in the week
251
        // to start at might be later than the start date.
252
        if ($ds < $this->iWeekStart) {
253
            $d = strtotime('-7 day', $this->iStartDate);
254
        } else {
255
            $d = $this->iStartDate;
256
        }
257
258
        $adjdate          = strtotime(($this->iWeekStart - $ds) . ' day', $d/*$this->iStartDate*/);
259
        $this->iStartDate = $adjdate;
260
261
        // We want to end on the last day of the week
262
        $preferredEndDay = ($this->iWeekStart + 6) % 7;
263
        if ($preferredEndDay != $de) {
264
            // Solve equivalence eq:    $de + x ~ $preferredDay (mod 7)
265
            $adj            = (7 + ($preferredEndDay - $de)) % 7;
266
            $adjdate        = strtotime("+${adj} day", $this->iEndDate);
267
            $this->iEndDate = $adjdate;
268
        }
269
    }
270
271
    // Specify background for the table title area (upper left corner of the table)
272
    public function SetTableTitleBackground($aColor)
273
    {
274
        $this->iTableHeaderBackgroundColor = $aColor;
275
    }
276
277
    ///////////////////////////////////////
278
    // PRIVATE Methods
279
280
    // Determine the height of all the scale headers combined
281
    public function GetHeaderHeight()
282
    {
283
        $img    = $this->iImg;
284
        $height = 1;
285
        if ($this->minute->iShowLabels) {
286
            $height += $this->minute->GetFontHeight($img);
287
            $height += $this->minute->iTitleVertMargin;
288
        }
289
        if ($this->hour->iShowLabels) {
290
            $height += $this->hour->GetFontHeight($img);
291
            $height += $this->hour->iTitleVertMargin;
292
        }
293
        if ($this->day->iShowLabels) {
294
            $height += $this->day->GetFontHeight($img);
295
            $height += $this->day->iTitleVertMargin;
296
        }
297
        if ($this->week->iShowLabels) {
298
            $height += $this->week->GetFontHeight($img);
299
            $height += $this->week->iTitleVertMargin;
300
        }
301
        if ($this->month->iShowLabels) {
302
            $height += $this->month->GetFontHeight($img);
303
            $height += $this->month->iTitleVertMargin;
304
        }
305
        if ($this->year->iShowLabels) {
306
            $height += $this->year->GetFontHeight($img);
307
            $height += $this->year->iTitleVertMargin;
308
        }
309
310
        return $height;
311
    }
312
313
    // Get width (in pixels) for a single day
314
    public function GetDayWidth()
315
    {
316
        return ($this->GetPlotWidth() - $this->iLabelWidth + 1) / $this->GetNumberOfDays();
317
    }
318
319
    // Get width (in pixels) for a single hour
320
    public function GetHourWidth()
321
    {
322
        return $this->GetDayWidth() / 24;
323
    }
324
325
    public function GetMinuteWidth()
326
    {
327
        return $this->GetHourWidth() / 60;
328
    }
329
330
    // Nuber of days in a year
331
    public function GetNumDaysInYear($aYear)
332
    {
333
        if ($this->IsLeap($aYear)) {
334
            return 366;
335
        }
336
337
        return 365;
338
    }
339
340
    // Get week number
341
    public function GetWeekNbr($aDate, $aSunStart = true)
342
    {
343
        // We can't use the internal strftime() since it gets the weeknumber
344
        // wrong since it doesn't follow ISO on all systems since this is
345
        // system linrary dependent.
346
        // Even worse is that this works differently if we are on a Windows
347
        // or UNIX box (it even differs between UNIX boxes how strftime()
348
        // is natively implemented)
349
        //
350
        // Credit to Nicolas Hoizey <[email protected]> for this elegant
351
        // version of Week Nbr calculation.
352
353
        $day = $this->NormalizeDate($aDate);
354
        if ($aSunStart) {
355
            $day += 60 * 60 * 24;
356
        }
357
358
        /**
359
         * According to ISO-8601 :
360
         * "Week 01 of a year is per definition the first week that has the Thursday in this year,
361
         * which is equivalent to the week that contains the fourth day of January.
362
         * In other words, the first week of a new year is the week that has the majority of its
363
         * days in the new year.".
364
         *
365
         * Be carefull, with PHP, -3 % 7 = -3, instead of 4 !!!
366
         *
367
         * day of year             = date("z", $day) + 1
368
         * offset to thursday      = 3 - (date("w", $day) + 6) % 7
369
         * first thursday of year  = 1 + (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $day)))) % 7
370
         * week number             = (thursday's day of year - first thursday's day of year) / 7 + 1
371
         * ---------------------------------------------------------------------------*/
372
        $thursday = $day + 60 * 60 * 24 * (3 - (date('w', $day) + 6) % 7); // take week's thursday
0 ignored issues
show
Bug introduced by
It seems like $day can also be of type double and false; however, parameter $timestamp of date() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

372
        $thursday = $day + 60 * 60 * 24 * (3 - (date('w', /** @scrutinizer ignore-type */ $day) + 6) % 7); // take week's thursday
Loading history...
373
        $week     = 1 + (date('z', $thursday) - (11 - date('w', mktime(0, 0, 0, 1, 1, date('Y', $thursday)))) % 7) / 7;
0 ignored issues
show
Bug introduced by
date('Y', $thursday) of type string is incompatible with the type integer expected by parameter $year of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

373
        $week     = 1 + (date('z', $thursday) - (11 - date('w', mktime(0, 0, 0, 1, 1, /** @scrutinizer ignore-type */ date('Y', $thursday)))) % 7) / 7;
Loading history...
Bug introduced by
$thursday of type double is incompatible with the type integer|null expected by parameter $timestamp of date(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

373
        $week     = 1 + (date('z', /** @scrutinizer ignore-type */ $thursday) - (11 - date('w', mktime(0, 0, 0, 1, 1, date('Y', $thursday)))) % 7) / 7;
Loading history...
374
375
        return $week;
376
    }
377
378
    // Is year a leap year?
379
    public function IsLeap($aYear)
380
    {
381
        // Is the year a leap year?
382
        //$year = 0+date("Y",$aDate);
383
        if ($aYear % 4 == 0) {
384
            if (!($aYear % 100 == 0) || ($aYear % 400 == 0)) {
385
                return true;
386
            }
387
        }
388
389
        return false;
390
    }
391
392
    // Get current year
393
    public function GetYear($aDate)
394
    {
395
        return 0 + date('Y', $aDate);
396
    }
397
398
    // Return number of days in a year
399
    public function GetNumDaysInMonth($aMonth, $aYear)
400
    {
401
        $days  = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
402
        $daysl = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
403
        if ($this->IsLeap($aYear)) {
404
            return $daysl[$aMonth];
405
        }
406
407
        return $days[$aMonth];
408
    }
409
410
    // Get day in month
411
    public function GetMonthDayNbr($aDate)
412
    {
413
        return 0 + strftime('%d', $aDate);
414
    }
415
416
    // Get day in year
417
    public function GetYearDayNbr($aDate)
418
    {
419
        return 0 + strftime('%j', $aDate);
420
    }
421
422
    // Get month number
423
    public function GetMonthNbr($aDate)
424
    {
425
        return 0 + strftime('%m', $aDate);
426
    }
427
428
    // Translate a date to screen coordinates (horizontal scale)
429
    public function TranslateDate($aDate)
430
    {
431
        //
432
        // In order to handle the problem with Daylight savings time
433
        // the scale written with equal number of seconds per day beginning
434
        // with the start date. This means that we "cement" the state of
435
        // DST as it is in the start date. If later the scale includes the
436
        // switchover date (depends on the locale) we need to adjust back
437
        // if the date we try to translate has a different DST status since
438
        // we would otherwise be off by one hour.
439
        $aDate  = $this->NormalizeDate($aDate);
440
        $tmp    = localtime($aDate);
0 ignored issues
show
Bug introduced by
It seems like $aDate can also be of type double and false; however, parameter $timestamp of localtime() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

440
        $tmp    = localtime(/** @scrutinizer ignore-type */ $aDate);
Loading history...
441
        $cloc   = $tmp[8];
442
        $tmp    = localtime($this->iStartDate);
443
        $sloc   = $tmp[8];
444
        $offset = 0;
445
        if ($sloc != $cloc) {
446
            if ($sloc) {
447
                $offset = 3600;
448
            } else {
449
                $offset = -3600;
450
            }
451
        }
452
        $img = $this->iImg;
453
454
        return ($aDate - $this->iStartDate - $offset) / SECPERDAY * $this->GetDayWidth() + $img->left_margin + $this->iLabelWidth;
455
    }
456
457
    // Get screen coordinatesz for the vertical position for a bar
458
    public function TranslateVertPos($aPos, $atTop = false)
459
    {
460
        $img = $this->iImg;
461
        if ($aPos > $this->iVertLines) {
462
            Util\JpGraphError::RaiseL(6015, $aPos);
463
        }
464
465
        // 'Illegal vertical position %d'
466
        if ($this->iVertLayout == GANTT_EVEN) {
467
            // Position the top bar at 1 vert spacing from the scale
468
            $pos = round($img->top_margin + $this->iVertHeaderSize + ($aPos + 1) * $this->iVertSpacing);
469
        } else {
470
            // position the top bar at 1/2 a vert spacing from the scale
471
            $pos = round($img->top_margin + $this->iVertHeaderSize + $this->iTopPlotMargin + ($aPos + 1) * $this->iVertSpacing);
472
        }
473
474
        if ($atTop) {
475
            $pos -= $this->iVertSpacing;
476
        }
477
478
        return $pos;
479
    }
480
481
    // What is the vertical spacing?
482
    public function GetVertSpacing()
483
    {
484
        return $this->iVertSpacing;
485
    }
486
487
    // Convert a date to timestamp
488
    public function NormalizeDate($aDate)
489
    {
490
        if ($aDate === false) {
491
            return false;
492
        }
493
494
        if (is_string($aDate)) {
495
            $t = strtotime($aDate);
496
            if ($t === false || $t === -1) {
497
                Util\JpGraphError::RaiseL(6016, $aDate);
498
                //("Date string ($aDate) specified for Gantt activity can not be interpretated. Please make sure it is a valid time string, e.g. 2005-04-23 13:30");
499
            }
500
501
            return $t;
502
        }
503
        if (is_int($aDate) || is_float($aDate)) {
504
            return $aDate;
505
        }
506
        Util\JpGraphError::RaiseL(6017, $aDate);
507
508
        //Unknown date format in GanttScale ($aDate).");
509
    }
510
511
    // Convert a time string to minutes
512
513
    public function TimeToMinutes($aTimeString)
514
    {
515
        // Split in hours and minutes
516
        $pos    = strpos($aTimeString, ':');
517
        $minint = 60;
518
        if ($pos === false) {
519
            $hourint = $aTimeString;
520
            $minint  = 0;
521
        } else {
522
            $hourint = floor(substr($aTimeString, 0, $pos));
0 ignored issues
show
Bug introduced by
substr($aTimeString, 0, $pos) of type string is incompatible with the type double|integer expected by parameter $num of floor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

522
            $hourint = floor(/** @scrutinizer ignore-type */ substr($aTimeString, 0, $pos));
Loading history...
523
            $minint  = floor(substr($aTimeString, $pos + 1));
524
        }
525
        $minint += 60 * $hourint;
526
527
        return $minint;
528
    }
529
530
    // Stroke the day scale (including gridlines)
531
    public function StrokeMinutes($aYCoord, $getHeight = false)
532
    {
533
        $img = $this->iImg;
534
        $xt  = $img->left_margin + $this->iLabelWidth;
535
        $yt  = $aYCoord + $img->top_margin;
536
        if ($this->minute->iShowLabels) {
537
            $img->SetFont($this->minute->iFFamily, $this->minute->iFStyle, $this->minute->iFSize);
538
            $yb = $yt + $img->GetFontHeight() +
539
            $this->minute->iTitleVertMargin + $this->minute->iFrameWeight;
540
            if ($getHeight) {
541
                return $yb - $img->top_margin;
542
            }
543
            $xb = $img->width - $img->right_margin + 1;
544
            $img->SetColor($this->minute->iBackgroundColor);
545
            $img->FilledRectangle($xt, $yt, $xb, $yb);
546
547
            $x = $xt;
548
            $img->SetTextAlign('center');
549
            $day    = date('w', $this->iStartDate);
550
            $minint = $this->minute->GetIntervall();
551
552
            if (60 % $minint !== 0) {
553
                Util\JpGraphError::RaiseL(6018, $minint);
554
                //'Intervall for minutes must divide the hour evenly, e.g. 1,5,10,12,15,20,30 etc You have specified an intervall of '.$minint.' minutes.');
555
            }
556
557
            $n         = 60 / $minint;
558
            $datestamp = $this->iStartDate;
559
            $width     = $this->GetHourWidth() / $n;
560
            if ($width < 8) {
561
                // TO small width to draw minute scale
562
                Util\JpGraphError::RaiseL(6019, $width);
563
                //('The available width ('.$width.') for minutes are to small for this scale to be displayed. Please use auto-sizing or increase the width of the graph.');
564
            }
565
566
            $nh = ceil(24 * 60 / $this->TimeToMinutes($this->hour->GetIntervall()));
567
            $nd = $this->GetNumberOfDays();
568
            // Convert to intervall to seconds
569
            $minint *= 60;
570
            for ($j = 0; $j < $nd; ++$j, ++$day, $day %= 7) {
571
                for ($k = 0; $k < $nh; ++$k) {
572
                    for ($i = 0; $i < $n; ++$i, $x += $width, $datestamp += $minint) {
573
                        if ($day == 6 || $day == 0) {
574
                            $img->PushColor($this->day->iWeekendBackgroundColor);
575
                            if ($this->iUsePlotWeekendBackground) {
576
                                $img->FilledRectangle($x, $yt + $this->day->iFrameWeight, $x + $width, $img->height - $img->bottom_margin);
577
                            } else {
578
                                $img->FilledRectangle($x, $yt + $this->day->iFrameWeight, $x + $width, $yb - $this->day->iFrameWeight);
579
                            }
580
581
                            $img->PopColor();
582
                        }
583
584
                        if ($day == 0) {
585
                            $img->SetColor($this->day->iSundayTextColor);
586
                        } else {
587
                            $img->SetColor($this->day->iTextColor);
588
                        }
589
590
                        switch ($this->minute->iStyle) {
591
                            case MINUTESTYLE_CUSTOM:
592
                                $txt = date($this->minute->iLabelFormStr, $datestamp);
593
594
                                break;
595
                            case MINUTESTYLE_MM:
596
                            default:
597
                                // 15
598
                                $txt = date('i', $datestamp);
599
600
                                break;
601
                        }
602
                        $img->StrokeText(round($x + $width / 2), round($yb - $this->minute->iTitleVertMargin), $txt);
603
604
                        // Fix a rounding problem the wrong way ..
605
                        // If we also have hour scale then don't draw the firsta or last
606
                        // gridline since that will be overwritten by the hour scale gridline if such exists.
607
                        // However, due to the propagation of rounding of the 'x+=width' term in the loop
608
                        // this might sometimes be one pixel of so we fix this by not drawing it.
609
                        // The proper way to fix it would be to re-calculate the scale for each step and
610
                        // not using the additive term.
611
                        if (!(($i == $n || $i == 0) && $this->hour->iShowLabels && $this->hour->grid->iShow)) {
612
                            $img->SetColor($this->minute->grid->iColor);
613
                            $img->SetLineWeight($this->minute->grid->iWeight);
614
                            $img->Line($x, $yt, $x, $yb);
615
                            $this->minute->grid->Stroke($img, $x, $yb, $x, $img->height - $img->bottom_margin);
616
                        }
617
                    }
618
                }
619
            }
620
            $img->SetColor($this->minute->iFrameColor);
621
            $img->SetLineWeight($this->minute->iFrameWeight);
622
            $img->Rectangle($xt, $yt, $xb, $yb);
623
624
            return $yb - $img->top_margin;
625
        }
626
627
        return $aYCoord;
628
    }
629
630
    // Stroke the day scale (including gridlines)
631
    public function StrokeHours($aYCoord, $getHeight = false)
632
    {
633
        $img = $this->iImg;
634
        $xt  = $img->left_margin + $this->iLabelWidth;
635
        $yt  = $aYCoord + $img->top_margin;
636
        if ($this->hour->iShowLabels) {
637
            $img->SetFont($this->hour->iFFamily, $this->hour->iFStyle, $this->hour->iFSize);
638
            $yb = $yt + $img->GetFontHeight() +
639
            $this->hour->iTitleVertMargin + $this->hour->iFrameWeight;
640
            if ($getHeight) {
641
                return $yb - $img->top_margin;
642
            }
643
            $xb = $img->width - $img->right_margin + 1;
644
            $img->SetColor($this->hour->iBackgroundColor);
645
            $img->FilledRectangle($xt, $yt, $xb, $yb);
646
647
            $x = $xt;
648
            $img->SetTextAlign('center');
649
            $tmp    = $this->hour->GetIntervall();
650
            $minint = $this->TimeToMinutes($tmp);
651
            if (1440 % $minint !== 0) {
652
                Util\JpGraphError::RaiseL(6020, $tmp);
653
                //('Intervall for hours must divide the day evenly, e.g. 0:30, 1:00, 1:30, 4:00 etc. You have specified an intervall of '.$tmp);
654
            }
655
656
            $n         = ceil(24 * 60 / $minint);
657
            $datestamp = $this->iStartDate;
658
            $day       = date('w', $this->iStartDate);
659
            $doback    = !$this->minute->iShowLabels;
660
            $width     = $this->GetDayWidth() / $n;
661
            for ($j = 0; $j < $this->GetNumberOfDays(); ++$j, ++$day, $day %= 7) {
662
                for ($i = 0; $i < $n; ++$i, $x += $width) {
663
                    if ($day == 6 || $day == 0) {
664
                        $img->PushColor($this->day->iWeekendBackgroundColor);
665
                        if ($this->iUsePlotWeekendBackground && $doback) {
666
                            $img->FilledRectangle($x, $yt + $this->day->iFrameWeight, $x + $width, $img->height - $img->bottom_margin);
667
                        } else {
668
                            $img->FilledRectangle($x, $yt + $this->day->iFrameWeight, $x + $width, $yb - $this->day->iFrameWeight);
669
                        }
670
671
                        $img->PopColor();
672
                    }
673
674
                    if ($day == 0) {
675
                        $img->SetColor($this->day->iSundayTextColor);
676
                    } else {
677
                        $img->SetColor($this->day->iTextColor);
678
                    }
679
680
                    switch ($this->hour->iStyle) {
681
                        case HOURSTYLE_HMAMPM:
682
                            // 1:35pm
683
                            $txt = date('g:ia', $datestamp);
684
685
                            break;
686
                        case HOURSTYLE_H24:
687
                            // 13
688
                            $txt = date('H', $datestamp);
689
690
                            break;
691
                        case HOURSTYLE_HAMPM:
692
                            $txt = date('ga', $datestamp);
693
694
                            break;
695
                        case HOURSTYLE_CUSTOM:
696
                            $txt = date($this->hour->iLabelFormStr, $datestamp);
697
698
                            break;
699
                        case HOURSTYLE_HM24:
700
                        default:
701
                            $txt = date('H:i', $datestamp);
702
703
                            break;
704
                    }
705
                    $img->StrokeText(round($x + $width / 2), round($yb - $this->hour->iTitleVertMargin), $txt);
706
                    $img->SetColor($this->hour->grid->iColor);
707
                    $img->SetLineWeight($this->hour->grid->iWeight);
708
                    $img->Line($x, $yt, $x, $yb);
709
                    $this->hour->grid->Stroke($img, $x, $yb, $x, $img->height - $img->bottom_margin);
710
                    //$datestamp += $minint*60
711
                    $datestamp = mktime(
712
                        date('H', $datestamp),
0 ignored issues
show
Bug introduced by
date('H', $datestamp) of type string is incompatible with the type integer expected by parameter $hour of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

712
                        /** @scrutinizer ignore-type */ date('H', $datestamp),
Loading history...
713
                        date('i', $datestamp) + $minint,
0 ignored issues
show
Bug introduced by
date('i', $datestamp) + $minint of type double is incompatible with the type integer expected by parameter $minute of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

713
                        /** @scrutinizer ignore-type */ date('i', $datestamp) + $minint,
Loading history...
714
                        0,
715
                        date('m', $datestamp),
0 ignored issues
show
Bug introduced by
date('m', $datestamp) of type string is incompatible with the type integer expected by parameter $month of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

715
                        /** @scrutinizer ignore-type */ date('m', $datestamp),
Loading history...
716
                        date('d', $datestamp) + 1,
717
                        date('Y', $datestamp)
0 ignored issues
show
Bug introduced by
date('Y', $datestamp) of type string is incompatible with the type integer expected by parameter $year of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

717
                        /** @scrutinizer ignore-type */ date('Y', $datestamp)
Loading history...
718
                    );
719
                }
720
            }
721
            $img->SetColor($this->hour->iFrameColor);
722
            $img->SetLineWeight($this->hour->iFrameWeight);
723
            $img->Rectangle($xt, $yt, $xb, $yb);
724
725
            return $yb - $img->top_margin;
726
        }
727
728
        return $aYCoord;
729
    }
730
731
    // Stroke the day scale (including gridlines)
732
    public function StrokeDays($aYCoord, $getHeight = false)
733
    {
734
        $img      = $this->iImg;
735
        $daywidth = $this->GetDayWidth();
736
        $xt       = $img->left_margin + $this->iLabelWidth;
737
        $yt       = $aYCoord + $img->top_margin;
738
        if ($this->day->iShowLabels) {
739
            $img->SetFont($this->day->iFFamily, $this->day->iFStyle, $this->day->iFSize);
740
            $yb = $yt + $img->GetFontHeight() + $this->day->iTitleVertMargin + $this->day->iFrameWeight;
741
            if ($getHeight) {
742
                return $yb - $img->top_margin;
743
            }
744
            $xb = $img->width - $img->right_margin + 1;
745
            $img->SetColor($this->day->iBackgroundColor);
746
            $img->FilledRectangle($xt, $yt, $xb, $yb);
747
748
            $x = $xt;
749
            $img->SetTextAlign('center');
750
            $day       = date('w', $this->iStartDate);
751
            $datestamp = $this->iStartDate;
752
753
            $doback = !($this->hour->iShowLabels || $this->minute->iShowLabels);
754
755
            setlocale(LC_TIME, $this->iDateLocale->iLocale);
756
757
            for ($i = 0; $i < $this->GetNumberOfDays(); ++$i, $x += $daywidth, ++$day, $day %= 7) {
758
                if ($day == 6 || $day == 0) {
759
                    $img->SetColor($this->day->iWeekendBackgroundColor);
760
                    if ($this->iUsePlotWeekendBackground && $doback) {
761
                        $img->FilledRectangle(
762
                            $x,
763
                            $yt + $this->day->iFrameWeight,
764
                            $x + $daywidth,
765
                            $img->height - $img->bottom_margin
766
                        );
767
                    } else {
768
                        $img->FilledRectangle(
769
                            $x,
770
                            $yt + $this->day->iFrameWeight,
771
                            $x + $daywidth,
772
                            $yb - $this->day->iFrameWeight
773
                        );
774
                    }
775
                }
776
777
                $mn = strftime('%m', $datestamp);
778
                if ($mn[0] == '0') {
779
                    $mn = $mn[1];
780
                }
781
782
                switch ($this->day->iStyle) {
783
                    case DAYSTYLE_LONG:
784
                        // "Monday"
785
                        $txt = strftime('%A', $datestamp);
786
787
                        break;
788
                    case DAYSTYLE_SHORT:
789
                        // "Mon"
790
                        $txt = strftime('%a', $datestamp);
791
792
                        break;
793
                    case DAYSTYLE_SHORTDAYDATE1:
794
                        // "Mon 23/6"
795
                        $txt = strftime('%a %d/' . $mn, $datestamp);
796
797
                        break;
798
                    case DAYSTYLE_SHORTDAYDATE2:
799
                        // "Mon 23 Jun"
800
                        $txt = strftime('%a %d %b', $datestamp);
801
802
                        break;
803
                    case DAYSTYLE_SHORTDAYDATE3:
804
                        // "Mon 23 Jun 2003"
805
                        $txt = strftime('%a %d %b %Y', $datestamp);
806
807
                        break;
808
                    case DAYSTYLE_LONGDAYDATE1:
809
                        // "Monday 23 Jun"
810
                        $txt = strftime('%A %d %b', $datestamp);
811
812
                        break;
813
                    case DAYSTYLE_LONGDAYDATE2:
814
                        // "Monday 23 Jun 2003"
815
                        $txt = strftime('%A %d %b %Y', $datestamp);
816
817
                        break;
818
                    case DAYSTYLE_SHORTDATE1:
819
                        // "23/6"
820
                        $txt = strftime('%d/' . $mn, $datestamp);
821
822
                        break;
823
                    case DAYSTYLE_SHORTDATE2:
824
                        // "23 Jun"
825
                        $txt = strftime('%d %b', $datestamp);
826
827
                        break;
828
                    case DAYSTYLE_SHORTDATE3:
829
                        // "Mon 23"
830
                        $txt = strftime('%a %d', $datestamp);
831
832
                        break;
833
                    case DAYSTYLE_SHORTDATE4:
834
                        // "23"
835
                        $txt = strftime('%d', $datestamp);
836
837
                        break;
838
                    case DAYSTYLE_CUSTOM:
839
                        // Custom format
840
                        $txt = strftime($this->day->iLabelFormStr, $datestamp);
841
842
                        break;
843
                    case DAYSTYLE_ONELETTER:
844
                    default:
845
                        // "M"
846
                        $txt = strftime('%A', $datestamp);
847
                        $txt = strtoupper($txt[0]);
848
849
                        break;
850
                }
851
852
                if ($day == 0) {
853
                    $img->SetColor($this->day->iSundayTextColor);
854
                } else {
855
                    $img->SetColor($this->day->iTextColor);
856
                }
857
858
                $img->StrokeText(
859
                    round($x + $daywidth / 2 + 1),
860
                    round($yb - $this->day->iTitleVertMargin),
861
                    $txt
862
                );
863
                $img->SetColor($this->day->grid->iColor);
864
                $img->SetLineWeight($this->day->grid->iWeight);
865
                $img->Line($x, $yt, $x, $yb);
866
                $this->day->grid->Stroke($img, $x, $yb, $x, $img->height - $img->bottom_margin);
867
                $datestamp = mktime(0, 0, 0, date('m', $datestamp), date('d', $datestamp) + 1, date('Y', $datestamp));
0 ignored issues
show
Bug introduced by
date('Y', $datestamp) of type string is incompatible with the type integer expected by parameter $year of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

867
                $datestamp = mktime(0, 0, 0, date('m', $datestamp), date('d', $datestamp) + 1, /** @scrutinizer ignore-type */ date('Y', $datestamp));
Loading history...
Bug introduced by
date('m', $datestamp) of type string is incompatible with the type integer expected by parameter $month of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

867
                $datestamp = mktime(0, 0, 0, /** @scrutinizer ignore-type */ date('m', $datestamp), date('d', $datestamp) + 1, date('Y', $datestamp));
Loading history...
868
                //$datestamp += SECPERDAY;
869
            }
870
            $img->SetColor($this->day->iFrameColor);
871
            $img->SetLineWeight($this->day->iFrameWeight);
872
            $img->Rectangle($xt, $yt, $xb, $yb);
873
874
            return $yb - $img->top_margin;
875
        }
876
877
        return $aYCoord;
878
    }
879
880
    // Stroke week header and grid
881
    public function StrokeWeeks($aYCoord, $getHeight = false)
882
    {
883
        if ($this->week->iShowLabels) {
884
            $img = $this->iImg;
885
            $yt  = $aYCoord + $img->top_margin;
886
            $img->SetFont($this->week->iFFamily, $this->week->iFStyle, $this->week->iFSize);
887
            $yb = $yt + $img->GetFontHeight() + $this->week->iTitleVertMargin + $this->week->iFrameWeight;
888
889
            if ($getHeight) {
890
                return $yb - $img->top_margin;
891
            }
892
893
            $xt        = $img->left_margin + $this->iLabelWidth;
894
            $weekwidth = $this->GetDayWidth() * 7;
895
            $wdays     = $this->iDateLocale->GetDayAbb();
0 ignored issues
show
Unused Code introduced by
The assignment to $wdays is dead and can be removed.
Loading history...
896
            $xb        = $img->width - $img->right_margin + 1;
897
            $week      = $this->iStartDate;
898
            $weeknbr   = $this->GetWeekNbr($week);
899
            $img->SetColor($this->week->iBackgroundColor);
900
            $img->FilledRectangle($xt, $yt, $xb, $yb);
901
            $img->SetColor($this->week->grid->iColor);
902
            $x = $xt;
903
            if ($this->week->iStyle == WEEKSTYLE_WNBR) {
904
                $img->SetTextAlign('center');
905
                $txtOffset = $weekwidth / 2 + 1;
906
            } elseif ($this->week->iStyle == WEEKSTYLE_FIRSTDAY ||
907
                $this->week->iStyle == WEEKSTYLE_FIRSTDAY2 ||
908
                $this->week->iStyle == WEEKSTYLE_FIRSTDAYWNBR ||
909
                $this->week->iStyle == WEEKSTYLE_FIRSTDAY2WNBR) {
910
                $img->SetTextAlign('left');
911
                $txtOffset = 3;
912
            } else {
913
                Util\JpGraphError::RaiseL(6021);
914
                //("Unknown formatting style for week.");
915
            }
916
917
            for ($i = 0; $i < $this->GetNumberOfDays() / 7; ++$i, $x += $weekwidth) {
918
                $img->PushColor($this->week->iTextColor);
919
920
                if ($this->week->iStyle == WEEKSTYLE_WNBR) {
921
                    $txt = sprintf($this->week->iLabelFormStr, $weeknbr);
922
                } elseif ($this->week->iStyle == WEEKSTYLE_FIRSTDAY ||
923
                    $this->week->iStyle == WEEKSTYLE_FIRSTDAYWNBR) {
924
                    $txt = date('j/n', $week);
925
                } elseif ($this->week->iStyle == WEEKSTYLE_FIRSTDAY2 ||
926
                    $this->week->iStyle == WEEKSTYLE_FIRSTDAY2WNBR) {
927
                    $monthnbr   = date('n', $week) - 1;
928
                    $shortmonth = $this->iDateLocale->GetShortMonthName($monthnbr);
929
                    $txt        = date('j', $week) . ' ' . $shortmonth;
930
                }
931
932
                if ($this->week->iStyle == WEEKSTYLE_FIRSTDAYWNBR ||
933
                    $this->week->iStyle == WEEKSTYLE_FIRSTDAY2WNBR) {
934
                    $w = sprintf($this->week->iLabelFormStr, $weeknbr);
935
                    $txt .= ' ' . $w;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $txt does not seem to be defined for all execution paths leading up to this point.
Loading history...
936
                }
937
938
                $img->StrokeText(
939
                    round($x + $txtOffset),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $txtOffset does not seem to be defined for all execution paths leading up to this point.
Loading history...
940
                    round($yb - $this->week->iTitleVertMargin),
941
                    $txt
942
                );
943
944
                $week    = strtotime('+7 day', $week);
945
                $weeknbr = $this->GetWeekNbr($week);
946
                $img->PopColor();
947
                $img->SetLineWeight($this->week->grid->iWeight);
948
                $img->Line($x, $yt, $x, $yb);
949
                $this->week->grid->Stroke($img, $x, $yb, $x, $img->height - $img->bottom_margin);
950
            }
951
            $img->SetColor($this->week->iFrameColor);
952
            $img->SetLineWeight($this->week->iFrameWeight);
953
            $img->Rectangle($xt, $yt, $xb, $yb);
954
955
            return $yb - $img->top_margin;
956
        }
957
958
        return $aYCoord;
959
    }
960
961
    // Format the mont scale header string
962
    public function GetMonthLabel($aMonthNbr, $year)
963
    {
964
        $sn = $this->iDateLocale->GetShortMonthName($aMonthNbr);
965
        $ln = $this->iDateLocale->GetLongMonthName($aMonthNbr);
966
        switch ($this->month->iStyle) {
967
            case MONTHSTYLE_SHORTNAME:
968
                $m = $sn;
969
970
                break;
971
            case MONTHSTYLE_LONGNAME:
972
                $m = $ln;
973
974
                break;
975
            case MONTHSTYLE_SHORTNAMEYEAR2:
976
                $m = $sn . " '" . substr('' . $year, 2);
977
978
                break;
979
            case MONTHSTYLE_SHORTNAMEYEAR4:
980
                $m = $sn . ' ' . $year;
981
982
                break;
983
            case MONTHSTYLE_LONGNAMEYEAR2:
984
                $m = $ln . " '" . substr('' . $year, 2);
985
986
                break;
987
            case MONTHSTYLE_LONGNAMEYEAR4:
988
                $m = $ln . ' ' . $year;
989
990
                break;
991
            case MONTHSTYLE_FIRSTLETTER:
992
                $m = $sn[0];
993
994
                break;
995
        }
996
997
        return $m;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $m does not seem to be defined for all execution paths leading up to this point.
Loading history...
998
    }
999
1000
    // Stroke month scale and gridlines
1001
    public function StrokeMonths($aYCoord, $getHeight = false)
1002
    {
1003
        if ($this->month->iShowLabels) {
1004
            $img = $this->iImg;
1005
            $img->SetFont($this->month->iFFamily, $this->month->iFStyle, $this->month->iFSize);
1006
            $yt = $aYCoord + $img->top_margin;
1007
            $yb = $yt + $img->GetFontHeight() + $this->month->iTitleVertMargin + $this->month->iFrameWeight;
1008
            if ($getHeight) {
1009
                return $yb - $img->top_margin;
1010
            }
1011
            $monthnbr = $this->GetMonthNbr($this->iStartDate) - 1;
1012
            $xt       = $img->left_margin + $this->iLabelWidth;
1013
            $xb       = $img->width - $img->right_margin + 1;
1014
1015
            $img->SetColor($this->month->iBackgroundColor);
1016
            $img->FilledRectangle($xt, $yt, $xb, $yb);
1017
1018
            $img->SetLineWeight($this->month->grid->iWeight);
1019
            $img->SetColor($this->month->iTextColor);
1020
            $year = 0 + strftime('%Y', $this->iStartDate);
1021
            $img->SetTextAlign('center');
1022
            if ($this->GetMonthNbr($this->iStartDate) == $this->GetMonthNbr($this->iEndDate)
1023
                && $this->GetYear($this->iStartDate) == $this->GetYear($this->iEndDate)) {
1024
                $monthwidth = $this->GetDayWidth() * ($this->GetMonthDayNbr($this->iEndDate) - $this->GetMonthDayNbr($this->iStartDate) + 1);
1025
            } else {
1026
                $monthwidth = $this->GetDayWidth() * ($this->GetNumDaysInMonth($monthnbr, $year) - $this->GetMonthDayNbr($this->iStartDate) + 1);
1027
            }
1028
            // Is it enough space to stroke the first month?
1029
            $monthName = $this->GetMonthLabel($monthnbr, $year);
1030
            if ($monthwidth >= 1.2 * $img->GetTextWidth($monthName)) {
1031
                $img->SetColor($this->month->iTextColor);
1032
                $img->StrokeText(
1033
                    round($xt + $monthwidth / 2 + 1),
1034
                    round($yb - $this->month->iTitleVertMargin),
1035
                    $monthName
1036
                );
1037
            }
1038
            $x = $xt + $monthwidth;
1039
            while ($x < $xb) {
1040
                $img->SetColor($this->month->grid->iColor);
1041
                $img->Line($x, $yt, $x, $yb);
1042
                $this->month->grid->Stroke($img, $x, $yb, $x, $img->height - $img->bottom_margin);
1043
                ++$monthnbr;
1044
                if ($monthnbr == 12) {
1045
                    $monthnbr = 0;
1046
                    ++$year;
1047
                }
1048
                $monthName  = $this->GetMonthLabel($monthnbr, $year);
1049
                $monthwidth = $this->GetDayWidth() * $this->GetNumDaysInMonth($monthnbr, $year);
1050
                if ($x + $monthwidth < $xb) {
1051
                    $w = $monthwidth;
1052
                } else {
1053
                    $w = $xb - $x;
1054
                }
1055
1056
                if ($w >= 1.2 * $img->GetTextWidth($monthName)) {
1057
                    $img->SetColor($this->month->iTextColor);
1058
                    $img->StrokeText(
1059
                        round($x + $w / 2 + 1),
1060
                        round($yb - $this->month->iTitleVertMargin),
1061
                        $monthName
1062
                    );
1063
                }
1064
                $x += $monthwidth;
1065
            }
1066
            $img->SetColor($this->month->iFrameColor);
1067
            $img->SetLineWeight($this->month->iFrameWeight);
1068
            $img->Rectangle($xt, $yt, $xb, $yb);
1069
1070
            return $yb - $img->top_margin;
1071
        }
1072
1073
        return $aYCoord;
1074
    }
1075
1076
    // Stroke year scale and gridlines
1077
    public function StrokeYears($aYCoord, $getHeight = false)
1078
    {
1079
        if ($this->year->iShowLabels) {
1080
            $img = $this->iImg;
1081
            $yt  = $aYCoord + $img->top_margin;
1082
            $img->SetFont($this->year->iFFamily, $this->year->iFStyle, $this->year->iFSize);
1083
            $yb = $yt + $img->GetFontHeight() + $this->year->iTitleVertMargin + $this->year->iFrameWeight;
1084
1085
            if ($getHeight) {
1086
                return $yb - $img->top_margin;
1087
            }
1088
1089
            $xb   = $img->width - $img->right_margin + 1;
1090
            $xt   = $img->left_margin + $this->iLabelWidth;
1091
            $year = $this->GetYear($this->iStartDate);
1092
            $img->SetColor($this->year->iBackgroundColor);
1093
            $img->FilledRectangle($xt, $yt, $xb, $yb);
1094
            $img->SetLineWeight($this->year->grid->iWeight);
1095
            $img->SetTextAlign('center');
1096
            if ($year == $this->GetYear($this->iEndDate)) {
1097
                $yearwidth = $this->GetDayWidth() * ($this->GetYearDayNbr($this->iEndDate) - $this->GetYearDayNbr($this->iStartDate) + 1);
1098
            } else {
1099
                $yearwidth = $this->GetDayWidth() * ($this->GetNumDaysInYear($year) - $this->GetYearDayNbr($this->iStartDate) + 1);
1100
            }
1101
1102
            // The space for a year must be at least 20% bigger than the actual text
1103
            // so we allow 10% margin on each side
1104
            if ($yearwidth >= 1.20 * $img->GetTextWidth('' . $year)) {
1105
                $img->SetColor($this->year->iTextColor);
1106
                $img->StrokeText(
1107
                    round($xt + $yearwidth / 2 + 1),
1108
                    round($yb - $this->year->iTitleVertMargin),
1109
                    $year
1110
                );
1111
            }
1112
            $x = $xt + $yearwidth;
1113
            while ($x < $xb) {
1114
                $img->SetColor($this->year->grid->iColor);
1115
                $img->Line($x, $yt, $x, $yb);
1116
                $this->year->grid->Stroke($img, $x, $yb, $x, $img->height - $img->bottom_margin);
1117
                ++$year;
1118
                $yearwidth = $this->GetDayWidth() * $this->GetNumDaysInYear($year);
1119
                if ($x + $yearwidth < $xb) {
1120
                    $w = $yearwidth;
1121
                } else {
1122
                    $w = $xb - $x;
1123
                }
1124
1125
                if ($w >= 1.2 * $img->GetTextWidth('' . $year)) {
1126
                    $img->SetColor($this->year->iTextColor);
1127
                    $img->StrokeText(
1128
                        round($x + $w / 2 + 1),
1129
                        round($yb - $this->year->iTitleVertMargin),
1130
                        $year
1131
                    );
1132
                }
1133
                $x += $yearwidth;
1134
            }
1135
            $img->SetColor($this->year->iFrameColor);
1136
            $img->SetLineWeight($this->year->iFrameWeight);
1137
            $img->Rectangle($xt, $yt, $xb, $yb);
1138
1139
            return $yb - $img->top_margin;
1140
        }
1141
1142
        return $aYCoord;
1143
    }
1144
1145
    // Stroke table title (upper left corner)
1146
    public function StrokeTableHeaders($aYBottom)
1147
    {
1148
        $img = $this->iImg;
1149
        $xt  = $img->left_margin;
1150
        $yt  = $img->top_margin;
1151
        $xb  = $xt + $this->iLabelWidth;
1152
        $yb  = $aYBottom + $img->top_margin;
1153
1154
        if ($this->tableTitle->iShow) {
1155
            $img->SetColor($this->iTableHeaderBackgroundColor);
1156
            $img->FilledRectangle($xt, $yt, $xb, $yb);
1157
            $this->tableTitle->Align('center', 'top');
1158
            $this->tableTitle->Stroke($img, $xt + ($xb - $xt) / 2 + 1, $yt + 2);
1159
            $img->SetColor($this->iTableHeaderFrameColor);
1160
            $img->SetLineWeight($this->iTableHeaderFrameWeight);
1161
            $img->Rectangle($xt, $yt, $xb, $yb);
1162
        }
1163
1164
        $this->actinfo->Stroke($img, $xt, $yt, $xb, $yb, $this->tableTitle->iShow);
1165
1166
        // Draw the horizontal dividing line
1167
        $this->dividerh->Stroke($img, $xt, $yb, $img->width - $img->right_margin, $yb);
1168
1169
        // Draw the vertical dividing line
1170
        // We do the width "manually" since we want the line only to grow
1171
        // to the left
1172
        $fancy = $this->divider->iStyle == 'fancy';
1173
        if ($fancy) {
1174
            $this->divider->iStyle = 'solid';
1175
        }
1176
1177
        $tmp                    = $this->divider->iWeight;
1178
        $this->divider->iWeight = 1;
1179
        $y                      = $img->height - $img->bottom_margin;
1180
        for ($i = 0; $i < $tmp; ++$i) {
1181
            $this->divider->Stroke($img, $xb - $i, $yt, $xb - $i, $y);
1182
        }
1183
1184
        // Should we draw "fancy" divider
1185
        if ($fancy) {
1186
            $img->SetLineWeight(1);
1187
            $img->SetColor($this->iTableHeaderFrameColor);
1188
            $img->Line($xb, $yt, $xb, $y);
1189
            $img->Line($xb - $tmp + 1, $yt, $xb - $tmp + 1, $y);
1190
            $img->SetColor('white');
1191
            $img->Line($xb - $tmp + 2, $yt, $xb - $tmp + 2, $y);
1192
        }
1193
    }
1194
1195
    // Main entry point to stroke scale
1196
    public function Stroke()
1197
    {
1198
        if (!$this->IsRangeSet()) {
1199
            Util\JpGraphError::RaiseL(6022);
1200
            //("Gantt scale has not been specified.");
1201
        }
1202
        $img = $this->iImg;
1203
1204
        // If minutes are displayed then hour interval must be 1
1205
        if ($this->IsDisplayMinute() && $this->hour->GetIntervall() > 1) {
1206
            Util\JpGraphError::RaiseL(6023);
1207
            //('If you display both hour and minutes the hour intervall must be 1 (Otherwise it doesn\' make sense to display minutes).');
1208
        }
1209
1210
        // Stroke all headers. As argument we supply the offset from the
1211
        // top which depends on any previous headers
1212
1213
        // First find out the height of each header
1214
        $offy   = $this->StrokeYears(0, true);
1215
        $offm   = $this->StrokeMonths($offy, true);
1216
        $offw   = $this->StrokeWeeks($offm, true);
1217
        $offd   = $this->StrokeDays($offw, true);
1218
        $offh   = $this->StrokeHours($offd, true);
1219
        $offmin = $this->StrokeMinutes($offh, true);
1220
1221
        // ... then we can stroke them in the "backwards order to ensure that
1222
        // the larger scale gridlines is stroked over the smaller scale gridline
1223
        $this->StrokeMinutes($offh);
1224
        $this->StrokeHours($offd);
1225
        $this->StrokeDays($offw);
1226
        $this->StrokeWeeks($offm);
1227
        $this->StrokeMonths($offy);
1228
        $this->StrokeYears(0);
1229
1230
        // Now when we now the oaverall size of the scale headers
1231
        // we can stroke the overall table headers
1232
        $this->StrokeTableHeaders($offmin);
1233
1234
        // Now we can calculate the correct scaling factor for each vertical position
1235
        $this->iAvailableHeight = $img->height - $img->top_margin - $img->bottom_margin - $offd;
1236
1237
        $this->iVertHeaderSize = $offmin;
1238
        if ($this->iVertSpacing == -1) {
1239
            $this->iVertSpacing = $this->iAvailableHeight / $this->iVertLines;
1240
        }
1241
    }
1242
}
1243