HtmlRenderer::createCell()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 14
dl 0
loc 3
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2025 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Report;
21
22
use Fisharebest\Webtrees\I18N;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\I18N was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use Fisharebest\Webtrees\MediaFile;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\MediaFile was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use Fisharebest\Webtrees\Webtrees;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Webtrees was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
26
use function array_map;
27
use function ceil;
28
use function count;
29
use function explode;
30
use function implode;
31
use function preg_match;
32
use function str_replace;
33
use function stripos;
34
use function strrpos;
35
use function substr;
36
use function substr_count;
37
38
class HtmlRenderer extends AbstractRenderer
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Report\AbstractRenderer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
39
{
40
    // Cell padding
41
    public float $cPadding = 2;
42
43
    // Cell height ratio
44
    public float $cellHeightRatio = 1.8;
45
46
    // Current horizontal position
47
    public float $X = 0.0;
48
49
    // Current vertical position
50
    public float $Y = 0.0;
51
52
    // Page number counter
53
    public int $pageN = 1;
54
55
    // Store the page width without left and right margins
56
    // Only needed for PDF reports
57
    public float $noMarginWidth = 0.0;
58
59
    // Last cell height
60
    public float $lastCellHeight = 0.0;
61
62
    // LTR or RTL alignement; "left" on LTR, "right" on RTL
63
    // Used in <div>
64
    public string $alignRTL = 'left';
65
66
    // LTR or RTL entity
67
    public string $entityRTL = '&lrm;';
68
69
    // Largest Font Height is used by TextBox etc.
70
    //
71
    // Use this to calculate a the text height.
72
    // This makes sure that the text fits into the cell/box when different font sizes are used
73
    public float $largestFontHeight = 0;
74
75
    // Keep track of the highest Y position
76
    // Used with Header div / Body div / Footer div / "addpage" / The bottom of the last image etc.
77
    public float $maxY = 0;
78
79
    /**
80
     * @var ReportHtmlFootnote[] Array of elements in the footer notes
81
     */
82
    public array $printedfootnotes = [];
83
84
    /**
85
     * HTML Setup - ReportHtml
86
     *
87
     * @return void
88
     */
89
    public function setup(): void
90
    {
91
        parent::setup();
92
93
        // Setting up the correct dimensions if Portrait (default) or Landscape
94
        if ($this->orientation === 'landscape') {
95
            $tmpw              = $this->page_width;
96
            $this->page_width  = $this->page_height;
0 ignored issues
show
Bug Best Practice introduced by
The property page_width does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
97
            $this->page_height = $tmpw;
0 ignored issues
show
Bug Best Practice introduced by
The property page_height does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
98
        }
99
        // Store the pagewidth without margins
100
        $this->noMarginWidth = $this->page_width - $this->left_margin - $this->right_margin;
101
        // If RTL
102
        if ($this->rtl) {
103
            $this->alignRTL  = 'right';
104
            $this->entityRTL = '&rlm;';
105
        }
106
        // Change the default HTML font name
107
        $this->default_font = 'Arial';
0 ignored issues
show
Bug Best Practice introduced by
The property default_font does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
108
109
        if ($this->show_generated_by) {
110
            // The default style name for Generated by.... is 'genby'
111
            $element = new ReportHtmlCell(0.0, 10.0, '', 'C', '', 'genby', 1, ReportBaseElement::CURRENT_POSITION, ReportBaseElement::CURRENT_POSITION, false, 0, '', '', true);
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Report\ReportBaseElement was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
112
            $element->addText($this->generated_by);
113
            $element->setUrl(Webtrees::URL);
114
            $this->footerElements[] = $element;
0 ignored issues
show
Bug Best Practice introduced by
The property footerElements does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
115
        }
116
    }
117
118
    /**
119
     * Generate footnotes
120
     *
121
     * @return void
122
     */
123
    public function footnotes(): void
124
    {
125
        $this->currentStyle = '';
0 ignored issues
show
Bug Best Practice introduced by
The property currentStyle does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
126
        if (!empty($this->printedfootnotes)) {
127
            foreach ($this->printedfootnotes as $element) {
128
                $element->renderFootnote($this);
129
            }
130
        }
131
    }
132
133
    /**
134
     * Run the report.
135
     *
136
     * @return void
137
     */
138
    public function run(): void
139
    {
140
        // Setting up the styles
141
        echo '<style>';
142
        echo '#bodydiv { font: 10px sans-serif;}';
143
        foreach ($this->styles as $class => $style) {
144
            echo '.', $class, ' { ';
145
            if ($style['font'] === 'dejavusans') {
146
                $style['font'] = $this->default_font;
147
            }
148
            echo 'font-family: ', $style['font'], '; ';
149
            echo 'font-size: ', $style['size'], 'pt; ';
150
            // Case-insensitive
151
            if (stripos($style['style'], 'B') !== false) {
152
                echo 'font-weight: bold; ';
153
            }
154
            if (stripos($style['style'], 'I') !== false) {
155
                echo 'font-style: italic; ';
156
            }
157
            if (stripos($style['style'], 'U') !== false) {
158
                echo 'text-decoration: underline; ';
159
            }
160
            if (stripos($style['style'], 'D') !== false) {
161
                echo 'text-decoration: line-through; ';
162
            }
163
            echo '}', PHP_EOL;
164
        }
165
166
        //-- header divider
167
        echo '</style>', PHP_EOL;
168
        echo '<div id="headermargin" style="position: relative; top: auto; height: ', $this->header_margin, 'pt; width: ', $this->noMarginWidth, 'pt;"></div>';
169
        echo '<div id="headerdiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt;">';
170
        foreach ($this->headerElements as $element) {
171
            if ($element instanceof ReportBaseElement) {
172
                $element->render($this);
173
            } elseif ($element === 'footnotetexts') {
174
                $this->footnotes();
175
            } elseif ($element === 'addpage') {
176
                $this->addPage();
177
            }
178
        }
179
        //-- body
180
        echo '</div>';
181
        echo '<script>document.getElementById("headerdiv").style.height="', $this->top_margin - $this->header_margin - 6, 'pt";</script>';
182
        echo '<div id="bodydiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt; height: 100%;">';
183
        $this->Y    = 0;
184
        $this->maxY = 0;
185
        foreach ($this->bodyElements as $element) {
186
            if ($element instanceof ReportBaseElement) {
187
                $element->render($this);
188
            } elseif ($element === 'footnotetexts') {
189
                $this->footnotes();
190
            } elseif ($element === 'addpage') {
191
                $this->addPage();
192
            }
193
        }
194
        //-- footer
195
        echo '</div>';
196
        echo '<script>document.getElementById("bodydiv").style.height="', $this->maxY, 'pt";</script>';
197
        echo '<div id="bottommargin" style="position: relative; top: auto; height: ', $this->bottom_margin - $this->footer_margin, 'pt;width:', $this->noMarginWidth, 'pt;"></div>';
198
        echo '<div id="footerdiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt;height:auto;">';
199
        $this->Y    = 0;
200
        $this->X    = 0;
201
        $this->maxY = 0;
202
        foreach ($this->footerElements as $element) {
203
            if ($element instanceof ReportBaseElement) {
204
                $element->render($this);
205
            } elseif ($element === 'footnotetexts') {
206
                $this->footnotes();
207
            } elseif ($element === 'addpage') {
208
                $this->addPage();
209
            }
210
        }
211
        echo '</div>';
212
        echo '<script>document.getElementById("footerdiv").style.height="', $this->maxY, 'pt";</script>';
213
        echo '<div id="footermargin" style="position: relative; top: auto; height: ', $this->footer_margin, 'pt;width:', $this->noMarginWidth, 'pt;"></div>';
214
    }
215
216
    /**
217
     * Create a new Cell object.
218
     *
219
     * @param float  $width   cell width (expressed in points)
220
     * @param float  $height  cell height (expressed in points)
221
     * @param string $border  Border style
222
     * @param string $align   Text alignement
223
     * @param string $bgcolor Background color code
224
     * @param string $style   The name of the text style
225
     * @param int    $ln      Indicates where the current position should go after the call
226
     * @param float  $top     Y-position
227
     * @param float  $left    X-position
228
     * @param bool   $fill    Indicates if the cell background must be painted (1) or transparent (0). Default value: 1
229
     * @param int    $stretch Stretch character mode
230
     * @param string $bocolor Border color
231
     * @param string $tcolor  Text color
232
     * @param bool   $reseth
233
     *
234
     * @return ReportBaseCell
235
     */
236
    public function createCell(float $width, float $height, string $border, string $align, string $bgcolor, string $style, int $ln, float $top, float $left, bool $fill, int $stretch, string $bocolor, string $tcolor, bool $reseth): ReportBaseCell
237
    {
238
        return new ReportHtmlCell($width, $height, $border, $align, $bgcolor, $style, $ln, $top, $left, $fill, $stretch, $bocolor, $tcolor, $reseth);
239
    }
240
241
    /**
242
     * Create a new TextBox object.
243
     *
244
     * @param float  $width   Text box width
245
     * @param float  $height  Text box height
246
     * @param bool   $border
247
     * @param string $bgcolor Background color code in HTML
248
     * @param bool   $newline
249
     * @param float  $left
250
     * @param float  $top
251
     * @param bool   $pagecheck
252
     * @param string $style
253
     * @param bool   $fill
254
     * @param bool   $padding
255
     * @param bool   $reseth
256
     *
257
     * @return ReportBaseTextbox
258
     */
259
    public function createTextBox(
260
        float $width,
261
        float $height,
262
        bool $border,
263
        string $bgcolor,
264
        bool $newline,
265
        float $left,
266
        float $top,
267
        bool $pagecheck,
268
        string $style,
269
        bool $fill,
270
        bool $padding,
271
        bool $reseth
272
    ): ReportBaseTextbox {
273
        return new ReportHtmlTextbox($width, $height, $border, $bgcolor, $newline, $left, $top, $pagecheck, $style, $fill, $padding, $reseth);
274
    }
275
276
    /**
277
     * Create a text element.
278
     *
279
     * @param string $style
280
     * @param string $color
281
     *
282
     * @return ReportBaseText
283
     */
284
    public function createText(string $style, string $color): ReportBaseText
285
    {
286
        return new ReportHtmlText($style, $color);
287
    }
288
289
    /**
290
     * Create a new Footnote object.
291
     *
292
     * @param string $style Style name
293
     *
294
     * @return ReportBaseFootnote
295
     */
296
    public function createFootnote(string $style): ReportBaseFootnote
297
    {
298
        return new ReportHtmlFootnote($style);
299
    }
300
301
    /**
302
     * Create a new image object.
303
     *
304
     * @param string $file  Filename
305
     * @param float  $x
306
     * @param float  $y
307
     * @param float  $w     Image width
308
     * @param float  $h     Image height
309
     * @param string $align L:left, C:center, R:right or empty to use x/y
310
     * @param string $ln    T:same line, N:next line
311
     *
312
     * @return ReportBaseImage
313
     */
314
    public function createImage(string $file, float $x, float $y, float $w, float $h, string $align, string $ln): ReportBaseImage
315
    {
316
        return new ReportHtmlImage($file, $x, $y, $w, $h, $align, $ln);
317
    }
318
319
    /**
320
     * Create a new image object from Media Object.
321
     *
322
     * @param MediaFile $media_file
323
     * @param float     $x
324
     * @param float     $y
325
     * @param float     $w     Image width
326
     * @param float     $h     Image height
327
     * @param string    $align L:left, C:center, R:right or empty to use x/y
328
     * @param string    $ln    T:same line, N:next line
329
     *
330
     * @return ReportBaseImage
331
     */
332
    public function createImageFromObject(
333
        MediaFile $media_file,
334
        float $x,
335
        float $y,
336
        float $w,
337
        float $h,
338
        string $align,
339
        string $ln
340
    ): ReportBaseImage {
341
        return new ReportHtmlImage($media_file->imageUrl((int) $w, (int) $h, 'crop'), $x, $y, $w, $h, $align, $ln);
342
    }
343
344
    /**
345
     * Create a line.
346
     *
347
     * @param float $x1
348
     * @param float $y1
349
     * @param float $x2
350
     * @param float $y2
351
     *
352
     * @return ReportBaseLine
353
     */
354
    public function createLine(float $x1, float $y1, float $x2, float $y2): ReportBaseLine
355
    {
356
        return new ReportHtmlLine($x1, $y1, $x2, $y2);
357
    }
358
359
    /**
360
     * Clear the Header
361
     *
362
     * @return void
363
     */
364
    public function clearHeader(): void
365
    {
366
        $this->headerElements = [];
0 ignored issues
show
Bug Best Practice introduced by
The property headerElements does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
367
    }
368
369
    /**
370
     * Update the Page Number and set a new Y if max Y is larger - ReportHtml
371
     *
372
     * @return void
373
     */
374
    public function addPage(): void
375
    {
376
        $this->pageN++;
377
378
        // Add a little margin to max Y "between pages"
379
        $this->maxY += 10;
380
381
        // If Y is still heigher by any reason...
382
        if ($this->maxY < $this->Y) {
383
            // ... update max Y
384
            $this->maxY = $this->Y;
385
        } else {
386
            // else update Y so that nothing will be overwritten, like images or cells...
387
            $this->Y = $this->maxY;
388
        }
389
    }
390
391
    /**
392
     * Uppdate max Y to keep track it in case of a pagebreak - ReportHtml
393
     *
394
     * @param float $y
395
     *
396
     * @return void
397
     */
398
    public function addMaxY(float $y): void
399
    {
400
        if ($this->maxY < $y) {
401
            $this->maxY = $y;
402
        }
403
    }
404
405
    /**
406
     * Checks the Footnote and numbers them - ReportHtml
407
     *
408
     * @param ReportHtmlFootnote $footnote
409
     *
410
     * @return ReportHtmlFootnote|bool object if already numbered, false otherwise
411
     */
412
    public function checkFootnote(ReportHtmlFootnote $footnote)
413
    {
414
        $ct  = count($this->printedfootnotes);
415
        $i   = 0;
416
        $val = $footnote->getValue();
417
        while ($i < $ct) {
418
            if ($this->printedfootnotes[$i]->getValue() === $val) {
419
                // If this footnote already exist then set up the numbers for this object
420
                $footnote->setNum($i + 1);
421
                $footnote->setAddlink((string) ($i + 1));
422
423
                return $this->printedfootnotes[$i];
424
            }
425
            $i++;
426
        }
427
        // If this Footnote has not been set up yet
428
        $footnote->setNum($ct + 1);
429
        $footnote->setAddlink((string) ($ct + 1));
430
        $this->printedfootnotes[] = $footnote;
431
432
        return false;
433
    }
434
435
    /**
436
     * Count the number of lines - ReportHtml
437
     *
438
     * @param string $str
439
     *
440
     * @return int Number of lines. 0 if empty line
441
     */
442
    public function countLines(string $str): int
443
    {
444
        if ($str === '') {
445
            return 0;
446
        }
447
448
        return substr_count($str, "\n") + 1;
449
    }
450
451
    /**
452
     * Get the current style.
453
     *
454
     * @return string
455
     */
456
    public function getCurrentStyle(): string
457
    {
458
        return $this->currentStyle;
459
    }
460
461
    /**
462
     * Get the current style height.
463
     *
464
     * @return float
465
     */
466
    public function getCurrentStyleHeight(): float
467
    {
468
        if (empty($this->currentStyle)) {
469
            return $this->default_font_size;
470
        }
471
        $style = $this->getStyle($this->currentStyle);
472
473
        return $style['size'];
474
    }
475
476
    /**
477
     * Get the current footnotes height.
478
     *
479
     * @param float $cellWidth
480
     *
481
     * @return float
482
     */
483
    public function getFootnotesHeight(float $cellWidth): float
484
    {
485
        $h = 0;
486
        foreach ($this->printedfootnotes as $element) {
487
            $h += $element->getFootnoteHeight($this, $cellWidth);
488
        }
489
490
        return $h;
491
    }
492
493
    /**
494
     * Get the maximum width from current position to the margin - ReportHtml
495
     *
496
     * @return float
497
     */
498
    public function getRemainingWidth(): float
499
    {
500
        return $this->noMarginWidth - $this->X;
501
    }
502
503
    /**
504
     * Get the page height.
505
     *
506
     * @return float
507
     */
508
    public function getPageHeight(): float
509
    {
510
        return $this->page_height - $this->top_margin;
511
    }
512
513
    /**
514
     * Get the width of a string.
515
     *
516
     * @param string $text
517
     *
518
     * @return float
519
     */
520
    public function getStringWidth(string $text): float
521
    {
522
        $style = $this->getStyle($this->currentStyle);
523
524
        return mb_strlen($text) * $style['size'] / 2;
525
    }
526
527
    /**
528
     * Get a text height in points - ReportHtml
529
     *
530
     * @param string $str
531
     *
532
     * @return float
533
     */
534
    public function getTextCellHeight(string $str): float
535
    {
536
        // Count the number of lines to calculate the height
537
        $nl = $this->countLines($str);
538
539
        // Calculate the cell height
540
        return ceil($this->getCurrentStyleHeight() * $this->cellHeightRatio * $nl);
541
    }
542
543
    /**
544
     * Get the current X position - ReportHtml
545
     *
546
     * @return float
547
     */
548
    public function getX(): float
549
    {
550
        return $this->X;
551
    }
552
553
    /**
554
     * Get the current Y position - ReportHtml
555
     *
556
     * @return float
557
     */
558
    public function getY(): float
559
    {
560
        return $this->Y;
561
    }
562
563
    /**
564
     * Get the current page number - ReportHtml
565
     *
566
     * @return int
567
     */
568
    public function pageNo(): int
569
    {
570
        return $this->pageN;
571
    }
572
573
    /**
574
     * Set the current style.
575
     *
576
     * @param string $s
577
     *
578
     * @void
579
     */
580
    public function setCurrentStyle(string $s): void
581
    {
582
        $this->currentStyle = $s;
0 ignored issues
show
Bug Best Practice introduced by
The property currentStyle does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
583
    }
584
585
    /**
586
     * Set the X position - ReportHtml
587
     *
588
     * @param float $x
589
     *
590
     * @return void
591
     */
592
    public function setX(float $x): void
593
    {
594
        $this->X = $x;
595
    }
596
597
    /**
598
     * Set the Y position - ReportHtml
599
     *
600
     * Also updates Max Y position
601
     *
602
     * @param float $y
603
     *
604
     * @return void
605
     */
606
    public function setY(float $y): void
607
    {
608
        $this->Y = $y;
609
        if ($this->maxY < $y) {
610
            $this->maxY = $y;
611
        }
612
    }
613
614
    /**
615
     * Set the X and Y position - ReportHtml
616
     *
617
     * Also updates Max Y position
618
     *
619
     * @param float $x
620
     * @param float $y
621
     *
622
     * @return void
623
     */
624
    public function setXy(float $x, float $y): void
625
    {
626
        $this->setX($x);
627
        $this->setY($y);
628
    }
629
630
    /**
631
     * Wrap text - ReportHtml
632
     *
633
     * @param string $str   Text to wrap
634
     * @param float  $width Width in points the text has to fit into
635
     *
636
     * @return string
637
     */
638
    public function textWrap(string $str, float $width): string
639
    {
640
        $line_width = (int) ($width / ($this->getCurrentStyleHeight() / 2));
641
642
        $lines = explode("\n", $str);
643
644
        $lines = array_map(fn (string $string): string => $this->utf8WordWrap($string, $line_width), $lines);
645
646
        return implode("\n", $lines);
647
    }
648
649
    /**
650
     * Wrap text, similar to the PHP wordwrap() function.
651
     *
652
     * @param string $string
653
     * @param int    $width
654
     *
655
     * @return string
656
     */
657
    private function utf8WordWrap(string $string, int $width): string
658
    {
659
        $out = '';
660
        while ($string) {
661
            if (mb_strlen($string) <= $width) {
662
                // Do not wrap any text that is less than the output area.
663
                $out .= $string;
664
                $string = '';
665
            } else {
666
                $sub1 = mb_substr($string, 0, $width + 1);
667
                if (mb_substr($string, mb_strlen($sub1) - 1, 1) === ' ') {
668
                    // include words that end by a space immediately after the area.
669
                    $sub = $sub1;
670
                } else {
671
                    $sub = mb_substr($string, 0, $width);
672
                }
673
                $spacepos = strrpos($sub, ' ');
674
                if ($spacepos === false) {
675
                    // No space on line?
676
                    $out .= $sub . "\n";
677
                    $string = mb_substr($string, mb_strlen($sub));
678
                } else {
679
                    // Split at space;
680
                    $out .= substr($string, 0, $spacepos) . "\n";
681
                    $string = substr($string, $spacepos + 1);
682
                }
683
            }
684
        }
685
686
        return $out;
687
    }
688
689
    /**
690
     * Write text - ReportHtml
691
     *
692
     * @param string $text  Text to print
693
     * @param string $color HTML RGB color code (Ex: #001122)
694
     * @param bool   $useclass
695
     *
696
     * @return void
697
     */
698
    public function write(string $text, string $color = '', bool $useclass = true): void
699
    {
700
        $style    = $this->getStyle($this->getCurrentStyle());
701
        $htmlcode = '<span dir="' . I18N::direction() . '"';
702
        if ($useclass) {
703
            $htmlcode .= ' class="' . $style['name'] . '"';
704
        }
705
        // Check if Text Color is set and if it’s valid HTML color
706
        if (preg_match('/#?(..)(..)(..)/', $color)) {
707
            $htmlcode .= ' style="color:' . $color . ';"';
708
        }
709
710
        $htmlcode .= '>' . $text . '</span>';
711
        $htmlcode = str_replace([
712
            "\n",
713
            '> ',
714
            ' <',
715
        ], [
716
            '<br>',
717
            '>&nbsp;',
718
            '&nbsp;<',
719
        ], $htmlcode);
720
        echo $htmlcode;
721
    }
722
}
723