Passed
Pull Request — main (#4945)
by
unknown
15:00
created

ReportHtmlTextbox   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 278
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 165
dl 0
loc 278
rs 6.4799
c 0
b 0
f 0
wmc 54

1 Method

Rating   Name   Duplication   Size   Complexity  
F render() 0 269 54

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2023 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 function abs;
23
use function count;
24
use function is_object;
25
use function ksort;
26
use function str_replace;
27
use function trim;
28
29
/**
30
 * Class ReportHtmlTextbox
31
 */
32
class ReportHtmlTextbox extends ReportBaseTextbox
33
{
34
    /**
35
     * Render the elements.
36
     *
37
     * @param HtmlRenderer $renderer
38
     *
39
     * @return void
40
     */
41
    public function render($renderer): void
42
    {
43
        static $lastBoxYfinal;
44
        // checkFootnote
45
        $newelements      = [];
46
        $lastelement      = [];
47
        $footnote_element = [];
48
        // Element counter
49
        $cE = count($this->elements);
50
        //-- collapse duplicate elements
51
        for ($i = 0; $i < $cE; $i++) {
52
            $element = $this->elements[$i];
53
            if ($element instanceof ReportBaseElement) {
54
                if ($element instanceof ReportBaseText) {
55
                    if (!empty($footnote_element)) {
56
                        ksort($footnote_element);
57
                        foreach ($footnote_element as $links) {
58
                            $newelements[] = $links;
59
                        }
60
                        $footnote_element = [];
61
                    }
62
                    if (empty($lastelement)) {
63
                        $lastelement = $element;
64
                    } elseif ($element->getStyleName() === $lastelement->getStyleName()) {
65
                        // Checking if the Text has the same style
66
                        $lastelement->addText(str_replace("\n", '<br>', $element->getValue()));
67
                    } else {
68
                        $newelements[] = $lastelement;
69
                        $lastelement   = $element;
70
                    }
71
                } elseif ($element instanceof ReportHtmlImage) {
72
                    $lastelement   = $element;
73
                } elseif ($element instanceof ReportHtmlFootnote) {
74
                    // Check if the Footnote has been set with it’s link number
75
                    $renderer->checkFootnote($element);
76
                    // Save first the last element if any
77
                    if (!empty($lastelement)) {
78
                        $newelements[] = $lastelement;
79
                        $lastelement   = [];
80
                    }
81
                    // Save the Footnote with it’s link number as key for sorting later
82
                    $footnote_element[$element->num] = $element;
83
                } elseif (trim($element->getValue()) !== '') {
84
                    // Do not keep empty footnotes
85
                    if (!empty($footnote_element)) {
86
                        ksort($footnote_element);
87
                        foreach ($footnote_element as $links) {
88
                            $newelements[] = $links;
89
                        }
90
                        $footnote_element = [];
91
                    }
92
                    if (!empty($lastelement)) {
93
                        $newelements[] = $lastelement;
94
                        $lastelement   = [];
95
                    }
96
                    $newelements[] = $element;
97
                }
98
            } else {
99
                if (!empty($lastelement)) {
100
                    $newelements[] = $lastelement;
101
                    $lastelement   = [];
102
                }
103
                if (!empty($footnote_element)) {
104
                    ksort($footnote_element);
105
                    foreach ($footnote_element as $links) {
106
                        $newelements[] = $links;
107
                    }
108
                    $footnote_element = [];
109
                }
110
                $newelements[] = $element;
111
            }
112
        }
113
        if (!empty($lastelement)) {
114
            $newelements[] = $lastelement;
115
        }
116
        if (!empty($footnote_element)) {
117
            ksort($footnote_element);
118
            foreach ($footnote_element as $links) {
119
                $newelements[] = $links;
120
            }
121
        }
122
        $this->elements = $newelements;
0 ignored issues
show
Documentation Bug introduced by
$newelements is of type array<mixed,Fisharebest\...tHtmlImage|array|mixed>, but the property $elements was declared to be of type array<mixed,Fisharebest\...portBaseElement|string>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
123
        unset($footnote_element, $lastelement, $newelements);
124
125
        $cP = 0; // Class Padding
126
127
        // Used with line breaks and cell height calculation within this box only
128
        $renderer->largestFontHeight = 0;
129
130
        // If current position (left)
131
        if ($this->left === ReportBaseElement::CURRENT_POSITION) {
132
            $cX = $renderer->getX();
133
        } else {
134
            $cX = $this->left;
135
            $renderer->setX($cX);
136
        }
137
        // If current position (top)
138
        $align_Y = false;
139
        $topstr = "";
0 ignored issues
show
Unused Code introduced by
The assignment to $topstr is dead and can be removed.
Loading history...
140
        if ($this->top < -110000) // pos='abs'
141
            $this->top += 222000; 
142
        if ($this->top < -10000) { // <= -100000: both pdf and html; -100000 -- -90000: only html
143
            $this->top += 90000;  //= ReportBaseElement::CURRENT_POSITION;
144
            if ($this->top < -9000) $this->top += 10000; 
145
            $topstr = "top:".$this->top."pt;";
146
            $align_Y = true;
147
        }
148
        if ($this->top === ReportBaseElement::CURRENT_POSITION) {
149
            $this->top = $renderer->getY();
150
        } else {
151
            $renderer->setY($this->top);
152
        }
153
154
        // Check the width if set to page wide OR set by xml to larger then page width (margin)
155
        if ($this->width === 0.0 || $this->width > $renderer->getRemainingWidth()) {
156
            $this->width = $renderer->getRemainingWidth();
157
        }
158
        // Setup the CellPadding
159
        if ($this->padding) {
160
            $cP = $renderer->cPadding;
161
        }
162
163
        // For padding, we have to use less wrap width
164
        $cW = $this->width - $cP * 2.0;
165
166
        //-- calculate the text box height
167
        // Number of lines, will be converted to height
168
        $cHT = 0;
169
        // Element height (except text)
170
        $eH = 0.0;
171
        // Footnote height (in points)
172
        $fH = 0;
173
        $w  = 0;
174
        //-- $lw is an array
175
        // 0 => last line width
176
        // 1 => 1 if text was wrapped, 0 if text did not wrap
177
        // 2 => number of LF
178
        $lw = [];
179
        // Element counter
180
        $cE = count($this->elements);
181
        for ($i = 0; $i < $cE; $i++) {
182
            if (is_object($this->elements[$i])) {
183
                $ew = $this->elements[$i]->setWrapWidth($cW - $w - 2, $cW);
184
                if ($ew === $cW) {
185
                    $w = 0;
186
                }
187
                $lw = $this->elements[$i]->getWidth($renderer);
188
                // Text is already gets the # LF
189
                $cHT += $lw[2];
190
                if ($lw[1] === 1) {
191
                    $w = $lw[0];
192
                } elseif ($lw[1] === 2) {
193
                    $w = 0;
194
                } else {
195
                    $w += $lw[0];
196
                }
197
                if ($w > $cW) {
198
                    $w = $lw[0];
199
                }
200
                // For anything else but text (images), get the height
201
                $eH += $this->elements[$i]->getHeight($renderer);
202
            } else {
203
                $fH += abs($renderer->getFootnotesHeight($cW));
204
            }
205
        }
206
207
        // Add up what’s the final height
208
        //$cH = $this->height;
209
        $cH = 0;
210
        // If any element exist
211
        if ($cE > 0) {
212
            // Check if this is text or some other element, like images
213
            if ($eH === 0.0) {
0 ignored issues
show
introduced by
The condition $eH === 0.0 is always true.
Loading history...
214
                // Number of LF but at least one line
215
                $cHT = ($cHT + 1) * $renderer->cellHeightRatio;
216
                // Calculate the cell height with the largest font size used
217
                $cHT *= $renderer->largestFontHeight;
218
                if ($cH < $cHT) {
219
                    $cH = $cHT;
220
                }
221
            } else {
222
                // This is any other element
223
                if ($cH < $eH) {
224
                    $cH = $eH;
225
                }
226
                // Add Footnote height to the rest of the height
227
                $cH += $fH;
228
            }
229
        }
230
231
        unset($lw, $cHT, $fH, $w);
232
233
        // Finally, check the last cells height
234
        if ($cH < $renderer->lastCellHeight) {
235
            $cH = $renderer->lastCellHeight;
236
        }
237
        // Update max Y in case of a pagebreak
238
        // We don't want to over write any images or other stuff
239
        $renderer->addMaxY($this->top + $cH);
240
241
        // Start to print HTML
242
        if (!$align_Y)
243
            echo '<div style="position:absolute;top:', $this->top, 'pt;';
244
        else
245
            echo '<div style="position:relative;top:', $this->top, 'pt;';
246
            //echo '<div style="position:relative;';
247
        // LTR (left) or RTL (right)
248
        echo $renderer->alignRTL, ':', $cX, 'pt;';
249
        // Background color
250
        if ($this->fill && $this->bgcolor !== '') {
251
            echo ' background-color:', $this->bgcolor, ';';
252
        }
253
        // Print padding only when it’s set
254
        if ($this->padding) {
255
            // Use Cell around padding to support RTL also
256
            echo 'padding:', $cP, 'pt;';
257
        }
258
        // Border setup
259
        if ($this->border) {
260
            echo ' border:solid black 1pt;';
261
            if (!$align_Y)
262
                echo 'width:', $this->width - 1 - $cP * 2, 'pt;height:', $cH - 1, 'pt;';
263
            else
264
                echo 'width:', $this->width - 1 - $cP * 2, 'pt;height:auto;'; // height:',$this->height,'pt;'; //,$topstr;
265
        } else {
266
            if (!$align_Y)
267
                echo 'width:', $this->width - $cP * 2, 'pt;height:', $cH, 'pt;';
268
            else
269
                echo 'width:', $this->width - $cP * 2, 'pt;height:auto;'; //height:',$this->height,'pt;'; //,$topstr;
270
        }
271
        echo '">';
272
273
        // Do a little "margin" trick before print
274
        // to get the correct current position => "."
275
        $cXT = $renderer->getX();
276
        $cYT = $renderer->getY();
277
        $renderer->setXy(0, 0);
278
279
        // Print the text elements
280
        foreach ($this->elements as $element) {
281
            if ($element instanceof ReportHtmlText) {
282
                $element->render($renderer, false);
283
            } elseif ($element instanceof ReportBaseElement) {
284
                $element->render($renderer);
285
            } elseif ($element === 'footnotetexts') {
286
                $renderer->footnotes();
287
            } elseif ($element === 'addpage') {
288
                $renderer->addPage();
289
            }
290
        }
291
        echo "</div>\n";
292
293
        // Reset "margins"
294
        $renderer->setXy($cXT, $cYT);
295
        // This will be mostly used to trick the multiple images last height
296
        if ($this->reseth) {
297
            $cH = 0;
298
        }
299
        // New line and some clean up
300
        if (!$this->newline) {
301
            $renderer->setXy($cX + $this->width, $this->top);
302
            $renderer->lastCellHeight = $cH;
303
        } else {
304
            $renderer->setXy(0, $this->top + $cH + $cP * 2);
305
            $renderer->lastCellHeight = 0;
306
        }
307
        // This will make images in textboxes to ignore images in previous textboxes
308
        // Without this trick the $lastpicbottom in the previos textbox will be used in ReportHtmlImage
309
        $renderer->pageN++;
310
    }
311
}
312