1b6f35a76SGreg Roach<?php 2b6f35a76SGreg Roach 3b6f35a76SGreg Roach/** 4b6f35a76SGreg Roach * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 6b6f35a76SGreg Roach * This program is free software: you can redistribute it and/or modify 7b6f35a76SGreg Roach * it under the terms of the GNU General Public License as published by 8b6f35a76SGreg Roach * the Free Software Foundation, either version 3 of the License, or 9b6f35a76SGreg Roach * (at your option) any later version. 10b6f35a76SGreg Roach * This program is distributed in the hope that it will be useful, 11b6f35a76SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12b6f35a76SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13b6f35a76SGreg Roach * GNU General Public License for more details. 14b6f35a76SGreg Roach * You should have received a copy of the GNU General Public License 1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>. 16b6f35a76SGreg Roach */ 17b6f35a76SGreg Roach 18b6f35a76SGreg Roachdeclare(strict_types=1); 19b6f35a76SGreg Roach 20b6f35a76SGreg Roachnamespace Fisharebest\Webtrees\Report; 21b6f35a76SGreg Roach 22b6f35a76SGreg Roachuse Fisharebest\Webtrees\I18N; 23b6f35a76SGreg Roachuse Fisharebest\Webtrees\MediaFile; 24b6f35a76SGreg Roachuse Fisharebest\Webtrees\Webtrees; 25b6f35a76SGreg Roach 265deaecabSGreg Roachuse function array_map; 27b6f35a76SGreg Roachuse function ceil; 28b6f35a76SGreg Roachuse function count; 29b6f35a76SGreg Roachuse function explode; 305deaecabSGreg Roachuse function implode; 31b6f35a76SGreg Roachuse function preg_match; 32b6f35a76SGreg Roachuse function str_replace; 33b6f35a76SGreg Roachuse function stripos; 345deaecabSGreg Roachuse function strrpos; 355deaecabSGreg Roachuse function substr; 36b6f35a76SGreg Roachuse function substr_count; 37b6f35a76SGreg Roach 38b6f35a76SGreg Roach/** 39b6f35a76SGreg Roach * Class HtmlRenderer 40b6f35a76SGreg Roach */ 41b6f35a76SGreg Roachclass HtmlRenderer extends AbstractRenderer 42b6f35a76SGreg Roach{ 430a709a28SGreg Roach // Cell padding 440a709a28SGreg Roach public float $cPadding = 2; 450a709a28SGreg Roach 460a709a28SGreg Roach // Cell height ratio 470a709a28SGreg Roach public float $cellHeightRatio = 1.8; 480a709a28SGreg Roach 490a709a28SGreg Roach // Current horizontal position 500a709a28SGreg Roach public float $X = 0.0; 510a709a28SGreg Roach 520a709a28SGreg Roach // Current vertical position 530a709a28SGreg Roach public float $Y = 0.0; 540a709a28SGreg Roach 550a709a28SGreg Roach // Page number counter 560a709a28SGreg Roach public int $pageN = 1; 570a709a28SGreg Roach 580a709a28SGreg Roach // Store the page width without left and right margins 590a709a28SGreg Roach // Only needed for PDF reports 600a709a28SGreg Roach public float $noMarginWidth = 0.0; 610a709a28SGreg Roach 620a709a28SGreg Roach // Last cell height 630a709a28SGreg Roach public float $lastCellHeight = 0.0; 640a709a28SGreg Roach 650a709a28SGreg Roach // LTR or RTL alignement; "left" on LTR, "right" on RTL 660a709a28SGreg Roach // Used in <div> 670a709a28SGreg Roach public string $alignRTL = 'left'; 680a709a28SGreg Roach 690a709a28SGreg Roach // LTR or RTL entity 700a709a28SGreg Roach public string $entityRTL = '‎'; 710a709a28SGreg Roach 720a709a28SGreg Roach // Largest Font Height is used by TextBox etc. 730a709a28SGreg Roach // 740a709a28SGreg Roach // Use this to calculate a the text height. 750a709a28SGreg Roach // This makes sure that the text fits into the cell/box when different font sizes are used 760a709a28SGreg Roach public float $largestFontHeight = 0; 770a709a28SGreg Roach 780a709a28SGreg Roach // Keep track of the highest Y position 790a709a28SGreg Roach // Used with Header div / Body div / Footer div / "addpage" / The bottom of the last image etc. 800a709a28SGreg Roach public float $maxY = 0; 81b6f35a76SGreg Roach 82b6f35a76SGreg Roach /** 830a709a28SGreg Roach * @var ReportHtmlFootnote[] Array of elements in the footer notes 84b6f35a76SGreg Roach */ 850a709a28SGreg Roach public array $printedfootnotes = []; 86b6f35a76SGreg Roach 87b6f35a76SGreg Roach /** 88b6f35a76SGreg Roach * HTML Setup - ReportHtml 89b6f35a76SGreg Roach * 90b6f35a76SGreg Roach * @return void 91b6f35a76SGreg Roach */ 92b6f35a76SGreg Roach public function setup(): void 93b6f35a76SGreg Roach { 94b6f35a76SGreg Roach parent::setup(); 95b6f35a76SGreg Roach 96b6f35a76SGreg Roach // Setting up the correct dimensions if Portrait (default) or Landscape 97b6f35a76SGreg Roach if ($this->orientation === 'landscape') { 98b6f35a76SGreg Roach $tmpw = $this->page_width; 99b6f35a76SGreg Roach $this->page_width = $this->page_height; 100b6f35a76SGreg Roach $this->page_height = $tmpw; 101b6f35a76SGreg Roach } 102b6f35a76SGreg Roach // Store the pagewidth without margins 103b6f35a76SGreg Roach $this->noMarginWidth = $this->page_width - $this->left_margin - $this->right_margin; 104b6f35a76SGreg Roach // If RTL 105b6f35a76SGreg Roach if ($this->rtl) { 106b6f35a76SGreg Roach $this->alignRTL = 'right'; 107b6f35a76SGreg Roach $this->entityRTL = '‏'; 108b6f35a76SGreg Roach } 109b6f35a76SGreg Roach // Change the default HTML font name 110b6f35a76SGreg Roach $this->default_font = 'Arial'; 111b6f35a76SGreg Roach 112b6f35a76SGreg Roach if ($this->show_generated_by) { 113b6f35a76SGreg Roach // The default style name for Generated by.... is 'genby' 114*f315390bSGreg Roach $element = new ReportHtmlCell(0.0, 10.0, '', 'C', '', 'genby', 1, ReportBaseElement::CURRENT_POSITION, ReportBaseElement::CURRENT_POSITION, false, 0, '', '', true); 115b6f35a76SGreg Roach $element->addText($this->generated_by); 116b6f35a76SGreg Roach $element->setUrl(Webtrees::URL); 117b6f35a76SGreg Roach $this->footerElements[] = $element; 118b6f35a76SGreg Roach } 119b6f35a76SGreg Roach } 120b6f35a76SGreg Roach 121b6f35a76SGreg Roach /** 122b6f35a76SGreg Roach * Generate footnotes 123b6f35a76SGreg Roach * 124b6f35a76SGreg Roach * @return void 125b6f35a76SGreg Roach */ 126b6f35a76SGreg Roach public function footnotes(): void 127b6f35a76SGreg Roach { 128b6f35a76SGreg Roach $this->currentStyle = ''; 129b6f35a76SGreg Roach if (!empty($this->printedfootnotes)) { 130b6f35a76SGreg Roach foreach ($this->printedfootnotes as $element) { 131b6f35a76SGreg Roach $element->renderFootnote($this); 132b6f35a76SGreg Roach } 133b6f35a76SGreg Roach } 134b6f35a76SGreg Roach } 135b6f35a76SGreg Roach 136b6f35a76SGreg Roach /** 137b6f35a76SGreg Roach * Run the report. 138b6f35a76SGreg Roach * 139b6f35a76SGreg Roach * @return void 140b6f35a76SGreg Roach */ 141b6f35a76SGreg Roach public function run(): void 142b6f35a76SGreg Roach { 143b6f35a76SGreg Roach // Setting up the styles 144b1ee2291SGreg Roach echo '<style>'; 145b6f35a76SGreg Roach echo '#bodydiv { font: 10px sans-serif;}'; 146b6f35a76SGreg Roach foreach ($this->styles as $class => $style) { 147b6f35a76SGreg Roach echo '.', $class, ' { '; 148b6f35a76SGreg Roach if ($style['font'] === 'dejavusans') { 149b6f35a76SGreg Roach $style['font'] = $this->default_font; 150b6f35a76SGreg Roach } 151b6f35a76SGreg Roach echo 'font-family: ', $style['font'], '; '; 152b6f35a76SGreg Roach echo 'font-size: ', $style['size'], 'pt; '; 153b6f35a76SGreg Roach // Case-insensitive 154b6f35a76SGreg Roach if (stripos($style['style'], 'B') !== false) { 155b6f35a76SGreg Roach echo 'font-weight: bold; '; 156b6f35a76SGreg Roach } 157b6f35a76SGreg Roach if (stripos($style['style'], 'I') !== false) { 158b6f35a76SGreg Roach echo 'font-style: italic; '; 159b6f35a76SGreg Roach } 160b6f35a76SGreg Roach if (stripos($style['style'], 'U') !== false) { 161b6f35a76SGreg Roach echo 'text-decoration: underline; '; 162b6f35a76SGreg Roach } 163b6f35a76SGreg Roach if (stripos($style['style'], 'D') !== false) { 164b6f35a76SGreg Roach echo 'text-decoration: line-through; '; 165b6f35a76SGreg Roach } 166b6f35a76SGreg Roach echo '}', PHP_EOL; 167b6f35a76SGreg Roach } 168b6f35a76SGreg Roach 169b6f35a76SGreg Roach //-- header divider 170b6f35a76SGreg Roach echo '</style>', PHP_EOL; 171b6f35a76SGreg Roach echo '<div id="headermargin" style="position: relative; top: auto; height: ', $this->header_margin, 'pt; width: ', $this->noMarginWidth, 'pt;"></div>'; 172b6f35a76SGreg Roach echo '<div id="headerdiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt;">'; 173b6f35a76SGreg Roach foreach ($this->headerElements as $element) { 174b6f35a76SGreg Roach if ($element instanceof ReportBaseElement) { 175b6f35a76SGreg Roach $element->render($this); 176b6f35a76SGreg Roach } elseif ($element === 'footnotetexts') { 177b6f35a76SGreg Roach $this->footnotes(); 178b6f35a76SGreg Roach } elseif ($element === 'addpage') { 179b6f35a76SGreg Roach $this->addPage(); 180b6f35a76SGreg Roach } 181b6f35a76SGreg Roach } 182b6f35a76SGreg Roach //-- body 183b6f35a76SGreg Roach echo '</div>'; 184b6f35a76SGreg Roach echo '<script>document.getElementById("headerdiv").style.height="', $this->top_margin - $this->header_margin - 6, 'pt";</script>'; 185b6f35a76SGreg Roach echo '<div id="bodydiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt; height: 100%;">'; 186b6f35a76SGreg Roach $this->Y = 0; 187b6f35a76SGreg Roach $this->maxY = 0; 188b6f35a76SGreg Roach foreach ($this->bodyElements as $element) { 189b6f35a76SGreg Roach if ($element instanceof ReportBaseElement) { 190b6f35a76SGreg Roach $element->render($this); 191b6f35a76SGreg Roach } elseif ($element === 'footnotetexts') { 192b6f35a76SGreg Roach $this->footnotes(); 193b6f35a76SGreg Roach } elseif ($element === 'addpage') { 194b6f35a76SGreg Roach $this->addPage(); 195b6f35a76SGreg Roach } 196b6f35a76SGreg Roach } 197b6f35a76SGreg Roach //-- footer 198b6f35a76SGreg Roach echo '</div>'; 199b6f35a76SGreg Roach echo '<script>document.getElementById("bodydiv").style.height="', $this->maxY, 'pt";</script>'; 200b6f35a76SGreg Roach echo '<div id="bottommargin" style="position: relative; top: auto; height: ', $this->bottom_margin - $this->footer_margin, 'pt;width:', $this->noMarginWidth, 'pt;"></div>'; 201b6f35a76SGreg Roach echo '<div id="footerdiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt;height:auto;">'; 202b6f35a76SGreg Roach $this->Y = 0; 203b6f35a76SGreg Roach $this->X = 0; 204b6f35a76SGreg Roach $this->maxY = 0; 205b6f35a76SGreg Roach foreach ($this->footerElements as $element) { 206b6f35a76SGreg Roach if ($element instanceof ReportBaseElement) { 207b6f35a76SGreg Roach $element->render($this); 208b6f35a76SGreg Roach } elseif ($element === 'footnotetexts') { 209b6f35a76SGreg Roach $this->footnotes(); 210b6f35a76SGreg Roach } elseif ($element === 'addpage') { 211b6f35a76SGreg Roach $this->addPage(); 212b6f35a76SGreg Roach } 213b6f35a76SGreg Roach } 214b6f35a76SGreg Roach echo '</div>'; 215b6f35a76SGreg Roach echo '<script>document.getElementById("footerdiv").style.height="', $this->maxY, 'pt";</script>'; 216b6f35a76SGreg Roach echo '<div id="footermargin" style="position: relative; top: auto; height: ', $this->footer_margin, 'pt;width:', $this->noMarginWidth, 'pt;"></div>'; 217b6f35a76SGreg Roach } 218b6f35a76SGreg Roach 219b6f35a76SGreg Roach /** 220b6f35a76SGreg Roach * Create a new Cell object. 221b6f35a76SGreg Roach * 22277bab461SGreg Roach * @param float $width cell width (expressed in points) 22377bab461SGreg Roach * @param float $height cell height (expressed in points) 22477bab461SGreg Roach * @param string $border Border style 225b6f35a76SGreg Roach * @param string $align Text alignement 226b6f35a76SGreg Roach * @param string $bgcolor Background color code 227b6f35a76SGreg Roach * @param string $style The name of the text style 228b6f35a76SGreg Roach * @param int $ln Indicates where the current position should go after the call 229*f315390bSGreg Roach * @param float $top Y-position 230*f315390bSGreg Roach * @param float $left X-position 231*f315390bSGreg Roach * @param bool $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 1 23224f2a3afSGreg Roach * @param int $stretch Stretch character mode 233b6f35a76SGreg Roach * @param string $bocolor Border color 234b6f35a76SGreg Roach * @param string $tcolor Text color 235b6f35a76SGreg Roach * @param bool $reseth 236b6f35a76SGreg Roach * 237b6f35a76SGreg Roach * @return ReportBaseCell 238b6f35a76SGreg Roach */ 239*f315390bSGreg Roach public function createCell(float $width, float $height, string $border, string $align, string $bgcolor, string $style, int $ln, float $top, float $left, bool $fill, int $stretch, string $bocolor, string $tcolor, bool $reseth): ReportBaseCell 240b6f35a76SGreg Roach { 241b6f35a76SGreg Roach return new ReportHtmlCell($width, $height, $border, $align, $bgcolor, $style, $ln, $top, $left, $fill, $stretch, $bocolor, $tcolor, $reseth); 242b6f35a76SGreg Roach } 243b6f35a76SGreg Roach 244b6f35a76SGreg Roach /** 245b6f35a76SGreg Roach * Create a new TextBox object. 246b6f35a76SGreg Roach * 247b6f35a76SGreg Roach * @param float $width Text box width 248b6f35a76SGreg Roach * @param float $height Text box height 249b6f35a76SGreg Roach * @param bool $border 250b6f35a76SGreg Roach * @param string $bgcolor Background color code in HTML 251b6f35a76SGreg Roach * @param bool $newline 252b6f35a76SGreg Roach * @param float $left 253b6f35a76SGreg Roach * @param float $top 254b6f35a76SGreg Roach * @param bool $pagecheck 255b6f35a76SGreg Roach * @param string $style 256b6f35a76SGreg Roach * @param bool $fill 257b6f35a76SGreg Roach * @param bool $padding 258b6f35a76SGreg Roach * @param bool $reseth 259b6f35a76SGreg Roach * 260b6f35a76SGreg Roach * @return ReportBaseTextbox 261b6f35a76SGreg Roach */ 262b6f35a76SGreg Roach public function createTextBox( 263b6f35a76SGreg Roach float $width, 264b6f35a76SGreg Roach float $height, 265b6f35a76SGreg Roach bool $border, 266b6f35a76SGreg Roach string $bgcolor, 267b6f35a76SGreg Roach bool $newline, 268b6f35a76SGreg Roach float $left, 269b6f35a76SGreg Roach float $top, 270b6f35a76SGreg Roach bool $pagecheck, 271b6f35a76SGreg Roach string $style, 272b6f35a76SGreg Roach bool $fill, 273b6f35a76SGreg Roach bool $padding, 274b6f35a76SGreg Roach bool $reseth 275b6f35a76SGreg Roach ): ReportBaseTextbox { 276b6f35a76SGreg Roach return new ReportHtmlTextbox($width, $height, $border, $bgcolor, $newline, $left, $top, $pagecheck, $style, $fill, $padding, $reseth); 277b6f35a76SGreg Roach } 278b6f35a76SGreg Roach 279b6f35a76SGreg Roach /** 280b6f35a76SGreg Roach * Create a text element. 281b6f35a76SGreg Roach * 282b6f35a76SGreg Roach * @param string $style 283b6f35a76SGreg Roach * @param string $color 284b6f35a76SGreg Roach * 285b6f35a76SGreg Roach * @return ReportBaseText 286b6f35a76SGreg Roach */ 287b6f35a76SGreg Roach public function createText(string $style, string $color): ReportBaseText 288b6f35a76SGreg Roach { 289b6f35a76SGreg Roach return new ReportHtmlText($style, $color); 290b6f35a76SGreg Roach } 291b6f35a76SGreg Roach 292b6f35a76SGreg Roach /** 293b6f35a76SGreg Roach * Create a new Footnote object. 294b6f35a76SGreg Roach * 295b6f35a76SGreg Roach * @param string $style Style name 296b6f35a76SGreg Roach * 297b6f35a76SGreg Roach * @return ReportBaseFootnote 298b6f35a76SGreg Roach */ 29924f2a3afSGreg Roach public function createFootnote(string $style): ReportBaseFootnote 300b6f35a76SGreg Roach { 301b6f35a76SGreg Roach return new ReportHtmlFootnote($style); 302b6f35a76SGreg Roach } 303b6f35a76SGreg Roach 304b6f35a76SGreg Roach /** 305b6f35a76SGreg Roach * Create a new image object. 306b6f35a76SGreg Roach * 307b6f35a76SGreg Roach * @param string $file Filename 308b6f35a76SGreg Roach * @param float $x 309b6f35a76SGreg Roach * @param float $y 310b6f35a76SGreg Roach * @param float $w Image width 311b6f35a76SGreg Roach * @param float $h Image height 312b6f35a76SGreg Roach * @param string $align L:left, C:center, R:right or empty to use x/y 313b6f35a76SGreg Roach * @param string $ln T:same line, N:next line 314b6f35a76SGreg Roach * 315b6f35a76SGreg Roach * @return ReportBaseImage 316b6f35a76SGreg Roach */ 317b6f35a76SGreg Roach public function createImage(string $file, float $x, float $y, float $w, float $h, string $align, string $ln): ReportBaseImage 318b6f35a76SGreg Roach { 319b6f35a76SGreg Roach return new ReportHtmlImage($file, $x, $y, $w, $h, $align, $ln); 320b6f35a76SGreg Roach } 321b6f35a76SGreg Roach 322b6f35a76SGreg Roach /** 323b6f35a76SGreg Roach * Create a new image object from Media Object. 324b6f35a76SGreg Roach * 325b6f35a76SGreg Roach * @param MediaFile $media_file 326b6f35a76SGreg Roach * @param float $x 327b6f35a76SGreg Roach * @param float $y 328b6f35a76SGreg Roach * @param float $w Image width 329b6f35a76SGreg Roach * @param float $h Image height 330b6f35a76SGreg Roach * @param string $align L:left, C:center, R:right or empty to use x/y 331b6f35a76SGreg Roach * @param string $ln T:same line, N:next line 332b6f35a76SGreg Roach * 333b6f35a76SGreg Roach * @return ReportBaseImage 334b6f35a76SGreg Roach */ 335b6f35a76SGreg Roach public function createImageFromObject( 336b6f35a76SGreg Roach MediaFile $media_file, 337b6f35a76SGreg Roach float $x, 338b6f35a76SGreg Roach float $y, 339b6f35a76SGreg Roach float $w, 340b6f35a76SGreg Roach float $h, 341b6f35a76SGreg Roach string $align, 3429458f20aSGreg Roach string $ln 343b6f35a76SGreg Roach ): ReportBaseImage { 3441558ebe9SGreg Roach return new ReportHtmlImage($media_file->imageUrl((int) $w, (int) $h, 'crop'), $x, $y, $w, $h, $align, $ln); 345b6f35a76SGreg Roach } 346b6f35a76SGreg Roach 347b6f35a76SGreg Roach /** 348b6f35a76SGreg Roach * Create a line. 349b6f35a76SGreg Roach * 350b6f35a76SGreg Roach * @param float $x1 351b6f35a76SGreg Roach * @param float $y1 352b6f35a76SGreg Roach * @param float $x2 353b6f35a76SGreg Roach * @param float $y2 354b6f35a76SGreg Roach * 355b6f35a76SGreg Roach * @return ReportBaseLine 356b6f35a76SGreg Roach */ 357b6f35a76SGreg Roach public function createLine(float $x1, float $y1, float $x2, float $y2): ReportBaseLine 358b6f35a76SGreg Roach { 359b6f35a76SGreg Roach return new ReportHtmlLine($x1, $y1, $x2, $y2); 360b6f35a76SGreg Roach } 361b6f35a76SGreg Roach 362b6f35a76SGreg Roach /** 363b6f35a76SGreg Roach * Clear the Header 364b6f35a76SGreg Roach * 365b6f35a76SGreg Roach * @return void 366b6f35a76SGreg Roach */ 367b6f35a76SGreg Roach public function clearHeader(): void 368b6f35a76SGreg Roach { 369b6f35a76SGreg Roach $this->headerElements = []; 370b6f35a76SGreg Roach } 371b6f35a76SGreg Roach 372b6f35a76SGreg Roach /** 373b6f35a76SGreg Roach * Update the Page Number and set a new Y if max Y is larger - ReportHtml 374b6f35a76SGreg Roach * 375b6f35a76SGreg Roach * @return void 376b6f35a76SGreg Roach */ 377b6f35a76SGreg Roach public function addPage(): void 378b6f35a76SGreg Roach { 379b6f35a76SGreg Roach $this->pageN++; 380b6f35a76SGreg Roach 381b6f35a76SGreg Roach // Add a little margin to max Y "between pages" 382b6f35a76SGreg Roach $this->maxY += 10; 383b6f35a76SGreg Roach 384b6f35a76SGreg Roach // If Y is still heigher by any reason... 385b6f35a76SGreg Roach if ($this->maxY < $this->Y) { 386b6f35a76SGreg Roach // ... update max Y 387b6f35a76SGreg Roach $this->maxY = $this->Y; 388b6f35a76SGreg Roach } else { 389b6f35a76SGreg Roach // else update Y so that nothing will be overwritten, like images or cells... 390b6f35a76SGreg Roach $this->Y = $this->maxY; 391b6f35a76SGreg Roach } 392b6f35a76SGreg Roach } 393b6f35a76SGreg Roach 394b6f35a76SGreg Roach /** 395b6f35a76SGreg Roach * Uppdate max Y to keep track it in case of a pagebreak - ReportHtml 396b6f35a76SGreg Roach * 397b6f35a76SGreg Roach * @param float $y 398b6f35a76SGreg Roach * 399b6f35a76SGreg Roach * @return void 400b6f35a76SGreg Roach */ 40177bab461SGreg Roach public function addMaxY(float $y): void 402b6f35a76SGreg Roach { 403b6f35a76SGreg Roach if ($this->maxY < $y) { 404b6f35a76SGreg Roach $this->maxY = $y; 405b6f35a76SGreg Roach } 406b6f35a76SGreg Roach } 407b6f35a76SGreg Roach 408b6f35a76SGreg Roach /** 409b6f35a76SGreg Roach * Checks the Footnote and numbers them - ReportHtml 410b6f35a76SGreg Roach * 411b6f35a76SGreg Roach * @param ReportHtmlFootnote $footnote 412b6f35a76SGreg Roach * 413b6f35a76SGreg Roach * @return ReportHtmlFootnote|bool object if already numbered, false otherwise 414b6f35a76SGreg Roach */ 415b6f35a76SGreg Roach public function checkFootnote(ReportHtmlFootnote $footnote) 416b6f35a76SGreg Roach { 417b6f35a76SGreg Roach $ct = count($this->printedfootnotes); 418b6f35a76SGreg Roach $i = 0; 419b6f35a76SGreg Roach $val = $footnote->getValue(); 420b6f35a76SGreg Roach while ($i < $ct) { 421b6f35a76SGreg Roach if ($this->printedfootnotes[$i]->getValue() === $val) { 422b6f35a76SGreg Roach // If this footnote already exist then set up the numbers for this object 423b6f35a76SGreg Roach $footnote->setNum($i + 1); 424b6f35a76SGreg Roach $footnote->setAddlink((string) ($i + 1)); 425b6f35a76SGreg Roach 426b6f35a76SGreg Roach return $this->printedfootnotes[$i]; 427b6f35a76SGreg Roach } 428b6f35a76SGreg Roach $i++; 429b6f35a76SGreg Roach } 430b6f35a76SGreg Roach // If this Footnote has not been set up yet 431b6f35a76SGreg Roach $footnote->setNum($ct + 1); 432b6f35a76SGreg Roach $footnote->setAddlink((string) ($ct + 1)); 433b6f35a76SGreg Roach $this->printedfootnotes[] = $footnote; 434b6f35a76SGreg Roach 435b6f35a76SGreg Roach return false; 436b6f35a76SGreg Roach } 437b6f35a76SGreg Roach 438b6f35a76SGreg Roach /** 439b6f35a76SGreg Roach * Count the number of lines - ReportHtml 440b6f35a76SGreg Roach * 441b6f35a76SGreg Roach * @param string $str 442b6f35a76SGreg Roach * 443b6f35a76SGreg Roach * @return int Number of lines. 0 if empty line 444b6f35a76SGreg Roach */ 44577bab461SGreg Roach public function countLines(string $str): int 446b6f35a76SGreg Roach { 447b6f35a76SGreg Roach if ($str === '') { 448b6f35a76SGreg Roach return 0; 449b6f35a76SGreg Roach } 450b6f35a76SGreg Roach 451b6f35a76SGreg Roach return substr_count($str, "\n") + 1; 452b6f35a76SGreg Roach } 453b6f35a76SGreg Roach 454b6f35a76SGreg Roach /** 455b6f35a76SGreg Roach * Get the current style. 456b6f35a76SGreg Roach * 457b6f35a76SGreg Roach * @return string 458b6f35a76SGreg Roach */ 459b6f35a76SGreg Roach public function getCurrentStyle(): string 460b6f35a76SGreg Roach { 461b6f35a76SGreg Roach return $this->currentStyle; 462b6f35a76SGreg Roach } 463b6f35a76SGreg Roach 464b6f35a76SGreg Roach /** 465b6f35a76SGreg Roach * Get the current style height. 466b6f35a76SGreg Roach * 467b6f35a76SGreg Roach * @return float 468b6f35a76SGreg Roach */ 469b6f35a76SGreg Roach public function getCurrentStyleHeight(): float 470b6f35a76SGreg Roach { 471b6f35a76SGreg Roach if (empty($this->currentStyle)) { 472b6f35a76SGreg Roach return $this->default_font_size; 473b6f35a76SGreg Roach } 474b6f35a76SGreg Roach $style = $this->getStyle($this->currentStyle); 475b6f35a76SGreg Roach 476*f315390bSGreg Roach return $style['size']; 477b6f35a76SGreg Roach } 478b6f35a76SGreg Roach 479b6f35a76SGreg Roach /** 480b6f35a76SGreg Roach * Get the current footnotes height. 481b6f35a76SGreg Roach * 482b6f35a76SGreg Roach * @param float $cellWidth 483b6f35a76SGreg Roach * 484b6f35a76SGreg Roach * @return float 485b6f35a76SGreg Roach */ 486b6f35a76SGreg Roach public function getFootnotesHeight(float $cellWidth): float 487b6f35a76SGreg Roach { 488b6f35a76SGreg Roach $h = 0; 489b6f35a76SGreg Roach foreach ($this->printedfootnotes as $element) { 490b6f35a76SGreg Roach $h += $element->getFootnoteHeight($this, $cellWidth); 491b6f35a76SGreg Roach } 492b6f35a76SGreg Roach 493b6f35a76SGreg Roach return $h; 494b6f35a76SGreg Roach } 495b6f35a76SGreg Roach 496b6f35a76SGreg Roach /** 497b6f35a76SGreg Roach * Get the maximum width from current position to the margin - ReportHtml 498b6f35a76SGreg Roach * 499b6f35a76SGreg Roach * @return float 500b6f35a76SGreg Roach */ 501b6f35a76SGreg Roach public function getRemainingWidth(): float 502b6f35a76SGreg Roach { 503b6f35a76SGreg Roach return $this->noMarginWidth - $this->X; 504b6f35a76SGreg Roach } 505b6f35a76SGreg Roach 506b6f35a76SGreg Roach /** 507b6f35a76SGreg Roach * Get the page height. 508b6f35a76SGreg Roach * 509b6f35a76SGreg Roach * @return float 510b6f35a76SGreg Roach */ 511b6f35a76SGreg Roach public function getPageHeight(): float 512b6f35a76SGreg Roach { 513b6f35a76SGreg Roach return $this->page_height - $this->top_margin; 514b6f35a76SGreg Roach } 515b6f35a76SGreg Roach 516b6f35a76SGreg Roach /** 517b6f35a76SGreg Roach * Get the width of a string. 518b6f35a76SGreg Roach * 519b6f35a76SGreg Roach * @param string $text 520b6f35a76SGreg Roach * 521b6f35a76SGreg Roach * @return float 522b6f35a76SGreg Roach */ 523b6f35a76SGreg Roach public function getStringWidth(string $text): float 524b6f35a76SGreg Roach { 525b6f35a76SGreg Roach $style = $this->getStyle($this->currentStyle); 526b6f35a76SGreg Roach 527*f315390bSGreg Roach return mb_strlen($text) * $style['size'] / 2; 528b6f35a76SGreg Roach } 529b6f35a76SGreg Roach 530b6f35a76SGreg Roach /** 531b6f35a76SGreg Roach * Get a text height in points - ReportHtml 532b6f35a76SGreg Roach * 533b6f35a76SGreg Roach * @param string $str 534b6f35a76SGreg Roach * 535b6f35a76SGreg Roach * @return float 536b6f35a76SGreg Roach */ 537b6f35a76SGreg Roach public function getTextCellHeight(string $str): float 538b6f35a76SGreg Roach { 539b6f35a76SGreg Roach // Count the number of lines to calculate the height 540b6f35a76SGreg Roach $nl = $this->countLines($str); 541b6f35a76SGreg Roach 542b6f35a76SGreg Roach // Calculate the cell height 54352135727SGreg Roach return ceil($this->getCurrentStyleHeight() * $this->cellHeightRatio * $nl); 544b6f35a76SGreg Roach } 545b6f35a76SGreg Roach 546b6f35a76SGreg Roach /** 547b6f35a76SGreg Roach * Get the current X position - ReportHtml 548b6f35a76SGreg Roach * 549b6f35a76SGreg Roach * @return float 550b6f35a76SGreg Roach */ 551b6f35a76SGreg Roach public function getX(): float 552b6f35a76SGreg Roach { 553b6f35a76SGreg Roach return $this->X; 554b6f35a76SGreg Roach } 555b6f35a76SGreg Roach 556b6f35a76SGreg Roach /** 557b6f35a76SGreg Roach * Get the current Y position - ReportHtml 558b6f35a76SGreg Roach * 559b6f35a76SGreg Roach * @return float 560b6f35a76SGreg Roach */ 561b6f35a76SGreg Roach public function getY(): float 562b6f35a76SGreg Roach { 563b6f35a76SGreg Roach return $this->Y; 564b6f35a76SGreg Roach } 565b6f35a76SGreg Roach 566b6f35a76SGreg Roach /** 567b6f35a76SGreg Roach * Get the current page number - ReportHtml 568b6f35a76SGreg Roach * 569b6f35a76SGreg Roach * @return int 570b6f35a76SGreg Roach */ 571b6f35a76SGreg Roach public function pageNo(): int 572b6f35a76SGreg Roach { 573b6f35a76SGreg Roach return $this->pageN; 574b6f35a76SGreg Roach } 575b6f35a76SGreg Roach 576b6f35a76SGreg Roach /** 577b6f35a76SGreg Roach * Set the current style. 578b6f35a76SGreg Roach * 579b6f35a76SGreg Roach * @param string $s 580b6f35a76SGreg Roach * 581b6f35a76SGreg Roach * @void 582b6f35a76SGreg Roach */ 583b6f35a76SGreg Roach public function setCurrentStyle(string $s): void 584b6f35a76SGreg Roach { 585b6f35a76SGreg Roach $this->currentStyle = $s; 586b6f35a76SGreg Roach } 587b6f35a76SGreg Roach 588b6f35a76SGreg Roach /** 589b6f35a76SGreg Roach * Set the X position - ReportHtml 590b6f35a76SGreg Roach * 591b6f35a76SGreg Roach * @param float $x 592b6f35a76SGreg Roach * 593b6f35a76SGreg Roach * @return void 594b6f35a76SGreg Roach */ 59524f2a3afSGreg Roach public function setX(float $x): void 596b6f35a76SGreg Roach { 597b6f35a76SGreg Roach $this->X = $x; 598b6f35a76SGreg Roach } 599b6f35a76SGreg Roach 600b6f35a76SGreg Roach /** 601b6f35a76SGreg Roach * Set the Y position - ReportHtml 602b6f35a76SGreg Roach * 603b6f35a76SGreg Roach * Also updates Max Y position 604b6f35a76SGreg Roach * 605b6f35a76SGreg Roach * @param float $y 606b6f35a76SGreg Roach * 607b6f35a76SGreg Roach * @return void 608b6f35a76SGreg Roach */ 60924f2a3afSGreg Roach public function setY(float $y): void 610b6f35a76SGreg Roach { 611b6f35a76SGreg Roach $this->Y = $y; 612b6f35a76SGreg Roach if ($this->maxY < $y) { 613b6f35a76SGreg Roach $this->maxY = $y; 614b6f35a76SGreg Roach } 615b6f35a76SGreg Roach } 616b6f35a76SGreg Roach 617b6f35a76SGreg Roach /** 618b6f35a76SGreg Roach * Set the X and Y position - ReportHtml 619b6f35a76SGreg Roach * 620b6f35a76SGreg Roach * Also updates Max Y position 621b6f35a76SGreg Roach * 622b6f35a76SGreg Roach * @param float $x 623b6f35a76SGreg Roach * @param float $y 624b6f35a76SGreg Roach * 625b6f35a76SGreg Roach * @return void 626b6f35a76SGreg Roach */ 62724f2a3afSGreg Roach public function setXy(float $x, float $y): void 628b6f35a76SGreg Roach { 629b6f35a76SGreg Roach $this->setX($x); 630b6f35a76SGreg Roach $this->setY($y); 631b6f35a76SGreg Roach } 632b6f35a76SGreg Roach 633b6f35a76SGreg Roach /** 634b6f35a76SGreg Roach * Wrap text - ReportHtml 635b6f35a76SGreg Roach * 636b6f35a76SGreg Roach * @param string $str Text to wrap 637b6f35a76SGreg Roach * @param float $width Width in points the text has to fit into 638b6f35a76SGreg Roach * 639b6f35a76SGreg Roach * @return string 640b6f35a76SGreg Roach */ 641b6f35a76SGreg Roach public function textWrap(string $str, float $width): string 642b6f35a76SGreg Roach { 6435deaecabSGreg Roach $line_width = (int) ($width / ($this->getCurrentStyleHeight() / 2)); 6445deaecabSGreg Roach 645b6f35a76SGreg Roach $lines = explode("\n", $str); 6465deaecabSGreg Roach 6470b1d3aa5SGreg Roach $lines = array_map(fn (string $string): string => $this->utf8WordWrap($string, $line_width), $lines); 6485deaecabSGreg Roach 6495deaecabSGreg Roach return implode("\n", $lines); 650b6f35a76SGreg Roach } 651b6f35a76SGreg Roach 6525deaecabSGreg Roach /** 6535deaecabSGreg Roach * Wrap text, similar to the PHP wordwrap() function. 6545deaecabSGreg Roach * 6555deaecabSGreg Roach * @param string $string 6565deaecabSGreg Roach * @param int $width 6575deaecabSGreg Roach * 6585deaecabSGreg Roach * @return string 6595deaecabSGreg Roach */ 6605deaecabSGreg Roach private function utf8WordWrap(string $string, int $width): string 6615deaecabSGreg Roach { 6625deaecabSGreg Roach $out = ''; 6635deaecabSGreg Roach while ($string) { 6645deaecabSGreg Roach if (mb_strlen($string) <= $width) { 6655deaecabSGreg Roach // Do not wrap any text that is less than the output area. 6665deaecabSGreg Roach $out .= $string; 6675deaecabSGreg Roach $string = ''; 6685deaecabSGreg Roach } else { 6695deaecabSGreg Roach $sub1 = mb_substr($string, 0, $width + 1); 6705deaecabSGreg Roach if (mb_substr($string, mb_strlen($sub1) - 1, 1) === ' ') { 6715deaecabSGreg Roach // include words that end by a space immediately after the area. 6725deaecabSGreg Roach $sub = $sub1; 6735deaecabSGreg Roach } else { 6745deaecabSGreg Roach $sub = mb_substr($string, 0, $width); 6755deaecabSGreg Roach } 6765deaecabSGreg Roach $spacepos = strrpos($sub, ' '); 6775deaecabSGreg Roach if ($spacepos === false) { 6785deaecabSGreg Roach // No space on line? 6795deaecabSGreg Roach $out .= $sub . "\n"; 6805deaecabSGreg Roach $string = mb_substr($string, mb_strlen($sub)); 6815deaecabSGreg Roach } else { 6825deaecabSGreg Roach // Split at space; 6835deaecabSGreg Roach $out .= substr($string, 0, $spacepos) . "\n"; 6845deaecabSGreg Roach $string = substr($string, $spacepos + 1); 6855deaecabSGreg Roach } 6865deaecabSGreg Roach } 6875deaecabSGreg Roach } 6885deaecabSGreg Roach 6895deaecabSGreg Roach return $out; 690b6f35a76SGreg Roach } 691b6f35a76SGreg Roach 692b6f35a76SGreg Roach /** 693b6f35a76SGreg Roach * Write text - ReportHtml 694b6f35a76SGreg Roach * 695b6f35a76SGreg Roach * @param string $text Text to print 696b6f35a76SGreg Roach * @param string $color HTML RGB color code (Ex: #001122) 697b6f35a76SGreg Roach * @param bool $useclass 698b6f35a76SGreg Roach * 699b6f35a76SGreg Roach * @return void 700b6f35a76SGreg Roach */ 70124f2a3afSGreg Roach public function write(string $text, string $color = '', bool $useclass = true): void 702b6f35a76SGreg Roach { 703b6f35a76SGreg Roach $style = $this->getStyle($this->getCurrentStyle()); 704b6f35a76SGreg Roach $htmlcode = '<span dir="' . I18N::direction() . '"'; 705b6f35a76SGreg Roach if ($useclass) { 706b6f35a76SGreg Roach $htmlcode .= ' class="' . $style['name'] . '"'; 707b6f35a76SGreg Roach } 708b6f35a76SGreg Roach // Check if Text Color is set and if it’s valid HTML color 709b6f35a76SGreg Roach if (preg_match('/#?(..)(..)(..)/', $color)) { 710b6f35a76SGreg Roach $htmlcode .= ' style="color:' . $color . ';"'; 711b6f35a76SGreg Roach } 712b6f35a76SGreg Roach 713b6f35a76SGreg Roach $htmlcode .= '>' . $text . '</span>'; 714b6f35a76SGreg Roach $htmlcode = str_replace([ 715b6f35a76SGreg Roach "\n", 716b6f35a76SGreg Roach '> ', 717b6f35a76SGreg Roach ' <', 718b6f35a76SGreg Roach ], [ 719b6f35a76SGreg Roach '<br>', 720b6f35a76SGreg Roach '> ', 721b6f35a76SGreg Roach ' <', 722b6f35a76SGreg Roach ], $htmlcode); 723b6f35a76SGreg Roach echo $htmlcode; 724b6f35a76SGreg Roach } 725b6f35a76SGreg Roach} 726