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\Functions\FunctionsRtl; 23b6f35a76SGreg Roachuse Fisharebest\Webtrees\I18N; 24b6f35a76SGreg Roachuse Fisharebest\Webtrees\MediaFile; 25b6f35a76SGreg Roachuse Fisharebest\Webtrees\Webtrees; 26f7cf8a15SGreg Roachuse League\Flysystem\FilesystemOperator; 27b6f35a76SGreg Roach 285deaecabSGreg Roachuse function array_map; 29b6f35a76SGreg Roachuse function ceil; 30b6f35a76SGreg Roachuse function count; 31b6f35a76SGreg Roachuse function explode; 325deaecabSGreg Roachuse function implode; 33b6f35a76SGreg Roachuse function preg_match; 34b6f35a76SGreg Roachuse function str_replace; 35b6f35a76SGreg Roachuse function stripos; 365deaecabSGreg Roachuse function strrpos; 375deaecabSGreg Roachuse function substr; 38b6f35a76SGreg Roachuse function substr_count; 39b6f35a76SGreg Roach 40b6f35a76SGreg Roach/** 41b6f35a76SGreg Roach * Class HtmlRenderer 42b6f35a76SGreg Roach */ 43b6f35a76SGreg Roachclass HtmlRenderer extends AbstractRenderer 44b6f35a76SGreg Roach{ 45b6f35a76SGreg Roach /** 46b6f35a76SGreg Roach * Cell padding 47b6f35a76SGreg Roach * 48b6f35a76SGreg Roach * @var float 49b6f35a76SGreg Roach */ 50b6f35a76SGreg Roach public $cPadding = 2; 51b6f35a76SGreg Roach 52b6f35a76SGreg Roach /** 53b6f35a76SGreg Roach * Cell height ratio 54b6f35a76SGreg Roach * 55b6f35a76SGreg Roach * @var float 56b6f35a76SGreg Roach */ 57b6f35a76SGreg Roach public $cellHeightRatio = 1.8; 58b6f35a76SGreg Roach 59b6f35a76SGreg Roach /** 60b6f35a76SGreg Roach * Current horizontal position 61b6f35a76SGreg Roach * 62b6f35a76SGreg Roach * @var float 63b6f35a76SGreg Roach */ 64b6f35a76SGreg Roach public $X = 0.0; 65b6f35a76SGreg Roach 66b6f35a76SGreg Roach /** 67b6f35a76SGreg Roach * Current vertical position 68b6f35a76SGreg Roach * 69b6f35a76SGreg Roach * @var float 70b6f35a76SGreg Roach */ 71b6f35a76SGreg Roach public $Y = 0.0; 72b6f35a76SGreg Roach 73b6f35a76SGreg Roach /** 74b6f35a76SGreg Roach * Currently used style name 75b6f35a76SGreg Roach * 76b6f35a76SGreg Roach * @var string 77b6f35a76SGreg Roach */ 78b6f35a76SGreg Roach public $currentStyle = ''; 79b6f35a76SGreg Roach 80b6f35a76SGreg Roach /** 81b6f35a76SGreg Roach * Page number counter 82b6f35a76SGreg Roach * 83b6f35a76SGreg Roach * @var int 84b6f35a76SGreg Roach */ 85b6f35a76SGreg Roach public $pageN = 1; 86b6f35a76SGreg Roach 87b6f35a76SGreg Roach /** 88b6f35a76SGreg Roach * Store the page width without left and right margins 89b6f35a76SGreg Roach * 90b6f35a76SGreg Roach * In HTML, we don't need this 91b6f35a76SGreg Roach * 92b6f35a76SGreg Roach * @var float 93b6f35a76SGreg Roach */ 94b6f35a76SGreg Roach public $noMarginWidth = 0.0; 95b6f35a76SGreg Roach 96b6f35a76SGreg Roach /** 97b6f35a76SGreg Roach * Last cell height 98b6f35a76SGreg Roach * 99b6f35a76SGreg Roach * @var float 100b6f35a76SGreg Roach */ 101b6f35a76SGreg Roach public $lastCellHeight = 0.0; 102b6f35a76SGreg Roach 103b6f35a76SGreg Roach /** 104b6f35a76SGreg Roach * LTR or RTL alignement; "left" on LTR, "right" on RTL 105b6f35a76SGreg Roach * Used in <div> 106b6f35a76SGreg Roach * 107b6f35a76SGreg Roach * @var string 108b6f35a76SGreg Roach */ 109b6f35a76SGreg Roach public $alignRTL = 'left'; 110b6f35a76SGreg Roach 111b6f35a76SGreg Roach /** 112b6f35a76SGreg Roach * LTR or RTL entity 113b6f35a76SGreg Roach * 114b6f35a76SGreg Roach * @var string 115b6f35a76SGreg Roach */ 116b6f35a76SGreg Roach public $entityRTL = '‎'; 117b6f35a76SGreg Roach 118b6f35a76SGreg Roach /** 119b6f35a76SGreg Roach * Largest Font Height is used by TextBox etc. 120b6f35a76SGreg Roach * 121b6f35a76SGreg Roach * Use this to calculate a the text height. 122b6f35a76SGreg Roach * This makes sure that the text fits into the cell/box when different font sizes are used 123b6f35a76SGreg Roach * 124b6f35a76SGreg Roach * @var float 125b6f35a76SGreg Roach */ 126b6f35a76SGreg Roach public $largestFontHeight = 0; 127b6f35a76SGreg Roach 128b6f35a76SGreg Roach /** 129b6f35a76SGreg Roach * Keep track of the highest Y position 130b6f35a76SGreg Roach * 131b6f35a76SGreg Roach * Used with Header div / Body div / Footer div / "addpage" / The bottom of the last image etc. 132b6f35a76SGreg Roach * 133b6f35a76SGreg Roach * @var float 134b6f35a76SGreg Roach */ 135b6f35a76SGreg Roach public $maxY = 0; 136b6f35a76SGreg Roach 137b6f35a76SGreg Roach /** @var ReportBaseElement[] Array of elements in the header */ 138b6f35a76SGreg Roach public $headerElements = []; 139b6f35a76SGreg Roach 140b6f35a76SGreg Roach /** @var ReportBaseElement[] Array of elements in the footer */ 141b6f35a76SGreg Roach public $footerElements = []; 142b6f35a76SGreg Roach 143b6f35a76SGreg Roach /** @var ReportBaseElement[] Array of elements in the body */ 144b6f35a76SGreg Roach public $bodyElements = []; 145b6f35a76SGreg Roach 146b6f35a76SGreg Roach /** @var ReportHtmlFootnote[] Array of elements in the footer notes */ 147b6f35a76SGreg Roach public $printedfootnotes = []; 148b6f35a76SGreg Roach 149b6f35a76SGreg Roach /** 150b6f35a76SGreg Roach * HTML Setup - ReportHtml 151b6f35a76SGreg Roach * 152b6f35a76SGreg Roach * @return void 153b6f35a76SGreg Roach */ 154b6f35a76SGreg Roach public function setup(): void 155b6f35a76SGreg Roach { 156b6f35a76SGreg Roach parent::setup(); 157b6f35a76SGreg Roach 158b6f35a76SGreg Roach // Setting up the correct dimensions if Portrait (default) or Landscape 159b6f35a76SGreg Roach if ($this->orientation === 'landscape') { 160b6f35a76SGreg Roach $tmpw = $this->page_width; 161b6f35a76SGreg Roach $this->page_width = $this->page_height; 162b6f35a76SGreg Roach $this->page_height = $tmpw; 163b6f35a76SGreg Roach } 164b6f35a76SGreg Roach // Store the pagewidth without margins 165b6f35a76SGreg Roach $this->noMarginWidth = $this->page_width - $this->left_margin - $this->right_margin; 166b6f35a76SGreg Roach // If RTL 167b6f35a76SGreg Roach if ($this->rtl) { 168b6f35a76SGreg Roach $this->alignRTL = 'right'; 169b6f35a76SGreg Roach $this->entityRTL = '‏'; 170b6f35a76SGreg Roach } 171b6f35a76SGreg Roach // Change the default HTML font name 172b6f35a76SGreg Roach $this->default_font = 'Arial'; 173b6f35a76SGreg Roach 174b6f35a76SGreg Roach if ($this->show_generated_by) { 175b6f35a76SGreg Roach // The default style name for Generated by.... is 'genby' 176b6f35a76SGreg Roach $element = new ReportHtmlCell(0, 10, 0, 'C', '', 'genby', 1, ReportBaseElement::CURRENT_POSITION, ReportBaseElement::CURRENT_POSITION, 0, 0, '', '', true); 177b6f35a76SGreg Roach $element->addText($this->generated_by); 178b6f35a76SGreg Roach $element->setUrl(Webtrees::URL); 179b6f35a76SGreg Roach $this->footerElements[] = $element; 180b6f35a76SGreg Roach } 181b6f35a76SGreg Roach } 182b6f35a76SGreg Roach 183b6f35a76SGreg Roach /** 184b6f35a76SGreg Roach * Add an element. 185b6f35a76SGreg Roach * 186b6f35a76SGreg Roach * @param ReportBaseElement|string $element 187b6f35a76SGreg Roach * 188b6f35a76SGreg Roach * @return void 189b6f35a76SGreg Roach */ 190b6f35a76SGreg Roach public function addElement($element): void 191b6f35a76SGreg Roach { 192b6f35a76SGreg Roach if ($this->processing === 'B') { 193b6f35a76SGreg Roach $this->bodyElements[] = $element; 194b6f35a76SGreg Roach } elseif ($this->processing === 'H') { 195b6f35a76SGreg Roach $this->headerElements[] = $element; 196b6f35a76SGreg Roach } elseif ($this->processing === 'F') { 197b6f35a76SGreg Roach $this->footerElements[] = $element; 198b6f35a76SGreg Roach } 199b6f35a76SGreg Roach } 200b6f35a76SGreg Roach 201b6f35a76SGreg Roach /** 202b6f35a76SGreg Roach * Generate footnotes 203b6f35a76SGreg Roach * 204b6f35a76SGreg Roach * @return void 205b6f35a76SGreg Roach */ 206b6f35a76SGreg Roach public function footnotes(): void 207b6f35a76SGreg Roach { 208b6f35a76SGreg Roach $this->currentStyle = ''; 209b6f35a76SGreg Roach if (!empty($this->printedfootnotes)) { 210b6f35a76SGreg Roach foreach ($this->printedfootnotes as $element) { 211b6f35a76SGreg Roach $element->renderFootnote($this); 212b6f35a76SGreg Roach } 213b6f35a76SGreg Roach } 214b6f35a76SGreg Roach } 215b6f35a76SGreg Roach 216b6f35a76SGreg Roach /** 217b6f35a76SGreg Roach * Run the report. 218b6f35a76SGreg Roach * 219b6f35a76SGreg Roach * @return void 220b6f35a76SGreg Roach */ 221b6f35a76SGreg Roach public function run(): void 222b6f35a76SGreg Roach { 223b6f35a76SGreg Roach // Setting up the styles 224*b1ee2291SGreg Roach echo '<style>'; 225b6f35a76SGreg Roach echo '#bodydiv { font: 10px sans-serif;}'; 226b6f35a76SGreg Roach foreach ($this->styles as $class => $style) { 227b6f35a76SGreg Roach echo '.', $class, ' { '; 228b6f35a76SGreg Roach if ($style['font'] === 'dejavusans') { 229b6f35a76SGreg Roach $style['font'] = $this->default_font; 230b6f35a76SGreg Roach } 231b6f35a76SGreg Roach echo 'font-family: ', $style['font'], '; '; 232b6f35a76SGreg Roach echo 'font-size: ', $style['size'], 'pt; '; 233b6f35a76SGreg Roach // Case-insensitive 234b6f35a76SGreg Roach if (stripos($style['style'], 'B') !== false) { 235b6f35a76SGreg Roach echo 'font-weight: bold; '; 236b6f35a76SGreg Roach } 237b6f35a76SGreg Roach if (stripos($style['style'], 'I') !== false) { 238b6f35a76SGreg Roach echo 'font-style: italic; '; 239b6f35a76SGreg Roach } 240b6f35a76SGreg Roach if (stripos($style['style'], 'U') !== false) { 241b6f35a76SGreg Roach echo 'text-decoration: underline; '; 242b6f35a76SGreg Roach } 243b6f35a76SGreg Roach if (stripos($style['style'], 'D') !== false) { 244b6f35a76SGreg Roach echo 'text-decoration: line-through; '; 245b6f35a76SGreg Roach } 246b6f35a76SGreg Roach echo '}', PHP_EOL; 247b6f35a76SGreg Roach } 248b6f35a76SGreg Roach 249b6f35a76SGreg Roach //-- header divider 250b6f35a76SGreg Roach echo '</style>', PHP_EOL; 251b6f35a76SGreg Roach echo '<div id="headermargin" style="position: relative; top: auto; height: ', $this->header_margin, 'pt; width: ', $this->noMarginWidth, 'pt;"></div>'; 252b6f35a76SGreg Roach echo '<div id="headerdiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt;">'; 253b6f35a76SGreg Roach foreach ($this->headerElements as $element) { 254b6f35a76SGreg Roach if ($element instanceof ReportBaseElement) { 255b6f35a76SGreg Roach $element->render($this); 256b6f35a76SGreg Roach } elseif ($element === 'footnotetexts') { 257b6f35a76SGreg Roach $this->footnotes(); 258b6f35a76SGreg Roach } elseif ($element === 'addpage') { 259b6f35a76SGreg Roach $this->addPage(); 260b6f35a76SGreg Roach } 261b6f35a76SGreg Roach } 262b6f35a76SGreg Roach //-- body 263b6f35a76SGreg Roach echo '</div>'; 264b6f35a76SGreg Roach echo '<script>document.getElementById("headerdiv").style.height="', $this->top_margin - $this->header_margin - 6, 'pt";</script>'; 265b6f35a76SGreg Roach echo '<div id="bodydiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt; height: 100%;">'; 266b6f35a76SGreg Roach $this->Y = 0; 267b6f35a76SGreg Roach $this->maxY = 0; 268b6f35a76SGreg Roach foreach ($this->bodyElements as $element) { 269b6f35a76SGreg Roach if ($element instanceof ReportBaseElement) { 270b6f35a76SGreg Roach $element->render($this); 271b6f35a76SGreg Roach } elseif ($element === 'footnotetexts') { 272b6f35a76SGreg Roach $this->footnotes(); 273b6f35a76SGreg Roach } elseif ($element === 'addpage') { 274b6f35a76SGreg Roach $this->addPage(); 275b6f35a76SGreg Roach } 276b6f35a76SGreg Roach } 277b6f35a76SGreg Roach //-- footer 278b6f35a76SGreg Roach echo '</div>'; 279b6f35a76SGreg Roach echo '<script>document.getElementById("bodydiv").style.height="', $this->maxY, 'pt";</script>'; 280b6f35a76SGreg Roach echo '<div id="bottommargin" style="position: relative; top: auto; height: ', $this->bottom_margin - $this->footer_margin, 'pt;width:', $this->noMarginWidth, 'pt;"></div>'; 281b6f35a76SGreg Roach echo '<div id="footerdiv" style="position: relative; top: auto; width: ', $this->noMarginWidth, 'pt;height:auto;">'; 282b6f35a76SGreg Roach $this->Y = 0; 283b6f35a76SGreg Roach $this->X = 0; 284b6f35a76SGreg Roach $this->maxY = 0; 285b6f35a76SGreg Roach foreach ($this->footerElements as $element) { 286b6f35a76SGreg Roach if ($element instanceof ReportBaseElement) { 287b6f35a76SGreg Roach $element->render($this); 288b6f35a76SGreg Roach } elseif ($element === 'footnotetexts') { 289b6f35a76SGreg Roach $this->footnotes(); 290b6f35a76SGreg Roach } elseif ($element === 'addpage') { 291b6f35a76SGreg Roach $this->addPage(); 292b6f35a76SGreg Roach } 293b6f35a76SGreg Roach } 294b6f35a76SGreg Roach echo '</div>'; 295b6f35a76SGreg Roach echo '<script>document.getElementById("footerdiv").style.height="', $this->maxY, 'pt";</script>'; 296b6f35a76SGreg Roach echo '<div id="footermargin" style="position: relative; top: auto; height: ', $this->footer_margin, 'pt;width:', $this->noMarginWidth, 'pt;"></div>'; 297b6f35a76SGreg Roach } 298b6f35a76SGreg Roach 299b6f35a76SGreg Roach /** 300b6f35a76SGreg Roach * Create a new Cell object. 301b6f35a76SGreg Roach * 302b6f35a76SGreg Roach * @param int $width cell width (expressed in points) 303b6f35a76SGreg Roach * @param int $height cell height (expressed in points) 304b6f35a76SGreg Roach * @param mixed $border Border style 305b6f35a76SGreg Roach * @param string $align Text alignement 306b6f35a76SGreg Roach * @param string $bgcolor Background color code 307b6f35a76SGreg Roach * @param string $style The name of the text style 308b6f35a76SGreg Roach * @param int $ln Indicates where the current position should go after the call 309b6f35a76SGreg Roach * @param mixed $top Y-position 310b6f35a76SGreg Roach * @param mixed $left X-position 311b6f35a76SGreg Roach * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 1 31224f2a3afSGreg Roach * @param int $stretch Stretch character mode 313b6f35a76SGreg Roach * @param string $bocolor Border color 314b6f35a76SGreg Roach * @param string $tcolor Text color 315b6f35a76SGreg Roach * @param bool $reseth 316b6f35a76SGreg Roach * 317b6f35a76SGreg Roach * @return ReportBaseCell 318b6f35a76SGreg Roach */ 31924f2a3afSGreg Roach public function createCell(int $width, int $height, $border, string $align, string $bgcolor, string $style, int $ln, $top, $left, int $fill, int $stretch, string $bocolor, string $tcolor, bool $reseth): ReportBaseCell 320b6f35a76SGreg Roach { 321b6f35a76SGreg Roach return new ReportHtmlCell($width, $height, $border, $align, $bgcolor, $style, $ln, $top, $left, $fill, $stretch, $bocolor, $tcolor, $reseth); 322b6f35a76SGreg Roach } 323b6f35a76SGreg Roach 324b6f35a76SGreg Roach /** 325b6f35a76SGreg Roach * Create a new TextBox object. 326b6f35a76SGreg Roach * 327b6f35a76SGreg Roach * @param float $width Text box width 328b6f35a76SGreg Roach * @param float $height Text box height 329b6f35a76SGreg Roach * @param bool $border 330b6f35a76SGreg Roach * @param string $bgcolor Background color code in HTML 331b6f35a76SGreg Roach * @param bool $newline 332b6f35a76SGreg Roach * @param float $left 333b6f35a76SGreg Roach * @param float $top 334b6f35a76SGreg Roach * @param bool $pagecheck 335b6f35a76SGreg Roach * @param string $style 336b6f35a76SGreg Roach * @param bool $fill 337b6f35a76SGreg Roach * @param bool $padding 338b6f35a76SGreg Roach * @param bool $reseth 339b6f35a76SGreg Roach * 340b6f35a76SGreg Roach * @return ReportBaseTextbox 341b6f35a76SGreg Roach */ 342b6f35a76SGreg Roach public function createTextBox( 343b6f35a76SGreg Roach float $width, 344b6f35a76SGreg Roach float $height, 345b6f35a76SGreg Roach bool $border, 346b6f35a76SGreg Roach string $bgcolor, 347b6f35a76SGreg Roach bool $newline, 348b6f35a76SGreg Roach float $left, 349b6f35a76SGreg Roach float $top, 350b6f35a76SGreg Roach bool $pagecheck, 351b6f35a76SGreg Roach string $style, 352b6f35a76SGreg Roach bool $fill, 353b6f35a76SGreg Roach bool $padding, 354b6f35a76SGreg Roach bool $reseth 355b6f35a76SGreg Roach ): ReportBaseTextbox { 356b6f35a76SGreg Roach return new ReportHtmlTextbox($width, $height, $border, $bgcolor, $newline, $left, $top, $pagecheck, $style, $fill, $padding, $reseth); 357b6f35a76SGreg Roach } 358b6f35a76SGreg Roach 359b6f35a76SGreg Roach /** 360b6f35a76SGreg Roach * Create a text element. 361b6f35a76SGreg Roach * 362b6f35a76SGreg Roach * @param string $style 363b6f35a76SGreg Roach * @param string $color 364b6f35a76SGreg Roach * 365b6f35a76SGreg Roach * @return ReportBaseText 366b6f35a76SGreg Roach */ 367b6f35a76SGreg Roach public function createText(string $style, string $color): ReportBaseText 368b6f35a76SGreg Roach { 369b6f35a76SGreg Roach return new ReportHtmlText($style, $color); 370b6f35a76SGreg Roach } 371b6f35a76SGreg Roach 372b6f35a76SGreg Roach /** 373b6f35a76SGreg Roach * Create a new Footnote object. 374b6f35a76SGreg Roach * 375b6f35a76SGreg Roach * @param string $style Style name 376b6f35a76SGreg Roach * 377b6f35a76SGreg Roach * @return ReportBaseFootnote 378b6f35a76SGreg Roach */ 37924f2a3afSGreg Roach public function createFootnote(string $style): ReportBaseFootnote 380b6f35a76SGreg Roach { 381b6f35a76SGreg Roach return new ReportHtmlFootnote($style); 382b6f35a76SGreg Roach } 383b6f35a76SGreg Roach 384b6f35a76SGreg Roach /** 385b6f35a76SGreg Roach * Create a new image object. 386b6f35a76SGreg Roach * 387b6f35a76SGreg Roach * @param string $file Filename 388b6f35a76SGreg Roach * @param float $x 389b6f35a76SGreg Roach * @param float $y 390b6f35a76SGreg Roach * @param float $w Image width 391b6f35a76SGreg Roach * @param float $h Image height 392b6f35a76SGreg Roach * @param string $align L:left, C:center, R:right or empty to use x/y 393b6f35a76SGreg Roach * @param string $ln T:same line, N:next line 394b6f35a76SGreg Roach * 395b6f35a76SGreg Roach * @return ReportBaseImage 396b6f35a76SGreg Roach */ 397b6f35a76SGreg Roach public function createImage(string $file, float $x, float $y, float $w, float $h, string $align, string $ln): ReportBaseImage 398b6f35a76SGreg Roach { 399b6f35a76SGreg Roach return new ReportHtmlImage($file, $x, $y, $w, $h, $align, $ln); 400b6f35a76SGreg Roach } 401b6f35a76SGreg Roach 402b6f35a76SGreg Roach /** 403b6f35a76SGreg Roach * Create a new image object from Media Object. 404b6f35a76SGreg Roach * 405b6f35a76SGreg Roach * @param MediaFile $media_file 406b6f35a76SGreg Roach * @param float $x 407b6f35a76SGreg Roach * @param float $y 408b6f35a76SGreg Roach * @param float $w Image width 409b6f35a76SGreg Roach * @param float $h Image height 410b6f35a76SGreg Roach * @param string $align L:left, C:center, R:right or empty to use x/y 411b6f35a76SGreg Roach * @param string $ln T:same line, N:next line 412f7cf8a15SGreg Roach * @param FilesystemOperator $data_filesystem 413b6f35a76SGreg Roach * 414b6f35a76SGreg Roach * @return ReportBaseImage 415b6f35a76SGreg Roach */ 416b6f35a76SGreg Roach public function createImageFromObject( 417b6f35a76SGreg Roach MediaFile $media_file, 418b6f35a76SGreg Roach float $x, 419b6f35a76SGreg Roach float $y, 420b6f35a76SGreg Roach float $w, 421b6f35a76SGreg Roach float $h, 422b6f35a76SGreg Roach string $align, 423b6f35a76SGreg Roach string $ln, 424f7cf8a15SGreg Roach FilesystemOperator $data_filesystem 425b6f35a76SGreg Roach ): ReportBaseImage { 4261558ebe9SGreg Roach return new ReportHtmlImage($media_file->imageUrl((int) $w, (int) $h, 'crop'), $x, $y, $w, $h, $align, $ln); 427b6f35a76SGreg Roach } 428b6f35a76SGreg Roach 429b6f35a76SGreg Roach /** 430b6f35a76SGreg Roach * Create a line. 431b6f35a76SGreg Roach * 432b6f35a76SGreg Roach * @param float $x1 433b6f35a76SGreg Roach * @param float $y1 434b6f35a76SGreg Roach * @param float $x2 435b6f35a76SGreg Roach * @param float $y2 436b6f35a76SGreg Roach * 437b6f35a76SGreg Roach * @return ReportBaseLine 438b6f35a76SGreg Roach */ 439b6f35a76SGreg Roach public function createLine(float $x1, float $y1, float $x2, float $y2): ReportBaseLine 440b6f35a76SGreg Roach { 441b6f35a76SGreg Roach return new ReportHtmlLine($x1, $y1, $x2, $y2); 442b6f35a76SGreg Roach } 443b6f35a76SGreg Roach 444b6f35a76SGreg Roach /** 445b6f35a76SGreg Roach * Clear the Header 446b6f35a76SGreg Roach * 447b6f35a76SGreg Roach * @return void 448b6f35a76SGreg Roach */ 449b6f35a76SGreg Roach public function clearHeader(): void 450b6f35a76SGreg Roach { 451b6f35a76SGreg Roach $this->headerElements = []; 452b6f35a76SGreg Roach } 453b6f35a76SGreg Roach 454b6f35a76SGreg Roach /** 455b6f35a76SGreg Roach * Update the Page Number and set a new Y if max Y is larger - ReportHtml 456b6f35a76SGreg Roach * 457b6f35a76SGreg Roach * @return void 458b6f35a76SGreg Roach */ 459b6f35a76SGreg Roach public function addPage(): void 460b6f35a76SGreg Roach { 461b6f35a76SGreg Roach $this->pageN++; 462b6f35a76SGreg Roach 463b6f35a76SGreg Roach // Add a little margin to max Y "between pages" 464b6f35a76SGreg Roach $this->maxY += 10; 465b6f35a76SGreg Roach 466b6f35a76SGreg Roach // If Y is still heigher by any reason... 467b6f35a76SGreg Roach if ($this->maxY < $this->Y) { 468b6f35a76SGreg Roach // ... update max Y 469b6f35a76SGreg Roach $this->maxY = $this->Y; 470b6f35a76SGreg Roach } else { 471b6f35a76SGreg Roach // else update Y so that nothing will be overwritten, like images or cells... 472b6f35a76SGreg Roach $this->Y = $this->maxY; 473b6f35a76SGreg Roach } 474b6f35a76SGreg Roach } 475b6f35a76SGreg Roach 476b6f35a76SGreg Roach /** 477b6f35a76SGreg Roach * Uppdate max Y to keep track it incase of a pagebreak - ReportHtml 478b6f35a76SGreg Roach * 479b6f35a76SGreg Roach * @param float $y 480b6f35a76SGreg Roach * 481b6f35a76SGreg Roach * @return void 482b6f35a76SGreg Roach */ 483b6f35a76SGreg Roach public function addMaxY($y): void 484b6f35a76SGreg Roach { 485b6f35a76SGreg Roach if ($this->maxY < $y) { 486b6f35a76SGreg Roach $this->maxY = $y; 487b6f35a76SGreg Roach } 488b6f35a76SGreg Roach } 489b6f35a76SGreg Roach 490b6f35a76SGreg Roach /** 491b6f35a76SGreg Roach * Checks the Footnote and numbers them - ReportHtml 492b6f35a76SGreg Roach * 493b6f35a76SGreg Roach * @param ReportHtmlFootnote $footnote 494b6f35a76SGreg Roach * 495b6f35a76SGreg Roach * @return ReportHtmlFootnote|bool object if already numbered, false otherwise 496b6f35a76SGreg Roach */ 497b6f35a76SGreg Roach public function checkFootnote(ReportHtmlFootnote $footnote) 498b6f35a76SGreg Roach { 499b6f35a76SGreg Roach $ct = count($this->printedfootnotes); 500b6f35a76SGreg Roach $i = 0; 501b6f35a76SGreg Roach $val = $footnote->getValue(); 502b6f35a76SGreg Roach while ($i < $ct) { 503b6f35a76SGreg Roach if ($this->printedfootnotes[$i]->getValue() === $val) { 504b6f35a76SGreg Roach // If this footnote already exist then set up the numbers for this object 505b6f35a76SGreg Roach $footnote->setNum($i + 1); 506b6f35a76SGreg Roach $footnote->setAddlink((string) ($i + 1)); 507b6f35a76SGreg Roach 508b6f35a76SGreg Roach return $this->printedfootnotes[$i]; 509b6f35a76SGreg Roach } 510b6f35a76SGreg Roach $i++; 511b6f35a76SGreg Roach } 512b6f35a76SGreg Roach // If this Footnote has not been set up yet 513b6f35a76SGreg Roach $footnote->setNum($ct + 1); 514b6f35a76SGreg Roach $footnote->setAddlink((string) ($ct + 1)); 515b6f35a76SGreg Roach $this->printedfootnotes[] = $footnote; 516b6f35a76SGreg Roach 517b6f35a76SGreg Roach return false; 518b6f35a76SGreg Roach } 519b6f35a76SGreg Roach 520b6f35a76SGreg Roach /** 521b6f35a76SGreg Roach * Count the number of lines - ReportHtml 522b6f35a76SGreg Roach * 523b6f35a76SGreg Roach * @param string $str 524b6f35a76SGreg Roach * 525b6f35a76SGreg Roach * @return int Number of lines. 0 if empty line 526b6f35a76SGreg Roach */ 527b6f35a76SGreg Roach public function countLines($str): int 528b6f35a76SGreg Roach { 529b6f35a76SGreg Roach if ($str === '') { 530b6f35a76SGreg Roach return 0; 531b6f35a76SGreg Roach } 532b6f35a76SGreg Roach 533b6f35a76SGreg Roach return substr_count($str, "\n") + 1; 534b6f35a76SGreg Roach } 535b6f35a76SGreg Roach 536b6f35a76SGreg Roach /** 537b6f35a76SGreg Roach * Get the current style. 538b6f35a76SGreg Roach * 539b6f35a76SGreg Roach * @return string 540b6f35a76SGreg Roach */ 541b6f35a76SGreg Roach public function getCurrentStyle(): string 542b6f35a76SGreg Roach { 543b6f35a76SGreg Roach return $this->currentStyle; 544b6f35a76SGreg Roach } 545b6f35a76SGreg Roach 546b6f35a76SGreg Roach /** 547b6f35a76SGreg Roach * Get the current style height. 548b6f35a76SGreg Roach * 549b6f35a76SGreg Roach * @return float 550b6f35a76SGreg Roach */ 551b6f35a76SGreg Roach public function getCurrentStyleHeight(): float 552b6f35a76SGreg Roach { 553b6f35a76SGreg Roach if (empty($this->currentStyle)) { 554b6f35a76SGreg Roach return $this->default_font_size; 555b6f35a76SGreg Roach } 556b6f35a76SGreg Roach $style = $this->getStyle($this->currentStyle); 557b6f35a76SGreg Roach 558b6f35a76SGreg Roach return (float) $style['size']; 559b6f35a76SGreg Roach } 560b6f35a76SGreg Roach 561b6f35a76SGreg Roach /** 562b6f35a76SGreg Roach * Get the current footnotes height. 563b6f35a76SGreg Roach * 564b6f35a76SGreg Roach * @param float $cellWidth 565b6f35a76SGreg Roach * 566b6f35a76SGreg Roach * @return float 567b6f35a76SGreg Roach */ 568b6f35a76SGreg Roach public function getFootnotesHeight(float $cellWidth): float 569b6f35a76SGreg Roach { 570b6f35a76SGreg Roach $h = 0; 571b6f35a76SGreg Roach foreach ($this->printedfootnotes as $element) { 572b6f35a76SGreg Roach $h += $element->getFootnoteHeight($this, $cellWidth); 573b6f35a76SGreg Roach } 574b6f35a76SGreg Roach 575b6f35a76SGreg Roach return $h; 576b6f35a76SGreg Roach } 577b6f35a76SGreg Roach 578b6f35a76SGreg Roach /** 579b6f35a76SGreg Roach * Get the maximum width from current position to the margin - ReportHtml 580b6f35a76SGreg Roach * 581b6f35a76SGreg Roach * @return float 582b6f35a76SGreg Roach */ 583b6f35a76SGreg Roach public function getRemainingWidth(): float 584b6f35a76SGreg Roach { 585b6f35a76SGreg Roach return $this->noMarginWidth - $this->X; 586b6f35a76SGreg Roach } 587b6f35a76SGreg Roach 588b6f35a76SGreg Roach /** 589b6f35a76SGreg Roach * Get the page height. 590b6f35a76SGreg Roach * 591b6f35a76SGreg Roach * @return float 592b6f35a76SGreg Roach */ 593b6f35a76SGreg Roach public function getPageHeight(): float 594b6f35a76SGreg Roach { 595b6f35a76SGreg Roach return $this->page_height - $this->top_margin; 596b6f35a76SGreg Roach } 597b6f35a76SGreg Roach 598b6f35a76SGreg Roach /** 599b6f35a76SGreg Roach * Get the width of a string. 600b6f35a76SGreg Roach * 601b6f35a76SGreg Roach * @param string $text 602b6f35a76SGreg Roach * 603b6f35a76SGreg Roach * @return float 604b6f35a76SGreg Roach */ 605b6f35a76SGreg Roach public function getStringWidth(string $text): float 606b6f35a76SGreg Roach { 607b6f35a76SGreg Roach $style = $this->getStyle($this->currentStyle); 608b6f35a76SGreg Roach 609b6f35a76SGreg Roach return mb_strlen($text) * ($style['size'] / 2); 610b6f35a76SGreg Roach } 611b6f35a76SGreg Roach 612b6f35a76SGreg Roach /** 613b6f35a76SGreg Roach * Get a text height in points - ReportHtml 614b6f35a76SGreg Roach * 615b6f35a76SGreg Roach * @param string $str 616b6f35a76SGreg Roach * 617b6f35a76SGreg Roach * @return float 618b6f35a76SGreg Roach */ 619b6f35a76SGreg Roach public function getTextCellHeight(string $str): float 620b6f35a76SGreg Roach { 621b6f35a76SGreg Roach // Count the number of lines to calculate the height 622b6f35a76SGreg Roach $nl = $this->countLines($str); 623b6f35a76SGreg Roach 624b6f35a76SGreg Roach // Calculate the cell height 625b6f35a76SGreg Roach return ceil(($this->getCurrentStyleHeight() * $this->cellHeightRatio) * $nl); 626b6f35a76SGreg Roach } 627b6f35a76SGreg Roach 628b6f35a76SGreg Roach /** 629b6f35a76SGreg Roach * Get the current X position - ReportHtml 630b6f35a76SGreg Roach * 631b6f35a76SGreg Roach * @return float 632b6f35a76SGreg Roach */ 633b6f35a76SGreg Roach public function getX(): float 634b6f35a76SGreg Roach { 635b6f35a76SGreg Roach return $this->X; 636b6f35a76SGreg Roach } 637b6f35a76SGreg Roach 638b6f35a76SGreg Roach /** 639b6f35a76SGreg Roach * Get the current Y position - ReportHtml 640b6f35a76SGreg Roach * 641b6f35a76SGreg Roach * @return float 642b6f35a76SGreg Roach */ 643b6f35a76SGreg Roach public function getY(): float 644b6f35a76SGreg Roach { 645b6f35a76SGreg Roach return $this->Y; 646b6f35a76SGreg Roach } 647b6f35a76SGreg Roach 648b6f35a76SGreg Roach /** 649b6f35a76SGreg Roach * Get the current page number - ReportHtml 650b6f35a76SGreg Roach * 651b6f35a76SGreg Roach * @return int 652b6f35a76SGreg Roach */ 653b6f35a76SGreg Roach public function pageNo(): int 654b6f35a76SGreg Roach { 655b6f35a76SGreg Roach return $this->pageN; 656b6f35a76SGreg Roach } 657b6f35a76SGreg Roach 658b6f35a76SGreg Roach /** 659b6f35a76SGreg Roach * Set the current style. 660b6f35a76SGreg Roach * 661b6f35a76SGreg Roach * @param string $s 662b6f35a76SGreg Roach * 663b6f35a76SGreg Roach * @void 664b6f35a76SGreg Roach */ 665b6f35a76SGreg Roach public function setCurrentStyle(string $s): void 666b6f35a76SGreg Roach { 667b6f35a76SGreg Roach $this->currentStyle = $s; 668b6f35a76SGreg Roach } 669b6f35a76SGreg Roach 670b6f35a76SGreg Roach /** 671b6f35a76SGreg Roach * Set the X position - ReportHtml 672b6f35a76SGreg Roach * 673b6f35a76SGreg Roach * @param float $x 674b6f35a76SGreg Roach * 675b6f35a76SGreg Roach * @return void 676b6f35a76SGreg Roach */ 67724f2a3afSGreg Roach public function setX(float $x): void 678b6f35a76SGreg Roach { 679b6f35a76SGreg Roach $this->X = $x; 680b6f35a76SGreg Roach } 681b6f35a76SGreg Roach 682b6f35a76SGreg Roach /** 683b6f35a76SGreg Roach * Set the Y position - ReportHtml 684b6f35a76SGreg Roach * 685b6f35a76SGreg Roach * Also updates Max Y position 686b6f35a76SGreg Roach * 687b6f35a76SGreg Roach * @param float $y 688b6f35a76SGreg Roach * 689b6f35a76SGreg Roach * @return void 690b6f35a76SGreg Roach */ 69124f2a3afSGreg Roach public function setY(float $y): void 692b6f35a76SGreg Roach { 693b6f35a76SGreg Roach $this->Y = $y; 694b6f35a76SGreg Roach if ($this->maxY < $y) { 695b6f35a76SGreg Roach $this->maxY = $y; 696b6f35a76SGreg Roach } 697b6f35a76SGreg Roach } 698b6f35a76SGreg Roach 699b6f35a76SGreg Roach /** 700b6f35a76SGreg Roach * Set the X and Y position - ReportHtml 701b6f35a76SGreg Roach * 702b6f35a76SGreg Roach * Also updates Max Y position 703b6f35a76SGreg Roach * 704b6f35a76SGreg Roach * @param float $x 705b6f35a76SGreg Roach * @param float $y 706b6f35a76SGreg Roach * 707b6f35a76SGreg Roach * @return void 708b6f35a76SGreg Roach */ 70924f2a3afSGreg Roach public function setXy(float $x, float $y): void 710b6f35a76SGreg Roach { 711b6f35a76SGreg Roach $this->setX($x); 712b6f35a76SGreg Roach $this->setY($y); 713b6f35a76SGreg Roach } 714b6f35a76SGreg Roach 715b6f35a76SGreg Roach /** 716b6f35a76SGreg Roach * Wrap text - ReportHtml 717b6f35a76SGreg Roach * 718b6f35a76SGreg Roach * @param string $str Text to wrap 719b6f35a76SGreg Roach * @param float $width Width in points the text has to fit into 720b6f35a76SGreg Roach * 721b6f35a76SGreg Roach * @return string 722b6f35a76SGreg Roach */ 723b6f35a76SGreg Roach public function textWrap(string $str, float $width): string 724b6f35a76SGreg Roach { 7255deaecabSGreg Roach $line_width = (int) ($width / ($this->getCurrentStyleHeight() / 2)); 7265deaecabSGreg Roach 727b6f35a76SGreg Roach $lines = explode("\n", $str); 7285deaecabSGreg Roach 7295deaecabSGreg Roach $lines = array_map(fn (string $string): string => self::utf8WordWrap($string, $line_width), $lines); 7305deaecabSGreg Roach 7315deaecabSGreg Roach return implode("\n", $lines); 732b6f35a76SGreg Roach } 733b6f35a76SGreg Roach 7345deaecabSGreg Roach /** 7355deaecabSGreg Roach * Wrap text, similar to the PHP wordwrap() function. 7365deaecabSGreg Roach * 7375deaecabSGreg Roach * @param string $string 7385deaecabSGreg Roach * @param int $width 7395deaecabSGreg Roach * 7405deaecabSGreg Roach * @return string 7415deaecabSGreg Roach */ 7425deaecabSGreg Roach private function utf8WordWrap(string $string, int $width): string 7435deaecabSGreg Roach { 7445deaecabSGreg Roach $out = ''; 7455deaecabSGreg Roach while ($string) { 7465deaecabSGreg Roach if (mb_strlen($string) <= $width) { 7475deaecabSGreg Roach // Do not wrap any text that is less than the output area. 7485deaecabSGreg Roach $out .= $string; 7495deaecabSGreg Roach $string = ''; 7505deaecabSGreg Roach } else { 7515deaecabSGreg Roach $sub1 = mb_substr($string, 0, $width + 1); 7525deaecabSGreg Roach if (mb_substr($string, mb_strlen($sub1) - 1, 1) === ' ') { 7535deaecabSGreg Roach // include words that end by a space immediately after the area. 7545deaecabSGreg Roach $sub = $sub1; 7555deaecabSGreg Roach } else { 7565deaecabSGreg Roach $sub = mb_substr($string, 0, $width); 7575deaecabSGreg Roach } 7585deaecabSGreg Roach $spacepos = strrpos($sub, ' '); 7595deaecabSGreg Roach if ($spacepos === false) { 7605deaecabSGreg Roach // No space on line? 7615deaecabSGreg Roach $out .= $sub . "\n"; 7625deaecabSGreg Roach $string = mb_substr($string, mb_strlen($sub)); 7635deaecabSGreg Roach } else { 7645deaecabSGreg Roach // Split at space; 7655deaecabSGreg Roach $out .= substr($string, 0, $spacepos) . "\n"; 7665deaecabSGreg Roach $string = substr($string, $spacepos + 1); 7675deaecabSGreg Roach } 7685deaecabSGreg Roach } 7695deaecabSGreg Roach } 7705deaecabSGreg Roach 7715deaecabSGreg Roach return $out; 772b6f35a76SGreg Roach } 773b6f35a76SGreg Roach 774b6f35a76SGreg Roach /** 775b6f35a76SGreg Roach * Write text - ReportHtml 776b6f35a76SGreg Roach * 777b6f35a76SGreg Roach * @param string $text Text to print 778b6f35a76SGreg Roach * @param string $color HTML RGB color code (Ex: #001122) 779b6f35a76SGreg Roach * @param bool $useclass 780b6f35a76SGreg Roach * 781b6f35a76SGreg Roach * @return void 782b6f35a76SGreg Roach */ 78324f2a3afSGreg Roach public function write(string $text, string $color = '', bool $useclass = true): void 784b6f35a76SGreg Roach { 785b6f35a76SGreg Roach $style = $this->getStyle($this->getCurrentStyle()); 786b6f35a76SGreg Roach $htmlcode = '<span dir="' . I18N::direction() . '"'; 787b6f35a76SGreg Roach if ($useclass) { 788b6f35a76SGreg Roach $htmlcode .= ' class="' . $style['name'] . '"'; 789b6f35a76SGreg Roach } 790b6f35a76SGreg Roach // Check if Text Color is set and if it’s valid HTML color 791b6f35a76SGreg Roach if (preg_match('/#?(..)(..)(..)/', $color)) { 792b6f35a76SGreg Roach $htmlcode .= ' style="color:' . $color . ';"'; 793b6f35a76SGreg Roach } 794b6f35a76SGreg Roach 795b6f35a76SGreg Roach $htmlcode .= '>' . $text . '</span>'; 796b6f35a76SGreg Roach $htmlcode = str_replace([ 797b6f35a76SGreg Roach "\n", 798b6f35a76SGreg Roach '> ', 799b6f35a76SGreg Roach ' <', 800b6f35a76SGreg Roach ], [ 801b6f35a76SGreg Roach '<br>', 802b6f35a76SGreg Roach '> ', 803b6f35a76SGreg Roach ' <', 804b6f35a76SGreg Roach ], $htmlcode); 805b6f35a76SGreg Roach echo $htmlcode; 806b6f35a76SGreg Roach } 807b6f35a76SGreg Roach} 808