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