XPDF::rowInner()   F
last analyzed

Complexity

Conditions 15
Paths 678

Size

Total Lines 66
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 43
nc 678
nop 1
dl 0
loc 66
rs 2.1972
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace SKien\XFPDF;
5
6
use OPlathey\FPDF\FPDF;
7
use IntlDateFormatter;
8
9
/**
10
 * Extension of FPDF-Class to generate table/datagrid.
11
 * Supports: <ul>
12
 * <li> Page Header/Footer including Logo </li>
13
 * <li> Colheaders </li>
14
 * <li> Totals, Subtotals, Pagetotals and Carry over </li>
15
 * <li> Use defined fonts, colors and stripped datarows </li>
16
 * <li> Specify print format with JSON template file </li></ul>
17
 *
18
 * @package SKien/XFPDF
19
 * @version 2.0.0
20
 * @author Stefanius <[email protected]>
21
 * @copyright MIT License - see the LICENSE file for details
22
 */
23
class XPDF extends FPDF
24
{
25
    /** predifined Col-ID for automated row number */
26
    public const COL_ROW_NR = 1000;
27
28
    /** Bottom margin for trigger of the auto pagebreak */
29
    public const BOTTOM_MARGIN = 12;
30
31
    /** totals info                         */
32
    public const FLAG_TOTALS = 0x0007;
33
    /** calc total for column               */
34
    public const FLAG_TOTALS_CALC = 0x0001;
35
    /** print text in totals row            */
36
    public const FLAG_TOTALS_TEXT = 0x0002;
37
    /** leave empty in totals row           */
38
    public const FLAG_TOTALS_EMPTY = 0x0004;
39
    /** create internal link                */
40
    public const FLAG_INT_LINK = 0x0008;
41
    /** special format for the cell         */
42
    public const FLAG_FORMAT = 0x00F0;
43
    /** format cell as currency with symbol */
44
    public const FLAG_CUR_SYMBOL = 0x0010;
45
    /** format cell as currency without symbol  */
46
    public const FLAG_CUR_PLAIN = 0x0020;
47
    /** format cell as date/time            */
48
    public const FLAG_DATE = 0x0030;
49
    /** format cell as date/time            */
50
    public const FLAG_TIME = 0x0040;
51
    /** format cell as date/time            */
52
    public const FLAG_DATE_TIME = 0x0050;
53
    /** format cell as number               */
54
    public const FLAG_NUMBER = 0x0060;
55
    /** format cell IBAN                    */
56
    public const FLAG_IBAN = 0x0070;
57
    /** limit length to width with elipsis  */
58
    public const FLAG_ELIPSIS = 0x0080;
59
    /** cell containes image                */
60
    public const FLAG_IMAGE = 0x0100;
61
    /** suppress zero values                */
62
    public const FLAG_NO_ZERO = 0x0200;
63
64
65
    /** crate totals row on the end of report  */
66
    public const TOTALS = 0x01;
67
    /** create totals row on each pagebreak    */
68
    public const PAGE_TOTALS = 0x02;
69
    /** create carry over on the beginning of each new page    */
70
    public const CARRY_OVER = 0x04;
71
    /** create     */
72
    public const SUB_TOTALS = 0x08;
73
74
    /** default format for date cells */
75
    protected const DEF_DATE_FORMAT = IntlDateFormatter::MEDIUM;
76
    /** default format for time cells */
77
    protected const DEF_TIME_FORMAT = IntlDateFormatter::SHORT;
78
79
    /** @var string pageheader  */
80
    protected string $strPageTitle;
81
    /** @var string logo    */
82
    protected string $strLogo;
83
    /** @var float height of the loge in user units    */
84
    protected float $fltLogoHeight;
85
    /** @var string subject in page header  */
86
    protected string $strPageSubject;
87
    /** @var string pagefooter  */
88
    protected string $strPageFooter;
89
    /** @var XPDFFont font to use in header of the document */
90
    protected XPDFFont $fontHeader;
91
    /** @var XPDFFont font to use for subject in the header of the document */
92
    protected XPDFFont $fontSubject;
93
    /** @var XPDFFont font to use in footer of the document */
94
    protected XPDFFont $fontFooter;
95
    /** @var XPDFFont font to use in col headers of the grid    */
96
    protected XPDFFont $fontColHeader;
97
    /** @var XPDFFont font to use in sub headers of the grid    */
98
    protected XPDFFont $fontSubHeader;
99
    /** @var XPDFFont font to use in rows of a grid */
100
    protected XPDFFont $fontRows;
101
    /** @var string textcolor to use in header of the document  */
102
    protected string $strHeaderTextColor = '#000000';
103
    /** @var string textcolor to use in footer of the document  */
104
    protected string $strFooterTextColor = '#000000';
105
    /** @var string textcolor to use in colheader of the document   */
106
    protected string $strColHeaderTextColor = '#00007F';
107
    /** @var string textcolor to use in subheader of the document   */
108
    protected string $strSubHeaderTextColor = '#000000';
109
    /** @var string textcolor to use in rows of the document    */
110
    protected string $strRowTextColor = '#000000';
111
    /** @var string textcolor to use for internal links */
112
    protected string $strLinkTextColor = '#0000FF';
113
    /** @var string drawcolor to use in header of the document  */
114
    protected string $strHeaderDrawColor = '#404040';
115
    /** @var string drawcolor to use in footer of the document  */
116
    protected string $strFooterDrawColor = '#404040';
117
    /** @var string fillcolor to use in colheader of the document   */
118
    protected string $strColHeaderFillColor = '#D7D7D7';
119
    /** @var string fillcolor to use in subheader of the document   */
120
    protected string $strSubHeaderFillColor = '#A7A7A7';
121
    /** @var bool   strip datarows for better contrast   */
122
    protected bool $bStripped = true;
123
    /** @var string drawcolor to use in rows of the document (striped)  */
124
    protected string $strRowDrawColor = '#404040';
125
    /** @var string fillcolor to use in rows of the document (striped)  */
126
    protected string $strRowFillColor = '#E0EBFF';
127
    /** @var bool   currently inside of of grid      */
128
    protected bool $bInGrid = false;
129
    /** @var int|string border   */
130
    protected $border = 1;
131
    /** @var int        index of last col    */
132
    protected int $iMaxCol = -1;
133
    /** @var int        index of last title col (in case of colspans in header < $iMaxCol        */
134
    protected int $iMaxColHeader = -1;
135
    /** @var array      titles for the table header      */
136
    protected array $aColHeader = Array();
137
    /** @var array      width of each col in percent         */
138
    protected array $aColWidth = Array();
139
    /** @var array      align of each datacol (header always center)         */
140
    protected array $aColAlign = Array();
141
    /** @var array      flags for each datacol       */
142
    protected array $aColFlags = Array();
143
    /** @var array      fieldname or number of each datacol      */
144
    protected array $aColField = Array();
145
    /** @var array      colspan of the headercols        */
146
    protected array $aColSpan  = Array();
147
    /** @var array      info for image cols      */
148
    protected array $aImgInfo  = Array();
149
    /** @var bool       enable automatic calculation of totals       */
150
    protected bool $bCalcTotals = false;
151
    /** @var string     text for totals      */
152
    protected string $strTotals = 'Total:';
153
    /** @var bool       print subtotals on each pagebreak        */
154
    protected bool $bPageTotals = false;
155
    /** @var string     text for subtotals       */
156
    protected string $strPageTotals = '';
157
    /** @var bool       print carry over on top of each new page         */
158
    protected bool $bCarryOver = false;
159
    /** @var string     text for carry over      */
160
    protected string $strCarryOver = 'Carry over:';
161
    /** @var string     text for subtotals       */
162
    protected string $strSubTotals = 'Subtotal:';
163
    /** @var int        index of last totals col         */
164
    protected int $iMaxColTotals = -1;
165
    /** @var array      calculated totals        */
166
    protected array $aTotals = Array();
167
    /** @var array      calculated subtotals         */
168
    protected array $aSubTotals = Array();
169
    /** @var array      colspan of the totals        */
170
    protected array $aTotalsColSpan = Array();
171
    /** @var int        current rownumber    */
172
    protected int $iRow;
173
    /** @var float      lineheight in mm     */
174
    protected float $fltLineHeight = 8.0;
175
    /** @var string      */
176
    protected ?string $strLocale = null;
177
    /** @var IntlDateFormatter the formatter for date cells   */
178
    protected IntlDateFormatter $oDateFormatter;
179
    /** @var IntlDateFormatter the formatter for time cells   */
180
    protected IntlDateFormatter $oTimeFormatter;
181
    /** @var IntlDateFormatter the formatter for datetime cells   */
182
    protected IntlDateFormatter $oDTFormatter;
183
    /** @var int        decimals for number format       */
184
    protected int $iNumberDecimals = 2;
185
    /** @var string     prefix for number format */
186
    protected string $strNumberPrefix = '';
187
    /** @var string     suffix for number format */
188
    protected string $strNumberSuffix = '';
189
    /** @var bool       setlocale() not called or returned false!   */
190
    protected bool $bInvalidLocale = true;
191
    /** @var bool       Multiline rows!   */
192
    protected bool $bMultiline = false;
193
194
    /**
195
     * class constructor.
196
     * allows to set up the page size, the orientation and the unit of measure used in all methods (except for font sizes).
197
     * @param string $orientation
198
     * @param string $unit
199
     * @param string|array $size
200
     * @see FPDF::__construct()
201
     */
202
    public function __construct(string $orientation = 'P', string $unit = 'mm', $size = 'A4')
203
    {
204
        parent::__construct($orientation, $unit, $size);
205
206
        $this->setDisplayMode('fullpage', 'single');
207
        $this->setAutoPageBreak(true, self::BOTTOM_MARGIN);
208
        $this->aliasNbPages('{NP}');
209
        $this->setLocale("en_US.utf8, en_US");
210
211
        $this->strPageTitle = '';
212
        $this->strPageFooter = "{PN}/{NP}\t{D} {T}";
213
        $this->strLogo = '';
214
        $this->fltLogoHeight = 8.0;
215
216
        $this->iRow = 0;
217
218
        $this->fontHeader = new XPDFFont('Arial', 'B', 12);
219
        $this->fontSubject = new XPDFFont('Arial', 'I', 8);
220
        $this->fontFooter = new XPDFFont('Arial', 'I', 8);
221
        $this->fontColHeader = new XPDFFont('Arial', 'B', 10);
222
        $this->fontSubHeader = new XPDFFont('Arial', 'B', 10);
223
        $this->fontRows = new XPDFFont('Arial', '', 10);
224
225
        $this->oDateFormatter = new IntlDateFormatter($this->strLocale, self::DEF_DATE_FORMAT, IntlDateFormatter::NONE);
226
        $this->oTimeFormatter = new IntlDateFormatter($this->strLocale, IntlDateFormatter::NONE, self::DEF_TIME_FORMAT);
227
        $this->oDTFormatter = new IntlDateFormatter($this->strLocale, self::DEF_DATE_FORMAT, self::DEF_TIME_FORMAT);
228
    }
229
230
    /**
231
     * Set information for document to create.
232
     * @param string $strTitle
233
     * @param string $strSubject
234
     * @param string $strAuthor
235
     * @param string $strKeywords
236
     * @param bool $isUTF8  Indicates if the strings encoded in ISO-8859-1 (false) or UTF-8 (true). (Default: true)
237
     */
238
    public function setInfo(string $strTitle, string $strSubject = '', string $strAuthor = '', string $strKeywords = '', bool $isUTF8 = true) : void
239
    {
240
        $this->setTitle($strTitle, $isUTF8);
241
        $this->setSubject($strSubject, $isUTF8);
242
        $this->setAuthor($strAuthor, $isUTF8);
243
        $this->setKeywords($strKeywords, $isUTF8);
244
        $this->setCreator('FPDF - Dokument Generator');
245
    }
246
247
    /**
248
     * Set locale for formating.
249
     * If $strLocale is an comma separated list each value is tried to be
250
     * set as new locale until success. <br/>
251
     * Example: <i>"de_DE.utf8, de_DE, de, DE"</i><br/>
252
     * <br/>
253
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
254
     * @param string $strLocale
255
     * @see XPDF::initGrid()
256
     * @link http://www.php.net/manual/en/function.setlocale.php
257
     */
258
    public function setLocale(string $strLocale) : void
259
    {
260
        if ($this->strLocale != $strLocale) {
261
            $this->strLocale = $strLocale;
262
263
            // if locale contains multiple coma separated values, just explode and trim...
264
            $locale = $this->strLocale;
265
            if (strpos($this->strLocale, ',')) {
266
                $locale = array_map('trim', explode(',', $this->strLocale));
267
            }
268
            $this->bInvalidLocale = false;
269
            if (setlocale(LC_ALL, $locale) === false) {
270
                trigger_error('setlocale("' . $this->strLocale . '") failed! Check if the requested language is available on the server!', E_USER_NOTICE);
271
                $this->bInvalidLocale = true;
272
            }
273
        }
274
    }
275
276
    /**
277
     * Set the pageheader of the document.
278
     * The title is printed in the left of the pageheader using the font set with XPDF:SetHeaderFont() <br/>
279
     * Optional the subject and/or the logo can be set.
280
     * Subject and logo can also be set using XPDF:SetSubject() and XPDF:SetLogo() <br/>
281
     * The page header is separated from the report by a double line.
282
     * @param string $strTitle         Title of the Report
283
     * @param string $strHeaderSubject Subject of the Report
284
     * @param string $strLogo          Logo to print.
285
     * @see XPDF:SetHeaderFont()
286
     * @see XPDF:SetSubject()
287
     * @see XPDF:SetLogo()
288
     */
289
    public function setPageHeader(string $strTitle, string $strHeaderSubject = '', string $strLogo = '') : void
290
    {
291
        $this->strPageTitle = $strTitle;
292
        if (strlen($strLogo) > 0) {
293
            $this->strLogo = $strLogo;
294
        }
295
        $this->strPageSubject = $strHeaderSubject;
296
    }
297
298
    /**
299
     * Set the subject in the pageheader of the document.
300
     * The subject is printed in the left of the pageheader in the 2'nd line using the font set
301
     * with XPDF:SetSubjectFont() <br/>
302
     * @param string $strPageSubject
303
     * @see XPDF:SetSubjectFont()
304
     */
305
    public function setPageSubject(string $strPageSubject) : void
306
    {
307
        $this->strPageSubject = $strPageSubject;
308
    }
309
310
    /**
311
     * Set logo printed in the document header.
312
     * The logo is printed right-aligned in the header, and by default,  the logo will be
313
     * scaled to a height of 8mm. Another height can be set with XPDF::setLogoHeight(). <br/>
314
     * For convinience, the loge can be set directly within XPDF::setPageHeader().
315
     * @param string $strLogo  image file to print.
316
     * @see XPDF::setLogoHeight()
317
     * @see XPDF::setPageHeader()
318
     */
319
    public function setLogo(string $strLogo) : void
320
    {
321
        $this->strLogo = $strLogo;
322
    }
323
324
    /**
325
     * Set height of the logo in the document header.
326
     * @param float $fltLogoHeight height of the logo image
327
     */
328
    public function setLogoHeight(float $fltLogoHeight) : void
329
    {
330
        $this->fltLogoHeight = $fltLogoHeight;
331
    }
332
333
    /**
334
     * Set the pagefooter of the document.
335
     * @param string $strFooter     The footer can consist of up to three sections delimitet by TAB <b>('\t')</b><br/>
336
     *      possible placeholders are <ul>
337
     *      <li> '{D}'  -> current date (DD.MM.YYYY) </li>
338
     *      <li> '{T}'  -> current time (HH:ii) </li>
339
     *      <li> '{PN}' -> pagenumber </li>
340
     *      <li> '{NP}' -> total number of pages </li></ul>
341
     * default footer is: <b>'Page {PN}/{NP}\t{D}  {T}' </b>
342
     */
343
    public function setPageFooter($strFooter) : void
344
    {
345
        $this->strPageFooter = $strFooter;
346
    }
347
348
    /**
349
     * Initialisation of grid.
350
     * <ul>
351
     * <li> fonts </li>
352
     * <li> colors </li>
353
     * <li> totals/subtotals/carry over text </li>
354
     * <li> charset </li>
355
     * <li> formating </li></ul>
356
     * See xfpdf-sample.json for more information about this file.
357
     * @param string $strFilename
358
     */
359
    public function initGrid(string $strFilename) : void
360
    {
361
        if (file_exists($strFilename)) {
362
            $strJSON = file_get_contents($strFilename);
363
            $jsonData = json_decode($strJSON);
364
            if ($jsonData) {
365
                $this->fontHeader = $this->propertyFont($jsonData, 'fontHeader', $this->fontHeader);
366
                $this->fontSubject = $this->propertyFont($jsonData, 'fontSubject', $this->fontSubject);
367
                $this->fontFooter = $this->propertyFont($jsonData, 'fontFooter', $this->fontFooter);
368
                $this->fontColHeader = $this->propertyFont($jsonData, 'fontColHeader', $this->fontColHeader);
369
                $this->fontSubHeader = $this->propertyFont($jsonData, 'fontSubHeader', $this->fontSubHeader);
370
                $this->fontRows = $this->propertyFont($jsonData, 'fontRows', $this->fontRows);
371
372
                $this->fltLineHeight = $this->property($jsonData, 'dblLineHeight', $this->fltLineHeight);
373
                $this->fltLineHeight = $this->property($jsonData, 'fltLineHeight', $this->fltLineHeight);
374
375
                $this->strHeaderTextColor = $this->property($jsonData, 'strHeaderTextColor', $this->strHeaderTextColor);
376
                $this->strHeaderDrawColor = $this->property($jsonData, 'strHeaderDrawColor', $this->strHeaderDrawColor);
377
378
                $this->strFooterTextColor = $this->property($jsonData, 'strFooterTextColor', $this->strFooterTextColor);
379
                $this->strFooterDrawColor = $this->property($jsonData, 'strFooterDrawColor', $this->strFooterDrawColor);
380
381
                $this->strColHeaderTextColor = $this->property($jsonData, 'strColHeaderTextColor', $this->strColHeaderTextColor);
382
                $this->strColHeaderFillColor = $this->property($jsonData, 'strColHeaderFillColor', $this->strColHeaderFillColor);
383
                $this->strSubHeaderTextColor = $this->property($jsonData, 'strSubHeaderTextColor', $this->strSubHeaderTextColor);
384
                $this->strSubHeaderFillColor = $this->property($jsonData, 'strSubHeaderFillColor', $this->strSubHeaderFillColor);
385
                $this->strRowTextColor = $this->property($jsonData, 'strRowTextColor', $this->strRowTextColor);
386
                $this->strRowDrawColor = $this->property($jsonData, 'strRowDrawColor', $this->strRowDrawColor);
387
                $this->strRowFillColor = $this->property($jsonData, 'strRowFillColor', $this->strRowFillColor);
388
                $this->strLinkTextColor = $this->property($jsonData, 'strLinkTextColor', $this->strLinkTextColor);
389
390
                $this->bStripped = $this->property($jsonData, 'bStripped', $this->bStripped);
391
                $this->border = $this->property($jsonData, 'border', $this->border);
392
393
                $this->bCalcTotals = $this->property($jsonData, 'bCalcTotals', $this->bCalcTotals);
394
                $this->bPageTotals = $this->property($jsonData, 'bPageTotals', $this->bPageTotals);
395
                $this->bCarryOver = $this->property($jsonData, 'bCarryOver', $this->bCarryOver);
396
397
                $this->strTotals = $this->property($jsonData, 'strTotals', $this->strTotals);
398
                $this->strPageTotals = $this->property($jsonData, 'strPageTotals', $this->strPageTotals);
399
                $this->strCarryOver = $this->property($jsonData, 'strCarryOver', $this->strCarryOver);
400
401
                $this->charset = $this->property($jsonData, 'strCharset', $this->charset);
402
                $this->setLocale($this->property($jsonData, 'strLocale', $this->strLocale));
403
404
                $strFormat = $this->property($jsonData, 'strFormatDT', null);
405
                if ($strFormat !== null) {
406
                    $this->setDateTimeFormat($strFormat);
407
                } else {
408
                    $this->setDateTimeFormat(self::DEF_DATE_FORMAT, self::DEF_TIME_FORMAT);
409
                }
410
                $strFormat = $this->property($jsonData, 'strFormatD', null);
411
                if ($strFormat !== null) {
412
                    $this->setDateFormat($strFormat);
413
                } else {
414
                    $this->setDateFormat(self::DEF_DATE_FORMAT);
415
                }
416
                $strFormat = $this->property($jsonData, 'strFormatT', null);
417
                if ($strFormat !== null) {
418
                    $this->setTimeFormat($strFormat);
419
                } else {
420
                    $this->setTimeFormat(self::DEF_TIME_FORMAT);
421
                }
422
            } else {
423
                trigger_error('unable to decode contents of JSON file [' . $strFilename . ']', E_USER_NOTICE);
424
            }
425
        }
426
    }
427
428
    /**
429
     * Set the Date Format to use.
430
     * Date values are formatet using the language set by setLocale().
431
     *
432
     * The $format parameter can either be one of the IntlDateFormatter int Constants
433
     *
434
     * | const                      | en_US              | de_DE              |
435
     * |----------------------------|--------------------|--------------------|
436
     * | IntlDateFormatter::SHORT   | M/d/yy             | dd.MM.yy           |
437
     * | IntlDateFormatter::MEDIUM  | MMM d, yyyy        | dd.MM.yyyy         |
438
     * | IntlDateFormatter::LONG    | MMMM d, yyyy       | dd. MMMM yyyy      |
439
     * | IntlDateFormatter::FULL    | EEEE, MMMM d, yyyy | EEEE dd. MMMM yyyy |
440
     *
441
     * or a string pattern following the definition of IntlDateFormatter (see link below)
442
     *
443
     * [Pattern to Format Date and Time](./pattern-to-format-date-and-time)
444
     *
445
     * > Note: This property can also be set in the JSON file used in initGrid() !!
446
     * @param int|string $format
447
     * @see XPDF::initGrid()
448
     * @link https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
449
     */
450
    public function setDateFormat($format) : void
451
    {
452
        if (is_numeric($format)) {
453
            $this->oDateFormatter = new IntlDateFormatter($this->strLocale, $format, IntlDateFormatter::NONE);
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type string; however, parameter $datetype of IntlDateFormatter::__construct() does only seem to accept integer, 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

453
            $this->oDateFormatter = new IntlDateFormatter($this->strLocale, /** @scrutinizer ignore-type */ $format, IntlDateFormatter::NONE);
Loading history...
454
        } else {
455
            if (strpos($format, '%') !== FALSE) {
456
                trigger_error('XFPDF::setDateFormat("' . $format . '"): call with old strftime() formated Parameter!', E_USER_ERROR);
457
            } else {
458
                $this->oDateFormatter = new IntlDateFormatter($this->strLocale, 0, 0, null, null, $format);
459
            }
460
        }
461
    }
462
463
    /**
464
     * Set the Time Format to use.
465
     *
466
     * The $format parameter can either be one of the IntlDateFormatter int Constants
467
     *
468
     * | const                      | en_US           | de_DE         |
469
     * |----------------------------|-----------------|---------------|
470
     * | IntlDateFormatter::SHORT   | KK:mm a         | HH:mm         |
471
     * | IntlDateFormatter::MEDIUM  | KK:mm:ss a      | HH:mm:ss      |
472
     * | IntlDateFormatter::LONG    | KK:mm:ss a o    | HH:mm:ss o    |
473
     * | IntlDateFormatter::FULL    | KK:mm:ss a vvvv | HH:mm:ss vvvv |
474
     *
475
     * or a string pattern following the definition of IntlDateFormatter (see link below)
476
     *
477
     * [Pattern to Format Date and Time](./pattern-to-format-date-and-time)
478
     *
479
     * > Note: This property can also be set in the JSON file used in initGrid() !!
480
     * @param int|string $format
481
     * @see XPDF::initGrid()
482
     * @link https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
483
     */
484
    public function setTimeFormat($format) : void
485
    {
486
        if (is_numeric($format)) {
487
            $this->oTimeFormatter = new IntlDateFormatter($this->strLocale, IntlDateFormatter::NONE, $format);
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type string; however, parameter $timetype of IntlDateFormatter::__construct() does only seem to accept integer, 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

487
            $this->oTimeFormatter = new IntlDateFormatter($this->strLocale, IntlDateFormatter::NONE, /** @scrutinizer ignore-type */ $format);
Loading history...
488
        } else {
489
            if (strpos($format, '%') !== FALSE) {
490
                trigger_error('XFPDF::setTimeFormat("' . $format . '"): call with old strftime() formated Parameter!', E_USER_ERROR);
491
            } else {
492
                $this->oTimeFormatter = new IntlDateFormatter($this->strLocale, 0, 0, null, null, $format);
493
            }
494
        }
495
    }
496
497
    /**
498
     * Set the DateTime Format to use.
499
     * You can either set the $formatD and $formatT parameter to one of the
500
     * IntlDateFormatter int Constants (see setDateFormat, setTimeFormat)
501
     * or set a string pattern following the definition of IntlDateFormatter as
502
     * first parameter (see link below).
503
     *
504
     * [Pattern to Format Date and Time](./pattern-to-format-date-and-time)
505
     *
506
     * > Note: This property can also be set in the JSON file used in initGrid() !!
507
     * @param int|string $formatD
508
     * @param int $formatT
509
     * @see XPDF::setDateFormat()
510
     * @see XPDF::setTimeFormat()
511
     * @see XPDF::initGrid()
512
     * @link https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
513
     */
514
    public function setDateTimeFormat($formatD, int $formatT = 0) : void
515
    {
516
        if (is_numeric($formatD)) {
517
            $this->oDTFormatter = new IntlDateFormatter($this->strLocale, $formatD, $formatT);
0 ignored issues
show
Bug introduced by
It seems like $formatD can also be of type string; however, parameter $datetype of IntlDateFormatter::__construct() does only seem to accept integer, 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

517
            $this->oDTFormatter = new IntlDateFormatter($this->strLocale, /** @scrutinizer ignore-type */ $formatD, $formatT);
Loading history...
518
        } else {
519
            if (strpos($formatD, '%') !== FALSE) {
520
                trigger_error('XFPDF::setDateTimeFormat("' . $formatD . '"): call with old strftime() formated Parameter!', E_USER_ERROR);
521
            } else {
522
                $this->oDTFormatter = new IntlDateFormatter($this->strLocale, 0, 0, null, null, $formatD);
523
            }
524
        }
525
    }
526
527
    /**
528
     * Set format for numbers.
529
     * Decimal point and thousands separator from locale settings is used if available. <br/>
530
     * <br/>
531
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
532
     * @param int $iDecimals
533
     * @param string $strPrefix
534
     * @param string $strSuffix
535
     * @see XPDF::initGrid()
536
     */
537
    public function setNumberFormat(int $iDecimals, string $strPrefix = '', string $strSuffix = '') : void
538
    {
539
        $this->iNumberDecimals = $iDecimals;
540
        $this->strNumberPrefix = $strPrefix;
541
        $this->strNumberSuffix = $strSuffix;
542
    }
543
544
    /**
545
     * Set font for page header.
546
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
547
     * @see XPDF::initGrid()
548
     * @param string $strFontname
549
     * @param string $strStyle
550
     * @param int $iSize
551
     */
552
    public function setHeaderFont(string $strFontname, string $strStyle, int $iSize) : void
553
    {
554
        $this->fontHeader = new XPDFFont($strFontname, $strStyle, $iSize);
555
    }
556
557
    /**
558
     * Set font for subject in the page header.
559
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
560
     * @see XPDF::initGrid()
561
     * @param string $strFontname
562
     * @param string $strStyle
563
     * @param int $iSize
564
     */
565
    public function setSubjectFont(string $strFontname, string $strStyle, int $iSize) : void
566
    {
567
        $this->fontSubject = new XPDFFont($strFontname, $strStyle, $iSize);
568
    }
569
570
    /**
571
     * Set font for page footer.
572
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
573
     * @see XPDF::initGrid()
574
     * @param string $strFontname
575
     * @param string $strStyle
576
     * @param int $iSize
577
     */
578
    public function setFooterFont(string $strFontname, string $strStyle, int $iSize) : void
579
    {
580
        $this->fontFooter = new XPDFFont($strFontname, $strStyle, $iSize);
581
    }
582
583
    /**
584
     * Set font for col headers.
585
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
586
     * @see XPDF::initGrid()
587
     * @param string $strFontname
588
     * @param string $strStyle
589
     * @param int $iSize
590
     */
591
    public function setColHeaderFont(string $strFontname, string $strStyle, int $iSize) : void
592
    {
593
        $this->fontColHeader = new XPDFFont($strFontname, $strStyle, $iSize);
594
    }
595
596
    /**
597
     * Set font for sub headers.
598
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
599
     * @see XPDF::initGrid()
600
     * @param string $strFontname
601
     * @param string $strStyle
602
     * @param int $iSize
603
     */
604
    public function setSubHeaderFont(string $strFontname, string $strStyle, int $iSize) : void
605
    {
606
        $this->fontSubHeader = new XPDFFont($strFontname, $strStyle, $iSize);
607
    }
608
609
    /**
610
     * Set font for data rows.
611
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
612
     * @see XPDF::initGrid()
613
     * @param string $strFontname
614
     * @param string $strStyle
615
     * @param int $iSize
616
     */
617
    public function setRowFont(string $strFontname, string $strStyle, int $iSize) : void
618
    {
619
        $this->fontRows = new XPDFFont($strFontname, $strStyle, $iSize);
620
        $this->selectFont($this->fontRows);
621
    }
622
623
    /**
624
     * Set lineheight.
625
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
626
     * @see XPDF::initGrid()
627
     * @param float $fltLineHeight  lineheight in mm
628
     */
629
    public function setLineHeight(float $fltLineHeight) : void
630
    {
631
        $this->fltLineHeight = $fltLineHeight;
632
    }
633
634
    /**
635
     * Set colors for text and drawing in the pageheader.
636
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
637
     * @see XPDF::initGrid()
638
     * @param string $strTextColor
639
     * @param string $strDrawColor
640
     */
641
    public function setHeaderColors(string $strTextColor, string $strDrawColor) : void
642
    {
643
        $this->strHeaderTextColor = $strTextColor;
644
        $this->strHeaderDrawColor = $strDrawColor;
645
    }
646
647
    /**
648
     * Set colors for text and drawing in the colheader.
649
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
650
     * @see XPDF::initGrid()
651
     * @param string $strTextColor
652
     * @param string $strFillColor
653
     */
654
    public function setColHeaderColors(string $strTextColor, string $strFillColor) : void
655
    {
656
        $this->strColHeaderTextColor = $strTextColor;
657
        $this->strColHeaderFillColor = $strFillColor;
658
    }
659
660
    /**
661
     * Set colors for text and drawing in the grid.
662
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
663
     * @see XPDF::initGrid()
664
     * @param string $strTextColor
665
     * @param string $strDrawColor
666
     * @param string $strFillColor
667
     * @param bool $bStripped
668
     */
669
    public function setRowColors(string $strTextColor, string $strDrawColor, string $strFillColor, bool $bStripped = true) : void
670
    {
671
        $this->strRowTextColor = $strTextColor;
672
        $this->strRowDrawColor = $strDrawColor;
673
        $this->strRowFillColor = $strFillColor;
674
        $this->bStripped = $bStripped;
675
    }
676
677
    /**
678
     * Set colors for text and drawing in the pagefooter.
679
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
680
     * @see XPDF::initGrid()
681
     * @param string $strTextColor
682
     * @param string $strDrawColor
683
     */
684
    public function setFooterColors(string $strTextColor, string $strDrawColor) : void
685
    {
686
        $this->strFooterTextColor = $strTextColor;
687
        $this->strFooterDrawColor = $strDrawColor;
688
    }
689
690
    /**
691
     * Enables automatic calclation of totals.
692
     * <ul>
693
     * <li> totals over all at end of grid  (self::TOTALS) </li>
694
     * <li> subtotals at end of each page (self::PAGE_TOTALS) </li>
695
     * <li> carry over at beginning of new page (self::CARRY_OVER) </li></ul>
696
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
697
     * @see XPDF::initGrid()
698
     * @param int $iTotals  combination of
699
     */
700
    public function enableTotals(int $iTotals = self::TOTALS) : void
701
    {
702
        $this->bCalcTotals = ($iTotals & self::TOTALS) != 0;
703
        $this->bPageTotals = ($iTotals & self::PAGE_TOTALS) != 0;
704
        $this->bCarryOver = ($iTotals & self::CARRY_OVER) != 0;
705
        if ($this->bPageTotals) {
706
            // we must increase the bottom margin to trigger the pagebreak
707
            $this->setAutoPageBreak(true, self::BOTTOM_MARGIN + $this->fltLineHeight);
708
        }
709
    }
710
711
    /**
712
     * Set text for totals, subtotals and carry over row.
713
     * Following placeholders will be replaced: <ul>
714
     * <li>  {PN} -> current page  </li>
715
     * <li>  {PN-1} -> previous page </li></ul>
716
     * <b>!! Can be controled/overwritten through JSON-Format in InitGrid() !!</b>
717
     * @see XPDF::initGrid()
718
     * @param string $strTotals
719
     * @param string $strPageTotals
720
     * @param string $strCarryOver
721
     */
722
    public function setTotalsText(string $strTotals, string $strPageTotals = '', string $strCarryOver = '') : void
723
    {
724
        $this->strTotals = $strTotals;
725
        if (strlen($strPageTotals) > 0) {
726
            $this->strPageTotals = $strPageTotals;
727
        }
728
        if (strlen($strCarryOver) > 0) {
729
            $this->strCarryOver = $strCarryOver;
730
        }
731
    }
732
733
    /**
734
     * reset coldefinitions
735
     */
736
    public function resetCols() : void
737
    {
738
        $this->iMaxCol = -1;
739
        $this->aColWidth = array();
740
        $this->aColAlign = array();
741
        $this->aColFlags = array();
742
        $this->aColField = array();
743
        $this->iMaxColHeader = -1;
744
        $this->aColHeader = array();
745
        $this->aColSpan = array();
746
        $this->aTotalsColSpan = array();
747
        $this->aSubTotals = array();
748
        $this->aTotals = array();
749
    }
750
751
    /**
752
     * Add Column to the Grid.
753
     * String is directly mapped to field in datarow, number is requested through Col() method
754
     * @param string $strColHeader title text in the header, if equal -1, colspan of previous col ist increased
755
     * @param float $fltWidth      width in mm (if -1, col is enhanced so table uses on full page width)
756
     * @param string $strAlign     alignment of datacol 'L', 'C' or 'R' - headerer cells allways centered
757
     * @param string|int $strField     data-field or Column ID.
758
     * @param int $wFlags          Flags to define behaviour for column
759
     * @return int                 Index of the inserted col
760
     */
761
    public function addCol(string $strColHeader, float $fltWidth, string $strAlign, $strField, int $wFlags = 0) : int
762
    {
763
        $this->iMaxCol++;
764
        $this->aColWidth[$this->iMaxCol] = $fltWidth;
765
        $this->aColAlign[$this->iMaxCol] = $strAlign;
766
        $this->aColFlags[$this->iMaxCol] = $wFlags;
767
        $this->aColField[$this->iMaxCol] = $strField;
768
        if ($strColHeader != -1) {
769
            $this->iMaxColHeader++;
770
            $this->aColHeader[$this->iMaxColHeader] = $strColHeader;
771
            $this->aColSpan[$this->iMaxColHeader] = 1;
772
        } else {
773
            $this->aColSpan[$this->iMaxColHeader]++;
774
        }
775
        if ($this->iMaxCol == 0 || ($wFlags & self::FLAG_TOTALS) != 0) {
776
            $this->iMaxColTotals++;
777
            $this->aTotals[$this->iMaxCol] = 0.0;
778
            $this->aSubTotals[$this->iMaxCol] = 0.0;
779
            $this->aTotalsColSpan[$this->iMaxColTotals] = 1;
780
        } else {
781
            $this->aTotalsColSpan[$this->iMaxColTotals]++;
782
        }
783
        return $this->iMaxCol;
784
    }
785
786
    /**
787
     * Set infos for image col.
788
     * Set the margin from the top left corner of the cell and the height/width of the image. <ul>
789
     * <li> If no height and width specified, the image is printed in its origin size </li>
790
     * <li> If only one of both is specified, the image is scaled and the aspect ratio is retained </li></ul>
791
     * @param int $iCol        index of the col (usually the return value of the AddCol() Method)
792
     * @param float $fltTop    top margin from row in user units
793
     * @param float $fltLeft   left margin from cell in user units
794
     * @param float $fltHeight height of the image in user units
795
     * @param float $fltWidth  width of the image in user units
796
     */
797
    public function setColImageInfo(int $iCol, float $fltTop, float $fltLeft, float $fltHeight = -1, float $fltWidth = -1) : void
798
    {
799
        if ($this->aColFlags[$iCol] & self::FLAG_IMAGE == 0) {
800
            trigger_error('Col #' . $iCol . ' is not defined as image col!', E_USER_WARNING);
801
        }
802
        $this->aImgInfo[$iCol] = array(
803
            'fltTop' => $fltTop,
804
            'fltLeft' => $fltLeft,
805
            'fltWidth' => $fltHeight,
806
            'fltHeight' => $fltWidth);
807
    }
808
809
    /**
810
     * Have to be called once before datarows be added to the document.
811
     */
812
    public function prepare() : void
813
    {
814
        $this->calcColWidth();
815
        $this->bInGrid = true;
816
        $this->addPage();
817
818
        $this->selectDrawColor($this->strRowDrawColor);
819
        $this->selectFillColor($this->strRowFillColor);
820
        $this->selectTextColor($this->strRowTextColor);
821
        $this->selectFont($this->fontRows);
822
        $this->setLineWidth(0.2);
823
    }
824
825
    /**
826
     * Build row.
827
     * If fieldname specified in AddCol(), directly the value from the associative array is inserted
828
     * (in case of DATE-Field value is formated d.m.Y)
829
     * all other columns are requested through GetCol() - method
830
     * @param array $row    current row as associative array (may comes from DB query)
831
     */
832
    public function row(array $row) : void
833
    {
834
        $a = $this->saveSettings();
835
        $this->iRow++;
836
        if (($strPreRow = $this->preRow($row)) != '') {
837
            $this->subHeader($strPreRow);
838
        }
839
        if (!$this->isRowVisible($row)) {
840
            return;
841
        }
842
        $this->rowInner($row);
843
        $this->postRow($row);
844
        $this->restoreSettings($a);
845
    }
846
847
    /**
848
     * Mark the end of the grid.
849
     * If totals enabled, total row will be printed. <br/>
850
     */
851
    public function endGrid() : void
852
    {
853
        $this->totalsRow(self::TOTALS);
854
        // Internal flag is needed to suppress any printing of colheaders or subtotals after end of grid!
855
        $this->bInGrid = false;
856
    }
857
858
    /**
859
     * Starts group for new subtotals.
860
     * Reset calculated subtotals and print subheader if strHeader is set
861
     * @param string $strTotals
862
     * @param string $strHeader
863
     */
864
    public function startGroup(string $strTotals, ?string $strHeader = null) : void
865
    {
866
        $this->strSubTotals = $strTotals;
867
        $iCount = count($this->aSubTotals);
868
        for ($i = 0; $i < $iCount; $i++) {
869
            $this->aSubTotals[$i] = 0.0;
870
        }
871
        if ($strHeader) {
872
            $this->subHeader($strHeader);
873
        }
874
    }
875
876
    /**
877
     * End group and print subtotals row.
878
     */
879
    public function endGroup() : void
880
    {
881
        $this->totalsRow(self::SUB_TOTALS);
882
        $this->strSubTotals = '';
883
    }
884
885
    /**
886
     * Selects given font.
887
     * @param XPDFFont $font
888
     */
889
    public function selectFont(?XPDFFont $font) : void
890
    {
891
        if ($font !== null) {
892
            $this->setFont($font->strFontname, $font->strStyle, $font->iSize);
893
        }
894
    }
895
896
    /**
897
     * Set color for text.
898
     * @param string $strColor color to select in HTML-Format #RRGGBB
899
     */
900
    public function selectTextColor(string $strColor) : void
901
    {
902
        $r = 0; $g = 0; $b = 0;
903
        $this->getRGB($strColor, $r, $g, $b);
904
        $this->setTextColor($r, $g, $b);
905
    }
906
907
    /**
908
     * Set color for drawing.
909
     * @param string $strColor color to select in HTML-Format #RRGGBB
910
     */
911
    public function selectDrawColor(string $strColor) : void
912
    {
913
        $r = 0; $g = 0; $b = 0;
914
        $this->getRGB($strColor, $r, $g, $b);
915
        $this->setDrawColor($r, $g, $b);
916
    }
917
918
    /**
919
     * Set fillcolor.
920
     * @param string $strColor color to select in HTML-Format #RRGGBB
921
     */
922
    public function selectFillColor(string $strColor) : void
923
    {
924
        $r = 0; $g = 0; $b = 0;
925
        $this->getRGB($strColor, $r, $g, $b);
926
        $this->setFillColor($r, $g, $b);
927
    }
928
929
    /**
930
     * Last step: create the document.
931
     * If nor filename is given, the title set with SetInfo() or SetTitle()
932
     * method is used (or 'XFPDF.pdf' if no title set so far).
933
     * If the filename not ending with .pdf (case insensitive), the extension ist appended.
934
     * @param string $strFilename  Filename
935
     */
936
    public function createPDF(string $strFilename = '') : void
937
    {
938
        if (empty($strFilename)) {
939
            $strFilename = isset($this->metadata['Title']) ? $this->metadata['Title'] : 'XFPDF.pdf';
940
        }
941
        if (strtolower(substr($strFilename, -4)) !== '.pdf') {
942
            $strFilename .= '.pdf';
943
        }
944
        $this->output($strFilename, 'I');
945
    }
946
947
    /**
948
     * Print pageheader / logo / colheaders.
949
     * {@inheritDoc}
950
     * @see \OPlathey\FPDF\FPDF::header()
951
     */
952
    public function header() : void
953
    {
954
        if (!empty($this->strPageTitle)) {
955
            $fltLogoHeight = 0.0;
956
            if (!empty($this->strLogo)) {
957
                list($iWidth, $iHeight) = getimagesize($this->strLogo);
958
                if ($iWidth > 0 && $iHeight > 0) {
959
                    // scale image to desired high
960
                    $iWidth *= $this->fltLogoHeight / $iHeight;
961
                    $x = $this->w - $this->rMargin - $iWidth - 1;
962
                    $y = $this->tMargin + 0.5;
963
                    $this->Image($this->strLogo, $x, $y, $iWidth);
964
                    $fltLogoHeight = $this->fltLogoHeight;
965
                }
966
            }
967
968
            $this->selectDrawColor($this->strHeaderDrawColor);
969
            $this->selectFont($this->fontHeader);
970
            $this->selectTextColor($this->strHeaderTextColor);
971
            $this->setLineWidth(0.2);
972
            $strPageTitle = $this->replacePlaceholder($this->strPageTitle);
973
            $strPageSubject = $this->replacePlaceholder($this->strPageSubject);
974
            $this->cell(0, $this->FontSize, $strPageTitle, 0, 0, 'L');
975
            $this->ln();
976
            if (strlen($strPageSubject) > 0) {
977
                $this->selectFont($this->fontSubject);
978
                $this->cell(0, $this->FontSize, $strPageSubject, 0, 0, 'L');
979
                $this->ln();
980
            }
981
982
            $y = $this->getY();
983
            if (($fltLogoHeight + $this->tMargin) > $y) {
984
                $y = $fltLogoHeight + $this->tMargin;
985
                $this->setY($y);
986
            }
987
            $y += 2.0;
988
            $this->line($this->lMargin, $y, $this->w - $this->rMargin, $y);
989
            $y += 0.5;
990
            $this->line($this->lMargin, $y, $this->w - $this->rMargin, $y);
991
            $this->ln(6);
992
        }
993
        if ($this->iMaxCol > 0 && $this->bInGrid) {
994
            $this->colHeader();
995
            if ($this->bCarryOver && $this->page > 1) {
996
                $this->totalsRow(self::CARRY_OVER);
997
            }
998
        }
999
    }
1000
1001
    /**
1002
     * Print pagefooter.
1003
     * {@inheritDoc}
1004
     * @see \OPlathey\FPDF\FPDF::footer()
1005
     */
1006
    public function footer() : void
1007
    {
1008
        if ($this->bPageTotals) {
1009
            $this->totalsRow(self::PAGE_TOTALS);
1010
        }
1011
        if (!empty($this->strPageFooter)) {
1012
            $this->selectDrawColor($this->strFooterDrawColor);
1013
            $this->selectFont($this->fontFooter);
1014
            $this->selectTextColor($this->strFooterTextColor);
1015
            $this->setLineWidth(0.2);
1016
1017
            // Position 2mm from the bottom border
1018
            $this->setY(-self::BOTTOM_MARGIN + 2);
1019
            $this->line($this->lMargin, $this->GetY() - 0.5, $this->w - $this->rMargin, $this->getY() - 0.5);
1020
            $iWidth = $this->w - $this->rMargin - $this->lMargin;
1021
            $aCell = explode("\t", $this->strPageFooter, 3);
1022
            $aAlign = array('', '', '');
1023
            switch (count($aCell)) {
1024
                case 1:
1025
                    $aAlign = array('C', '', '');
1026
                    break;
1027
                case 2:
1028
                    $aAlign = array('L', 'R', '');
1029
                    break;
1030
                case 3:
1031
                    $aAlign = array('L', 'C', 'R');
1032
                    break;
1033
            }
1034
            $i = 0;
1035
            foreach ($aCell as $strCell) {
1036
                $strCell = $this->replacePlaceholder($strCell);
1037
                $this->cell($iWidth / count($aCell), 7, $strCell, 'T', 0, $aAlign[$i++]);
1038
            }
1039
        }
1040
    }
1041
1042
    /**
1043
     * Create headercols for grid.
1044
     */
1045
    protected function colHeader() : void
1046
    {
1047
        $this->selectFillColor($this->strColHeaderFillColor);
1048
        $this->selectTextColor($this->strColHeaderTextColor);
1049
        $this->selectDrawColor($this->strRowDrawColor);
1050
        $this->setLineWidth(0.2);
1051
        $this->selectFont($this->fontColHeader);
1052
1053
        $iCol = 0;
1054
        for ($i = 0; $i <= $this->iMaxColHeader; $i++) {
1055
            $iWidth = 0;
1056
            if ($this->aColSpan[$i] > 1) {
1057
                $j = 0;
1058
                while ($j < $this->aColSpan[$i]) {
1059
                    $iWidth += $this->aColWidth[$iCol++];
1060
                    $j++;
1061
                }
1062
            } else {
1063
                $iWidth = $this->aColWidth[$iCol++];
1064
            }
1065
1066
            $strHeader = $this->aColHeader[$i];
1067
            $this->cell($iWidth, $this->fltLineHeight, $strHeader, $this->border, 0, 'C', true);
1068
        }
1069
        $this->ln();
1070
    }
1071
1072
    /**
1073
     * Insert Subheader into the grid.
1074
     * @param string $strText
1075
     */
1076
    protected function subHeader(string $strText) : void
1077
    {
1078
        $a = $this->saveSettings();
1079
1080
        $this->selectFillColor($this->strSubHeaderFillColor);
1081
        $this->selectTextColor($this->strSubHeaderTextColor);
1082
        $this->selectFont($this->fontSubHeader);
1083
1084
        // increase pagebreak trigger to ensure not only subheader fits on current page
1085
        $iBottomMargin = $this->bMargin;
1086
        $this->setAutoPageBreak(true, $iBottomMargin + $this->fltLineHeight);
1087
        $iWidth = $this->w - $this->lMargin - $this->rMargin;
1088
        $this->cell($iWidth, $this->fltLineHeight, $strText, $this->border, 0, 'L', true);
1089
        $this->ln();
1090
        $this->restoreSettings($a);
1091
        // reset pagebreak trigger
1092
        $this->setAutoPageBreak(true, $iBottomMargin);
1093
    }
1094
1095
    /**
1096
     * Calculates width for dynamic col.
1097
     * Dynamic col is specified with a width of -1. <br/>
1098
     * <b>Only one col with width of -1 is allowed!</b> <br/>
1099
     * If no dyn. Col is specified, last col is assumed as dynamic. <br/><br/>
1100
     * Sum of all other cols is subtracted from page width. <br/>
1101
     */
1102
    protected function calcColWidth() : void
1103
    {
1104
        $iGridWidth = $this->w - $this->lMargin - $this->rMargin;
1105
        $iCol = -1;
1106
        for ($i = 0; $i <= $this->iMaxCol; $i++) {
1107
            if ($this->aColWidth[$i] < 0) {
1108
                if ($iCol >= 0) {
1109
                    trigger_error('Only one dynamic col is allowed!', E_USER_WARNING);
1110
                }
1111
                $iCol = $i;
1112
            }
1113
        }
1114
        if ($iCol < 0) {
1115
            $iCol = $this->iMaxCol;
1116
        }
1117
        $iWidth = 0;
1118
        for ($i = 0; $i <= $this->iMaxCol; $i++) {
1119
            if ($i != $iCol) {
1120
                $iWidth += $this->aColWidth[$i];
1121
            }
1122
        }
1123
        $this->aColWidth[$iCol] = $iGridWidth - $iWidth;
1124
    }
1125
1126
    /**
1127
     * Inner 'pure' function to build the row.
1128
     * @param array $row
1129
     */
1130
    protected function rowInner(array $row) : void
1131
    {
1132
        $this->bInGrid = true;
1133
1134
        if ($this->bMultiline) {
1135
            if ($this->getY() + (2 * $this->fltLineHeight) > $this->PageBreakTrigger)
1136
                $this->AddPage($this->CurOrientation);
1137
        }
1138
1139
        for ($i = 0; $i <= $this->iMaxCol; $i++) {
1140
            $strCell = '';
1141
            $field = $this->aColField[$i];
1142
            $wFlags = $this->aColFlags[$i];
1143
            $bFill = $this->bStripped && (($this->iRow % 2) == 0);
1144
1145
            // calc totals
1146
            if (isset($row[$field])) {
1147
                $this->calcTotals($i, $row[$field]);
1148
            }
1149
1150
            // save for restore, if changed for current col
1151
            $a = $this->saveSettings();
1152
1153
            if (is_numeric($field)) {
1154
                $strCell = $this->col(intval($field), $row, $bFill);
1155
            } else {
1156
                // directly get value from row data
1157
                if (!isset($row[$field])) {
1158
                    $strCell = '';
1159
                } else {
1160
                    $strCell = $row[$field];
1161
                }
1162
                if (($wFlags & self::FLAG_FORMAT) == self::FLAG_ELIPSIS) {
1163
                    $fltWidth = $this->getStringWidth($strCell);
1164
                    if ($fltWidth > $this->aColWidth[$i] - 2) {
1165
                        while ($fltWidth > $this->aColWidth[$i] - 3) {
1166
                            $strCell = substr($strCell, 0, -1);
1167
                            $fltWidth = $this->getStringWidth($strCell);
1168
                        }
1169
                        $strCell .= '..';
1170
                    }
1171
                }
1172
                $strCell = $this->formatValue($strCell, $wFlags);
1173
            }
1174
1175
            if ($this->isImageCol($i)) {
1176
                $this->drawImageCol($i, $strCell, $bFill);
1177
            } else {
1178
                $link = $this->getColLink($i, $row);
1179
                if ($this->bMultiline) {
1180
                    $x = $this->x;
1181
                    $y = $this->y;
1182
                    $this->multiCell($this->aColWidth[$i], $this->fltLineHeight, $strCell, $this->border, $this->aColAlign[$i], $bFill);
1183
                    if ($i != $this->iMaxCol) {
1184
                        $this->x = $x + $this->aColWidth[$i];
1185
                        $this->y = $y;
1186
                    }
1187
                } else {
1188
                    $this->cell($this->aColWidth[$i], $this->fltLineHeight, $strCell, $this->border, 0, $this->aColAlign[$i], $bFill, $link);
1189
                }
1190
            }
1191
1192
            $this->restoreSettings($a);
1193
        }
1194
        if (!$this->bMultiline) {
1195
            $this->ln();
1196
        }
1197
    }
1198
1199
    /**
1200
     * Print totals/subtotals row.
1201
     * @param int $iTotals
1202
     */
1203
    protected function totalsRow(int $iTotals) : void
1204
    {
1205
        if ($this->bInGrid && $this->bCalcTotals) {
1206
            $a = $this->saveSettings();
1207
            $this->setTotalsRowFormat($iTotals);
1208
            $aTotals = $this->getTotalsRowValues($iTotals);
1209
            $strText = $this->getTotalsRowText($iTotals);
1210
            $iCol = 0;
1211
            for ($iTotalsCol = 0; $iTotalsCol <= $this->iMaxColTotals; $iTotalsCol++) {
1212
                $strCol = '';
1213
                $strAlign = 'C';
1214
1215
                if ($this->isTotalsTextCol($iCol)) {
1216
                    $strCol = $strText;
1217
                    $strAlign = 'L';
1218
                } elseif ($this->isTotalsCalcCol($iCol)) {
1219
                    $strCol = $this->formatValue($aTotals[$iCol], $this->aColFlags[$iCol]);
1220
                    $strAlign = 'R';
1221
                }
1222
                $iWidth = $this->calcTotalsColWidth($iTotalsCol, $iCol);
1223
                $this->cell($iWidth, $this->fltLineHeight, $strCol, $this->border, 0, $strAlign, true);
1224
                $iCol += $this->aTotalsColSpan[$iTotalsCol];
1225
            }
1226
            $this->ln();
1227
            $this->restoreSettings($a);
1228
        }
1229
    }
1230
1231
    /**
1232
     * Set the format for the requested totals row.
1233
     * - totals and pagetotals use colors and font from ColHeader <br/>
1234
     * - all other types uses format from subheader <br/>
1235
     * @param int $iTotals
1236
     */
1237
    protected function setTotalsRowFormat(int $iTotals) : void
1238
    {
1239
        if ($iTotals == self::TOTALS || $iTotals == self::PAGE_TOTALS) {
1240
            $this->selectFillColor($this->strColHeaderFillColor);
1241
            $this->selectTextColor($this->strColHeaderTextColor);
1242
            $this->setLineWidth(0.2);
1243
            $this->selectFont($this->fontColHeader);
1244
        } else {
1245
            $this->selectFillColor($this->strSubHeaderFillColor);
1246
            $this->selectTextColor($this->strSubHeaderTextColor);
1247
            $this->setLineWidth(0.2);
1248
            $this->selectFont($this->fontSubHeader);
1249
        }
1250
    }
1251
1252
    /**
1253
     * Get the tect for requested totals row.
1254
     * Get the text dependent on the type of the totals row and replaace
1255
     * all supported placeholders.
1256
     * @param int $iTotals
1257
     * @return string
1258
     */
1259
    protected function getTotalsRowText(int $iTotals) : string
1260
    {
1261
        $strText = '';
1262
        switch ($iTotals) {
1263
            case self::TOTALS:
1264
                $strText = $this->strTotals;
1265
                break;
1266
            case self::PAGE_TOTALS:
1267
                $strText = $this->strPageTotals;
1268
                break;
1269
            case self::CARRY_OVER:
1270
                $strText = $this->strCarryOver;
1271
                break;
1272
            case self::SUB_TOTALS:
1273
                $strText = $this->strSubTotals;
1274
                break;
1275
            default:
1276
                break;
1277
        }
1278
        // replace supported placeholders
1279
        $strText = str_replace('{PN}', strval($this->page), $strText);
1280
        $strText = str_replace('{PN-1}', strval($this->page - 1), $strText);
1281
        return $strText;
1282
    }
1283
1284
    /**
1285
     * Get the calculated values for the requested totals row.
1286
     * @param int $iTotals
1287
     * @return array
1288
     */
1289
    protected function getTotalsRowValues(int $iTotals) : array
1290
    {
1291
        if ($iTotals == self::SUB_TOTALS) {
1292
            return $this->aSubTotals;
1293
        } else {
1294
            return $this->aTotals;
1295
        }
1296
    }
1297
1298
    /**
1299
     * Check, if requested col is set for the output of totals text.
1300
     * @param int $iCol
1301
     * @return bool
1302
     */
1303
    protected function isTotalsTextCol(int $iCol) : bool
1304
    {
1305
        return ($this->aColFlags[$iCol] & self::FLAG_TOTALS_TEXT) != 0;
1306
    }
1307
1308
    /**
1309
     * Check, if requested col is defined for totals calculation.
1310
     * @param int $iCol
1311
     * @return bool
1312
     */
1313
    protected function isTotalsCalcCol(int $iCol) : bool
1314
    {
1315
        return ($this->aColFlags[$iCol] & self::FLAG_TOTALS_CALC) != 0;
1316
    }
1317
1318
    /**
1319
     * Calculates the width of the requested totals col.
1320
     *
1321
     * @param int $iTotalsCol
1322
     * @param int $iCol
1323
     * @return float
1324
     */
1325
    protected function calcTotalsColWidth(int $iTotalsCol, int $iCol) : float
1326
    {
1327
        $fltWidth = 0;
1328
        if ($this->aTotalsColSpan[$iTotalsCol] > 1) {
1329
            $j = 0;
1330
            while ($j < $this->aTotalsColSpan[$iTotalsCol]) {
1331
                $fltWidth += $this->aColWidth[$iCol++];
1332
                $j++;
1333
            }
1334
        } else {
1335
            $fltWidth = $this->aColWidth[$iCol++];
1336
        }
1337
        return $fltWidth;
1338
    }
1339
1340
    /**
1341
     * Calculate totals for given col.
1342
     * @param int $iCol
1343
     * @param mixed $value
1344
     */
1345
    protected function calcTotals(int $iCol, $value) : void
1346
    {
1347
        // calc totals if enabled
1348
        if ($this->bCalcTotals && $this->isTotalsCalcCol($iCol)) {
1349
            if (is_numeric($value)) {
1350
                $this->aTotals[$iCol] += $value;
1351
                $this->aSubTotals[$iCol] += $value;
1352
            }
1353
        }
1354
    }
1355
    /**
1356
     * Check, if requested col is iamge col.
1357
     * @param int $iCol
1358
     * @return bool
1359
     */
1360
    protected function isImageCol(int $iCol) : bool
1361
    {
1362
        return ($this->aColFlags[$iCol] & self::FLAG_IMAGE) != 0;
1363
    }
1364
1365
    /**
1366
     * Draw the image for a image col.
1367
     * @param int $iCol
1368
     * @param string $strImage
1369
     */
1370
    protected function drawImageCol(int $iCol, string $strImage, bool $bFill) : void
1371
    {
1372
        $fltTop = $this->getY();
1373
        $fltLeft = $this->getX();
1374
        $fltHeight = 0;
1375
        $fltWidth = 0;
1376
        if (isset($this->aImgInfo[$iCol])) {
1377
            $fltTop += $this->aImgInfo[$iCol]['fltTop'];
1378
            $fltLeft += $this->aImgInfo[$iCol]['fltLeft'];
1379
            if ($this->aImgInfo[$iCol]['fltHeight'] > 0) {
1380
                $fltHeight = $this->aImgInfo[$iCol]['fltHeight'];
1381
            }
1382
            if ($this->aImgInfo[$iCol]['fltWidth'] > 0) {
1383
                $fltWidth = $this->aImgInfo[$iCol]['fltWidth'];
1384
            }
1385
        }
1386
        $this->cell($this->aColWidth[$iCol], $this->fltLineHeight, '', $this->border, 0, 'C', $bFill);
1387
        $this->image($strImage, $fltLeft, $fltTop, $fltWidth, $fltHeight);
1388
    }
1389
1390
    /**
1391
     * Hook to hide a row dependend on row data.
1392
     * Can be overloaded in subclass to hide a row.
1393
     * @param array $row
1394
     * @return bool    function must return false, if row should not be printed
1395
     */
1396
    protected function isRowVisible(/** @scrutinizer ignore-unused */ array $row) : bool
1397
    {
1398
        return true;
1399
    }
1400
1401
    /**
1402
     * Get link information for requested col.
1403
     * Set text color and underline style if col contains link
1404
     * @param int $iCol
1405
     * @param array $row
1406
     * @return string|int
1407
     */
1408
    protected function getColLink(int $iCol, array $row)
1409
    {
1410
        $strLink = '';
1411
        if (($this->aColFlags[$iCol] & self::FLAG_INT_LINK) != 0) {
1412
            $strLink = $this->internalLink($iCol, $row);
1413
            $this->setFont('', 'U');
1414
            $this->selectTextColor($this->strLinkTextColor);
1415
        }
1416
        return $strLink;
1417
    }
1418
1419
    /**
1420
     * Called before next row is printed.
1421
     * This function can be overloaded in derived class to <ul>
1422
     * <li> change row data or add values </li>
1423
     * <li> specify text for subtitle before row is printed </li></ul>
1424
     * The $row parameter is defined as reference so $row may be changed within function in derived class. <br/>
1425
     * If the method returns a non-empty string, a subtitle containing the text is printed before the row
1426
     * @param array $row
1427
     * @return string
1428
     */
1429
    protected function preRow(/** @scrutinizer ignore-unused */ array &$row) : string
1430
    {
1431
        return '';
1432
    }
1433
1434
    /**
1435
     * Called after the output of a row.
1436
     * To be overloaded in derived class
1437
     * @param array $row
1438
     */
1439
    protected function postRow(/** @scrutinizer ignore-unused */ array $row) : void
1440
    {
1441
    }
1442
1443
    /**
1444
     * Get content of a col for current row - to overide in derived class.
1445
     * @param int $iCol     requested colnumber defined in AddCol()
1446
     * @param array $row    current record from DB
1447
     * @param bool          $bFill
1448
     * @return string
1449
     */
1450
    protected function col(int $iCol, array $row, /** @scrutinizer ignore-unused */ bool &$bFill) : string
1451
    {
1452
        $strCol = '';
1453
        switch ($iCol) {
1454
            case self::COL_ROW_NR:
1455
                $strCol = (string) $this->iRow;
1456
                break;
1457
            default:
1458
                break;
1459
        }
1460
        return $strCol;
1461
    }
1462
1463
    /**
1464
     * @param int $iCol
1465
     * @param array $row
1466
     * @return int
1467
     */
1468
    protected function internalLink(/** @scrutinizer ignore-unused */ int $iCol, /** @scrutinizer ignore-unused */ array $row) : int
1469
    {
1470
        return $this->addLink();
1471
    }
1472
1473
    /**
1474
     * Divides color in HTML notation into red, green and blue component
1475
     * @param string $strColor color in HTML notation #RRGGBB
1476
     * @param int $r    red component of color
1477
     * @param int $g    green component of color
1478
     * @param int $b    blue component of color
1479
     */
1480
    protected function getRGB(string $strColor, int &$r, int &$g, int &$b) : void
1481
    {
1482
        if ($strColor[0] == '#') {
1483
            if (strlen($strColor) == 7) {
1484
                $r = intval(substr($strColor, 1, 2), 16);
1485
                $g = intval(substr($strColor, 3, 2), 16);
1486
                $b = intval(substr($strColor, 5, 2), 16);
1487
            } elseif (strlen($strColor) == 4) {
1488
                $r = intval(substr($strColor, 1, 1), 16);
1489
                $g = intval(substr($strColor, 2, 1), 16);
1490
                $b = intval(substr($strColor, 3, 1), 16);
1491
                $r = $r + (16 * $r);
1492
                $g = $g + (16 * $g);
1493
                $b = $b + (16 * $b);
1494
            }
1495
        }
1496
    }
1497
1498
    /**
1499
     * Save some setting to restore after operations changing settings.
1500
     * @return array
1501
     */
1502
    protected function saveSettings() : array
1503
    {
1504
        $a = array();
1505
1506
        $a['family'] = $this->FontFamily;
1507
        $a['style']  = $this->FontStyle;
1508
        $a['size']   = $this->FontSizePt;
1509
        $a['ul']     = $this->underline;
1510
        $a['lw']     = $this->LineWidth;
1511
        $a['dc']     = $this->DrawColor;
1512
        $a['fc']     = $this->FillColor;
1513
        $a['tc']     = $this->TextColor;
1514
        $a['cf']     = $this->ColorFlag;
1515
1516
        return $a;
1517
    }
1518
1519
    /**
1520
     * Restore settings.
1521
     * Restore only values differing
1522
     * @param array $a
1523
     */
1524
    protected function restoreSettings(array $a) : void
1525
    {
1526
        // Restore line width
1527
        if ($this->LineWidth != $a['lw']) {
1528
            $this->LineWidth = $a['lw'];
1529
            $this->out(sprintf('%.2F w', $a['lw'] * $this->k));
1530
        }
1531
        // Restore font
1532
        if (($a['family'] != $this->FontFamily) ||
1533
             $a['style'] != $this->FontStyle ||
1534
             $a['size'] != $this->FontSizePt) {
1535
            $this->setFont($a['family'], $a['style'], $a['size']);
1536
        }
1537
        $this->underline = $a['ul'];
1538
1539
        // Restore colors
1540
        if ($this->DrawColor != $a['dc']) {
1541
            $this->DrawColor = $a['dc'];
1542
            $this->out($a['dc']);
1543
        }
1544
        if ($this->FillColor != $a['fc']) {
1545
            $this->FillColor = $a['fc'];
1546
            $this->out($a['fc']);
1547
        }
1548
        $this->TextColor = $a['tc'];
1549
        $this->ColorFlag = $a['cf'];
1550
    }
1551
1552
    /**
1553
     * Checks if object contains property with given name.
1554
     * If object doesn't have requested property, default value will be returned
1555
     * @param \stdClass $obj    from JSON-Data
1556
     * @param string $strName
1557
     * @param mixed $default
1558
     * @return mixed
1559
     */
1560
    protected function property(\stdClass $obj, string $strName, $default = '')
1561
    {
1562
        $value = $default;
1563
        if (property_exists($obj, $strName)) {
1564
            $value = $obj->$strName;
1565
        }
1566
        return $value;
1567
    }
1568
1569
    /**
1570
     * Checks if object contains font property with given name.
1571
     * If object doesn't have requested property, default font will be returned
1572
     * @param \stdClass $obj
1573
     * @param string $strName
1574
     * @param XPDFFont $fontDefault
1575
     * @return XPDFFont
1576
     */
1577
    protected function propertyFont(\stdClass $obj, string $strName, XPDFFont $fontDefault) : XPDFFont
1578
    {
1579
        $font = $fontDefault;
1580
        if (property_exists($obj, $strName)) {
1581
            $oFont = $obj->$strName;
1582
            $font = new XPDFFont($oFont->name, $oFont->style, $oFont->size);
1583
        }
1584
        return $font;
1585
    }
1586
1587
    /**
1588
     * @param string $strText
1589
     * @return string
1590
     */
1591
    public function convText(string $strText) : string
1592
    {
1593
        /*
1594
         // $strCharset = mb_detect_encoding($strText);
1595
         if ($this->strCharset != 'ISO-8859-15') {
1596
         $strText = iconv($this->strCharset, 'ISO-8859-15//TRANSLIT', $strText);
1597
         }
1598
         return html_entity_decode($strText, ENT_QUOTES, 'ISO-8859-15');
1599
         */
1600
        trigger_error('remove obsolete call from XPDF::convText()', E_USER_DEPRECATED);
1601
        return $strText;
1602
    }
1603
1604
    /**
1605
     * Formatting of the cell data.
1606
     * @param mixed $value
1607
     * @param int $iFormat
1608
     * @return string
1609
     */
1610
    protected function formatValue($value, int $iFormat) : string
1611
    {
1612
        $strValue = strval($value);
1613
        if (($iFormat & self::FLAG_NO_ZERO) && floatval($value) == 0.0) {
1614
            // suppress zero values...
1615
            $strValue = '';
1616
        } else {
1617
            switch ($iFormat & self::FLAG_FORMAT) {
1618
                case self::FLAG_CUR_SYMBOL:
1619
                    $strValue = $this->formatCurrency(floatval($value), true);
1620
                    break;
1621
                case self::FLAG_CUR_PLAIN:
1622
                    $strValue = $this->formatCurrency(floatval($value), false);
1623
                    break;
1624
                case self::FLAG_DATE:
1625
                    $strValue = $this->formatDate($value);
1626
                    break;
1627
                case self::FLAG_TIME:
1628
                    $strValue = $this->formatTime($value);
1629
                    break;
1630
                case self::FLAG_DATE_TIME:
1631
                    $strValue = $this->formatDateTime($value);
1632
                    break;
1633
                case self::FLAG_NUMBER:
1634
                    $strValue = $this->formatNumber(floatval($value));
1635
                    break;
1636
                case self::FLAG_IBAN:
1637
                    $strValue = $this->formatIBAN($value);
1638
                    break;
1639
            }
1640
        }
1641
        return $strValue;
1642
    }
1643
1644
    /**
1645
     * Formats value as number according to locale settings on system.
1646
     * @param float $fltValue
1647
     * @param int $iDecimals
1648
     * @param string $strPrefix
1649
     * @param string $strSuffix
1650
     * @return string
1651
     */
1652
    protected function formatNumber(float $fltValue, ?int $iDecimals = null, ?string $strPrefix = null, ?string $strSuffix = null) : string
1653
    {
1654
        if (!$this->bInvalidLocale) {
1655
            $li = localeconv();
1656
        } else {
1657
            $li = array('decimal_point' => '.', 'thousands_sep' => ',');
1658
        }
1659
        $iDecimals ??= $this->iNumberDecimals;
1660
        $strPrefix ??= $this->strNumberPrefix;
1661
        $strSuffix ??= $this->strNumberSuffix;
1662
        $strValue = number_format($fltValue, $iDecimals, $li['decimal_point'], $li['thousands_sep']);
1663
        if (strlen($strPrefix) > 0) {
1664
            $strValue .= $strPrefix . $strValue;
1665
        }
1666
        if (strlen($strSuffix) > 0) {
1667
            $strValue = $strValue . $strSuffix;
1668
        }
1669
        return $strValue;
1670
    }
1671
1672
    /**
1673
     * Formats value as localized currency.
1674
     * money_format($format, $number) has been DEPRECATED as of PHP 7.4.0.
1675
     * @param float $fltValue
1676
     * @param bool $bSymbol
1677
     * @return string
1678
     */
1679
    protected function formatCurrency(float $fltValue, bool $bSymbol = true) : string
1680
    {
1681
        if (!$this->bInvalidLocale) {
1682
            $li = localeconv();
1683
        } else {
1684
            $li = [
1685
                'mon_decimal_point' => '.',
1686
                'mon_thousands_sep' => ',',
1687
                'p_cs_precedes' => 1,
1688
                'p_ns_precedes' => 1,
1689
                'p_sep_by_space' => 1,
1690
                'n_sep_by_space' => 1,
1691
                'currency_symbol' => '$',
1692
            ];
1693
            $bSymbol = false;
1694
        }
1695
        $strValue = number_format($fltValue, 2, $li['mon_decimal_point'], $li['mon_thousands_sep']);
1696
        if ($bSymbol) {
1697
            $bPrecedes = ($fltValue >= 0 ? $li['p_cs_precedes'] : $li['n_cs_precedes']);
1698
            $bSpace = ($fltValue >= 0 ? $li['p_sep_by_space'] : $li['n_sep_by_space']);
1699
            $strSep = $bSpace ? ' ' : '';
1700
            if ( $li['currency_symbol'] == '€') {
1701
                $li['currency_symbol'] = '{eur}';
1702
            }
1703
            if ($bPrecedes) {
1704
                $strValue = $li['currency_symbol'] . $strSep . $strValue;
1705
            } else {
1706
                $strValue = $strValue . $strSep . $li['currency_symbol'];
1707
            }
1708
        }
1709
        return $strValue;
1710
    }
1711
1712
    /**
1713
     * Format a date cell.
1714
     * @param int|string $Date unix timestamp or valid Date string
1715
     * @return string
1716
     */
1717
    protected function formatDate($Date) : string
1718
    {
1719
        $strDate = '';
1720
        $oDate = null;
1721
        if (is_numeric($Date)) {
1722
            $oDate = new \DateTime();
1723
            $oDate->setTimestamp($Date);
0 ignored issues
show
Bug introduced by
It seems like $Date can also be of type string; however, parameter $timestamp of DateTime::setTimestamp() does only seem to accept integer, 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

1723
            $oDate->setTimestamp(/** @scrutinizer ignore-type */ $Date);
Loading history...
1724
        } else {
1725
            $oDate = new \DateTime($Date);
1726
        }
1727
        if ($oDate !== null) {
1728
            $strDate = $this->oDateFormatter->format($oDate);
1729
        }
1730
        return $strDate;
1731
    }
1732
1733
    /**
1734
     * Format a time cell.
1735
     * @param int|string  $Time unix timestamp or valid Time string
1736
     * @return string
1737
     */
1738
    protected function formatTime($Time) : string
1739
    {
1740
        $strTime = '';
1741
        $oTime = null;
1742
        if (is_numeric($Time)) {
1743
            $oTime = new \DateTime();
1744
            $oTime->setTimestamp($Time);
0 ignored issues
show
Bug introduced by
It seems like $Time can also be of type string; however, parameter $timestamp of DateTime::setTimestamp() does only seem to accept integer, 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

1744
            $oTime->setTimestamp(/** @scrutinizer ignore-type */ $Time);
Loading history...
1745
        } else {
1746
            $oTime = new \DateTime($Time);
1747
        }
1748
        if ($oTime !== null) {
1749
            $strTime = $this->oTimeFormatter->format($oTime);
1750
        }
1751
        return $strTime;
1752
    }
1753
1754
    /**
1755
     * Format a date-time cell.
1756
     * @param int|string $DT    unix timestamp or valid DateTime string
1757
     * @return string
1758
     */
1759
    protected function formatDateTime($DT) : string
1760
    {
1761
        $strDT = '';
1762
        $oDT = null;
1763
        if (is_numeric($DT)) {
1764
            $oDT = new \DateTime();
1765
            $oDT->setTimestamp($DT);
0 ignored issues
show
Bug introduced by
It seems like $DT can also be of type string; however, parameter $timestamp of DateTime::setTimestamp() does only seem to accept integer, 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

1765
            $oDT->setTimestamp(/** @scrutinizer ignore-type */ $DT);
Loading history...
1766
        } else {
1767
            $oDT = new \DateTime($DT);
1768
        }
1769
        if ($oDT !== null) {
1770
            $strDT = $this->oDTFormatter->format($oDT);
1771
        }
1772
        return $strDT;
1773
    }
1774
1775
    /**
1776
     * Format IBAN.
1777
     * @param string $strIBAN
1778
     * @return string
1779
     */
1780
    protected function formatIBAN(string $strIBAN) : string
1781
    {
1782
        $strIBAN = str_replace(' ', '', $strIBAN);
1783
        $strFormatedIBAN = '';
1784
        $iLen = strlen($strIBAN);
1785
        for ($i = 0; $i < $iLen; $i++) {
1786
            if ($i > 0 && $i % 4 == 0) {
1787
                $strFormatedIBAN .= ' ';
1788
            }
1789
            $strFormatedIBAN .= $strIBAN[$i];
1790
        }
1791
        return $strFormatedIBAN;
1792
    }
1793
1794
    /**
1795
     * Replace the placeholders that can be used in header and footer.
1796
     * @param string $strText
1797
     * @return string
1798
     */
1799
    protected function replacePlaceholder(string $strText) : string
1800
    {
1801
        $oDateFormatter = new IntlDateFormatter($this->strLocale, IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE);
1802
        $strText = str_replace("{D}", $oDateFormatter->format(new \DateTime()), $strText);
1803
        $oTimeFormatter = new IntlDateFormatter($this->strLocale, IntlDateFormatter::NONE, IntlDateFormatter::MEDIUM);
1804
        $strText = str_replace("{T}", $oTimeFormatter->format(new \DateTime()), $strText);
1805
        $strText = str_replace("{PN}", strval($this->pageNo()), $strText);
1806
1807
        return $strText;
1808
    }
1809
}
1810