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