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 hight 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 // Finaly, 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