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