ReportPdfTextBox   F
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 294
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 176
c 0
b 0
f 0
dl 0
loc 294
rs 3.6
wmc 60

1 Method

Rating   Name   Duplication   Size   Complexity  
F render() 0 285 60

How to fix   Complexity   

Complex Class

Complex classes like ReportPdfTextBox 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 ReportPdfTextBox, and based on these observations, apply Extract Interface, too.

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 function count;
23
use function hexdec;
24
use function is_array;
25
use function is_object;
26
use function ksort;
27
use function preg_match;
28
use function str_replace;
29
use function trim;
30
31
class ReportPdfTextBox extends ReportBaseTextbox
32
{
33
    /**
34
     * PDF Text Box renderer
35
     *
36
     * @param PdfRenderer $renderer
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Report\PdfRenderer 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...
37
     *
38
     * @return void
39
     */
40
    public function render($renderer): void
41
    {
42
        $newelements      = [];
43
        $lastelement      = '';
44
        $footnote_element = [];
45
        // Element counter
46
        $cE = count($this->elements);
47
        //-- collapse duplicate elements
48
        for ($i = 0; $i < $cE; $i++) {
49
            $element = $this->elements[$i];
50
            if ($element instanceof ReportBaseElement) {
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...
51
                if ($element instanceof ReportBaseText) {
52
                    ksort($footnote_element);
53
                    foreach ($footnote_element as $links) {
54
                        $newelements[] = $links;
55
                    }
56
                    $footnote_element = [];
57
                    if (empty($lastelement)) {
58
                        $lastelement = $element;
59
                    } elseif ($element->getStyleName() === $lastelement->getStyleName()) {
60
                        // Checking if the Text has the same style
61
                        $lastelement->addText(str_replace("\n", '<br>', $element->getValue()));
62
                    } else {
63
                        $newelements[] = $lastelement;
64
                        $lastelement   = $element;
65
                    }
66
                } elseif ($element instanceof ReportPdfFootnote) {
67
                    // Check if the Footnote has been set with it’s link number
68
                    $renderer->checkFootnote($element);
69
                    // Save first the last element if any
70
                    if (!empty($lastelement)) {
71
                        $newelements[] = $lastelement;
72
                        $lastelement   = [];
73
                    }
74
                    // Save the Footnote with it’s link number as key for sorting later
75
                    $footnote_element[$element->num] = $element;
76
                } elseif (!$element instanceof ReportPdfFootnote || trim($element->getValue()) !== '') {
77
                    // Do not keep empty footnotes
78
                    if (!empty($footnote_element)) {
79
                        ksort($footnote_element);
80
                        foreach ($footnote_element as $links) {
81
                            $newelements[] = $links;
82
                        }
83
                        $footnote_element = [];
84
                    }
85
                    if (!empty($lastelement)) {
86
                        $newelements[] = $lastelement;
87
                        $lastelement   = [];
88
                    }
89
                    $newelements[] = $element;
90
                }
91
            } else {
92
                if (!empty($lastelement)) {
93
                    $newelements[] = $lastelement;
94
                    $lastelement   = [];
95
                }
96
                if (!empty($footnote_element)) {
97
                    ksort($footnote_element);
98
                    foreach ($footnote_element as $links) {
99
                        $newelements[] = $links;
100
                    }
101
                    $footnote_element = [];
102
                }
103
                $newelements[] = $element;
104
            }
105
        }
106
        if (!empty($lastelement)) {
0 ignored issues
show
introduced by
The condition empty($lastelement) is always true.
Loading history...
107
            $newelements[] = $lastelement;
108
        }
109
        if (!empty($footnote_element)) {
110
            ksort($footnote_element);
111
            foreach ($footnote_element as $links) {
112
                $newelements[] = $links;
113
            }
114
        }
115
        $this->elements = $newelements;
116
        unset($footnote_element, $lastelement, $links, $newelements);
117
118
        // Used with line breaks and cell height calculation within this box
119
        $renderer->largestFontHeight = 0;
120
121
        // If current position (left)
122
        if ($this->left === ReportBaseElement::CURRENT_POSITION) {
123
            $cX = $renderer->tcpdf->GetX();
124
        } else {
125
            // For static position add margin (returns and updates X)
126
            $cX = $renderer->addMarginX($this->left);
127
        }
128
129
        // If current position (top)
130
        if ($this->top === ReportBaseElement::CURRENT_POSITION) {
131
            $cY = $renderer->tcpdf->GetY();
132
        } else {
133
            $cY = $this->top;
134
            $renderer->tcpdf->setY($cY);
135
        }
136
137
        // Check the width if set to page wide OR set by xml to larger then page width (margin)
138
        if ($this->width === 0.0 || $this->width > $renderer->getRemainingWidthPDF()) {
139
            $cW = $renderer->getRemainingWidthPDF();
140
        } else {
141
            $cW = $this->width;
142
        }
143
144
        // Save the original margins
145
        $cM = $renderer->tcpdf->getMargins();
146
        // Use cell padding to wrap the width
147
        // Temp Width with cell padding
148
        if (is_array($cM['cell'])) {
149
            $cWT = $cW - ($cM['padding_left'] + $cM['padding_right']);
150
        } else {
151
            $cWT = $cW - $cM['cell'] * 2;
152
        }
153
        // Element height (except text)
154
        $eH = 0.0;
155
        $w  = 0;
156
        // Temp Height
157
        $cHT = 0;
158
        //-- $lw is an array
159
        // 0 => last line width
160
        // 1 => 1 if text was wrapped, 0 if text did not wrap
161
        // 2 => number of LF
162
        $lw = [];
163
        // Element counter
164
        $cE = count($this->elements);
165
        //-- calculate the text box height + width
166
        for ($i = 0; $i < $cE; $i++) {
167
            if (is_object($this->elements[$i])) {
168
                $ew = $this->elements[$i]->setWrapWidth($cWT - $w, $cWT);
169
                if ($ew === $cWT) {
170
                    $w = 0;
171
                }
172
                $lw = $this->elements[$i]->getWidth($renderer);
173
                // Text is already gets the # LF
174
                $cHT += $lw[2];
175
                if ($lw[1] === 1) {
176
                    $w = $lw[0];
177
                } elseif ($lw[1] === 2) {
178
                    $w = 0;
179
                } else {
180
                    $w += $lw[0];
181
                }
182
                if ($w > $cWT) {
183
                    $w = $lw[0];
184
                }
185
                // Footnote is at the bottom of the page. No need to calculate it’s height or wrap the text!
186
                // We are changing the margins anyway!
187
                // For anything else but text (images), get the height
188
                $eH += $this->elements[$i]->getHeight($renderer);
189
            }
190
        }
191
192
        // Add up what’s the final height
193
        $cH = $this->height;
194
        // If any element exist
195
        if ($cE > 0) {
196
            // Check if this is text or some other element, like images
197
            if ($eH === 0.0) {
0 ignored issues
show
introduced by
The condition $eH === 0.0 is always true.
Loading history...
198
                // This is text elements. Number of LF but at least one line
199
                $cHT = ($cHT + 1) * $renderer->tcpdf->getCellHeightRatio();
200
                // Calculate the cell height with the largest font size used within this Box
201
                $cHT *= $renderer->largestFontHeight;
202
                // Add cell padding
203
                if ($this->padding) {
204
                    if (is_array($cM['cell'])) {
205
                        $cHT += $cM['padding_bottom'] + $cM['padding_top'];
206
                    } else {
207
                        $cHT += $cM['cell'] * 2;
208
                    }
209
                }
210
                if ($cH < $cHT) {
211
                    $cH = $cHT;
212
                }
213
            } elseif ($cH < $eH) {
214
                // This is any other element
215
                $cH = $eH;
216
            }
217
        }
218
        // Finally, check the last cells height
219
        if ($cH < $renderer->lastCellHeight) {
220
            $cH = $renderer->lastCellHeight;
221
        }
222
        // Add a new page if needed
223
        if ($this->pagecheck) {
224
            // Reset last cell height or Header/Footer will inherit it, in case of pagebreak
225
            $renderer->lastCellHeight = 0;
226
            if ($renderer->checkPageBreakPDF($cH)) {
227
                $cY = $renderer->tcpdf->GetY();
228
            }
229
        }
230
231
        // Setup the border and background color
232
        $cS = ''; // Class Style
233
        if ($this->border) {
234
            $cS = 'D';
235
        } // D or empty string: Draw (default)
236
        $match = [];
237
        // Fill the background
238
        if ($this->fill) {
239
            if (preg_match('/#?(..)(..)(..)/', $this->bgcolor, $match)) {
240
                $cS .= 'F'; // F: Fill the background
241
                $r  = hexdec($match[1]);
242
                $g  = hexdec($match[2]);
243
                $b  = hexdec($match[3]);
244
                $renderer->tcpdf->setFillColor($r, $g, $b);
245
            }
246
        }
247
        // Clean up a bit
248
        unset($lw, $w, $match, $cE, $eH);
249
        // Draw the border
250
        if (!empty($cS)) {
251
            if (!$renderer->tcpdf->getRTL()) {
252
                $cXM = $cX;
253
            } else {
254
                $cXM = $renderer->tcpdf->getPageWidth() - $cX - $cW;
255
            }
256
            $renderer->tcpdf->Rect($cXM, $cY, $cW, $cH, $cS);
257
        }
258
        // Add cell padding if set and if any text (element) exist
259
        if ($this->padding) {
260
            if ($cHT > 0) {
261
                if (is_array($cM['cell'])) {
262
                    $renderer->tcpdf->setY($cY + $cM['padding_top']);
263
                } else {
264
                    $renderer->tcpdf->setY($cY + $cM['cell']);
265
                }
266
            }
267
        }
268
        // Change the margins X, Width
269
        if (!$renderer->tcpdf->getRTL()) {
270
            if ($this->padding) {
271
                if (is_array($cM['cell'])) {
272
                    $renderer->tcpdf->setLeftMargin($cX + $cM['padding_left']);
273
                } else {
274
                    $renderer->tcpdf->setLeftMargin($cX + $cM['cell']);
275
                }
276
            } else {
277
                $renderer->tcpdf->setLeftMargin($cX);
278
            }
279
            $renderer->tcpdf->setRightMargin($renderer->getRemainingWidthPDF() - $cW + $cM['right']);
280
        } elseif ($this->padding) {
281
            if (is_array($cM['cell'])) {
282
                $renderer->tcpdf->setRightMargin($cX + $cM['padding_right']);
283
            } else {
284
                $renderer->tcpdf->setRightMargin($cX + $cM['cell']);
285
            }
286
            $renderer->tcpdf->setLeftMargin($renderer->getRemainingWidthPDF() - $cW + $cM['left']);
287
        } else {
288
            $renderer->tcpdf->setRightMargin($cX);
289
            $renderer->tcpdf->setLeftMargin($renderer->getRemainingWidthPDF() - $cW + $cM['left']);
290
        }
291
        // Save the current page number
292
        $cPN = $renderer->tcpdf->getPage();
293
294
        // Render the elements (write text, print picture...)
295
        foreach ($this->elements as $element) {
296
            if ($element instanceof ReportBaseElement) {
297
                $element->render($renderer);
298
            } elseif ($element === 'footnotetexts') {
299
                $renderer->footnotes();
300
            } elseif ($element === 'addpage') {
301
                $renderer->newPage();
302
            }
303
        }
304
        // Restore the margins
305
        $renderer->tcpdf->setLeftMargin($cM['left']);
306
        $renderer->tcpdf->setRightMargin($cM['right']);
307
308
        // This will be mostly used to trick the multiple images last height
309
        if ($this->reseth) {
310
            $cH = 0;
311
            // This can only happen with multiple images and with pagebreak
312
            if ($cPN !== $renderer->tcpdf->getPage()) {
313
                $renderer->tcpdf->setPage($cPN);
314
            }
315
        }
316
        // New line and some clean up
317
        if (!$this->newline) {
318
            $renderer->tcpdf->setXY($cX + $cW, $cY);
319
            $renderer->lastCellHeight = $cH;
320
        } else {
321
            // addMarginX() also updates X
322
            $renderer->addMarginX(0);
323
            $renderer->tcpdf->setY($cY + $cH);
324
            $renderer->lastCellHeight = 0;
325
        }
326
    }
327
}
328