xref: /webtrees/app/Report/ReportHtmlTextbox.php (revision 4faa22d32b39010e7ad5fc52d10fd42907741acc)
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
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Report;
21
22use function abs;
23use function count;
24use function is_object;
25use function ksort;
26use function str_replace;
27use function trim;
28
29/**
30 * Class ReportHtmlTextbox
31 */
32class 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        // checkFootnote
44        $newelements      = [];
45        $lastelement      = [];
46        $footnote_element = [];
47        // Element counter
48        $cE = count($this->elements);
49        //-- collapse duplicate elements
50        for ($i = 0; $i < $cE; $i++) {
51            $element = $this->elements[$i];
52            if ($element instanceof ReportBaseElement) {
53                if ($element instanceof ReportBaseText) {
54                    if (!empty($footnote_element)) {
55                        ksort($footnote_element);
56                        foreach ($footnote_element as $links) {
57                            $newelements[] = $links;
58                        }
59                        $footnote_element = [];
60                    }
61                    if (empty($lastelement)) {
62                        $lastelement = $element;
63                    } elseif ($element->getStyleName() === $lastelement->getStyleName()) {
64                        // Checking if the Text has the same style
65                        $lastelement->addText(str_replace("\n", '<br>', $element->getValue()));
66                    } else {
67                        $newelements[] = $lastelement;
68                        $lastelement   = $element;
69                    }
70                } elseif ($element instanceof ReportHtmlFootnote) {
71                    // Check if the Footnote has been set with it’s link number
72                    $renderer->checkFootnote($element);
73                    // Save first the last element if any
74                    if (!empty($lastelement)) {
75                        $newelements[] = $lastelement;
76                        $lastelement   = [];
77                    }
78                    // Save the Footnote with it’s link number as key for sorting later
79                    $footnote_element[$element->num] = $element;
80                } elseif (trim($element->getValue()) !== '') {
81                    // Do not keep empty footnotes
82                    if (!empty($footnote_element)) {
83                        ksort($footnote_element);
84                        foreach ($footnote_element as $links) {
85                            $newelements[] = $links;
86                        }
87                        $footnote_element = [];
88                    }
89                    if (!empty($lastelement)) {
90                        $newelements[] = $lastelement;
91                        $lastelement   = [];
92                    }
93                    $newelements[] = $element;
94                }
95            } else {
96                if (!empty($lastelement)) {
97                    $newelements[] = $lastelement;
98                    $lastelement   = [];
99                }
100                if (!empty($footnote_element)) {
101                    ksort($footnote_element);
102                    foreach ($footnote_element as $links) {
103                        $newelements[] = $links;
104                    }
105                    $footnote_element = [];
106                }
107                $newelements[] = $element;
108            }
109        }
110        if (!empty($lastelement)) {
111            $newelements[] = $lastelement;
112        }
113        if (!empty($footnote_element)) {
114            ksort($footnote_element);
115            foreach ($footnote_element as $links) {
116                $newelements[] = $links;
117            }
118        }
119        $this->elements = $newelements;
120        unset($footnote_element, $lastelement, $newelements);
121
122        $cP = 0; // Class Padding
123
124        // Used with line breaks and cell height calculation within this box only
125        $renderer->largestFontHeight = 0;
126
127        // If current position (left)
128        if ($this->left === ReportBaseElement::CURRENT_POSITION) {
129            $cX = $renderer->getX();
130        } else {
131            $cX = $this->left;
132            $renderer->setX($cX);
133        }
134        // If current position (top)
135        if ($this->top === ReportBaseElement::CURRENT_POSITION) {
136            $this->top = $renderer->getY();
137        } else {
138            $renderer->setY($this->top);
139        }
140
141        // Check the width if set to page wide OR set by xml to larger then page width (margin)
142        if ($this->width === 0.0 || $this->width > $renderer->getRemainingWidth()) {
143            $this->width = $renderer->getRemainingWidth();
144        }
145        // Setup the CellPadding
146        if ($this->padding) {
147            $cP = $renderer->cPadding;
148        }
149
150        // For padding, we have to use less wrap width
151        $cW = $this->width - $cP * 2.0;
152
153        //-- calculate the text box height
154        // Number of lines, will be converted to height
155        $cHT = 0;
156        // Element height (except text)
157        $eH = 0.0;
158        // Footnote height (in points)
159        $fH = 0;
160        $w  = 0;
161        //-- $lw is an array
162        // 0 => last line width
163        // 1 => 1 if text was wrapped, 0 if text did not wrap
164        // 2 => number of LF
165        $lw = [];
166        // Element counter
167        $cE = count($this->elements);
168        for ($i = 0; $i < $cE; $i++) {
169            if (is_object($this->elements[$i])) {
170                $ew = $this->elements[$i]->setWrapWidth($cW - $w - 2, $cW);
171                if ($ew === $cW) {
172                    $w = 0;
173                }
174                $lw = $this->elements[$i]->getWidth($renderer);
175                // Text is already gets the # LF
176                $cHT += $lw[2];
177                if ($lw[1] === 1) {
178                    $w = $lw[0];
179                } elseif ($lw[1] === 2) {
180                    $w = 0;
181                } else {
182                    $w += $lw[0];
183                }
184                if ($w > $cW) {
185                    $w = $lw[0];
186                }
187                // For anything else but text (images), get the height
188                $eH += $this->elements[$i]->getHeight($renderer);
189            } else {
190                $fH += abs($renderer->getFootnotesHeight($cW));
191            }
192        }
193
194        // Add up what’s the final height
195        $cH = $this->height;
196        // If any element exist
197        if ($cE > 0) {
198            // Check if this is text or some other element, like images
199            if ($eH === 0.0) {
200                // Number of LF but at least one line
201                $cHT = ($cHT + 1) * $renderer->cellHeightRatio;
202                // Calculate the cell height with the largest font size used
203                $cHT *= $renderer->largestFontHeight;
204                if ($cH < $cHT) {
205                    $cH = $cHT;
206                }
207            } else {
208                // This is any other element
209                if ($cH < $eH) {
210                    $cH = $eH;
211                }
212                // Add Footnote height to the rest of the height
213                $cH += $fH;
214            }
215        }
216
217        unset($lw, $cHT, $fH, $w);
218
219        // Finally, check the last cells height
220        if ($cH < $renderer->lastCellHeight) {
221            $cH = $renderer->lastCellHeight;
222        }
223        // Update max Y in case of a pagebreak
224        // We don't want to over write any images or other stuff
225        $renderer->addMaxY($this->top + $cH);
226
227        // Start to print HTML
228        echo '<div style="position:absolute;top:', $this->top, 'pt;';
229        // LTR (left) or RTL (right)
230        echo $renderer->alignRTL, ':', $cX, 'pt;';
231        // Background color
232        if ($this->fill && $this->bgcolor !== '') {
233            echo ' background-color:', $this->bgcolor, ';';
234        }
235        // Print padding only when it’s set
236        if ($this->padding) {
237            // Use Cell around padding to support RTL also
238            echo 'padding:', $cP, 'pt;';
239        }
240        // Border setup
241        if ($this->border) {
242            echo ' border:solid black 1pt;';
243            echo 'width:', $this->width - 1 - $cP * 2, 'pt;height:', $cH - 1, 'pt;';
244        } else {
245            echo 'width:', $this->width - $cP * 2, 'pt;height:', $cH, 'pt;';
246        }
247        echo '">';
248
249        // Do a little "margin" trick before print
250        // to get the correct current position => "."
251        $cXT = $renderer->getX();
252        $cYT = $renderer->getY();
253        $renderer->setXy(0, 0);
254
255        // Print the text elements
256        foreach ($this->elements as $element) {
257            if ($element instanceof ReportHtmlText) {
258                $element->render($renderer, false);
259            } elseif ($element instanceof ReportBaseElement) {
260                $element->render($renderer);
261            } elseif ($element === 'footnotetexts') {
262                $renderer->footnotes();
263            } elseif ($element === 'addpage') {
264                $renderer->addPage();
265            }
266        }
267        echo "</div>\n";
268
269        // Reset "margins"
270        $renderer->setXy($cXT, $cYT);
271        // This will be mostly used to trick the multiple images last height
272        if ($this->reseth) {
273            $cH = 0;
274        }
275        // New line and some clean up
276        if (!$this->newline) {
277            $renderer->setXy($cX + $this->width, $this->top);
278            $renderer->lastCellHeight = $cH;
279        } else {
280            $renderer->setXy(0, $this->top + $cH + $cP * 2);
281            $renderer->lastCellHeight = 0;
282        }
283    }
284}
285