1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2021 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Report; 21 22use Fisharebest\Webtrees\I18N; 23use Fisharebest\Webtrees\MediaFile; 24use Fisharebest\Webtrees\Webtrees; 25use League\Flysystem\FilesystemOperator; 26 27/** 28 * Class AbstractRenderer - base for PDF and HTML reports 29 */ 30abstract class AbstractRenderer 31{ 32 // Reports layouts are measured in points. 33 protected const UNITS = 'pt'; 34 35 // A point is 1/72 of an inch 36 protected const INCH_TO_POINTS = 72.0; 37 protected const MM_TO_POINTS = 72.0 / 25.4; 38 39 protected const PAPER_SIZES = [ 40 // ISO 216 41 'A0' => [841.0 * self::MM_TO_POINTS, 1189.0 * self::MM_TO_POINTS], 42 'A1' => [594.0 * self::MM_TO_POINTS, 841.0 * self::MM_TO_POINTS], 43 'A2' => [420.0 * self::MM_TO_POINTS, 594.0 * self::MM_TO_POINTS], 44 'A3' => [297.0 * self::MM_TO_POINTS, 420.0 * self::MM_TO_POINTS], 45 'A4' => [210.0 * self::MM_TO_POINTS, 297.0 * self::MM_TO_POINTS], 46 // US 47 'US-Letter' => [8.5 * self::INCH_TO_POINTS, 11.0 * self::INCH_TO_POINTS], 48 'US-Legal' => [8.5 * self::INCH_TO_POINTS, 14.0 * self::INCH_TO_POINTS], 49 'US-Tabloid' => [11.0 * self::INCH_TO_POINTS, 17.0 * self::INCH_TO_POINTS], 50 ]; 51 52 public float $left_margin = 18.0 * self::MM_TO_POINTS; 53 54 public float $right_margin = 9.9 * self::MM_TO_POINTS; 55 56 public float $top_margin = 26.8 * self::MM_TO_POINTS; 57 58 public float $bottom_margin = 21.6 * self::MM_TO_POINTS; 59 60 public float $header_margin = 4.9 * self::MM_TO_POINTS; 61 62 public float $footer_margin = 9.9 * self::MM_TO_POINTS; 63 64 /** @var string Page orientation (portrait, landscape) */ 65 public string $orientation = 'portrait'; 66 67 /** @var string Page format name */ 68 public string $page_format = 'A4'; 69 70 /** @var float Height of page format in points */ 71 public float $page_height = 0.0; 72 73 /** @var float Width of page format in points */ 74 public float $page_width = 0.0; 75 76 /** @var array<array<string,string>> An array of the Styles elements found in the document */ 77 public array $styles = []; 78 79 /** @var string The default Report font name */ 80 public string $default_font = 'dejavusans'; 81 82 /** @var float The default Report font size */ 83 public float $default_font_size = 12.0; 84 85 /** @var string Header (H), Body (B) or Footer (F) */ 86 public string $processing = 'H'; 87 88 /** @var bool RTL Language (false=LTR, true=RTL) */ 89 public bool $rtl = false; 90 91 /** @var bool Show the Generated by... (true=show the text) */ 92 public bool $show_generated_by = true; 93 94 /** @var string Generated By... text */ 95 public string $generated_by = ''; 96 97 /** @var string The report title */ 98 public string $title = ''; 99 100 /** @var string Author of the report, the users full name */ 101 public string $rauthor = Webtrees::NAME . ' ' . Webtrees::VERSION; 102 103 /** @var string Keywords */ 104 public string $rkeywords = ''; 105 106 /** @var string Report Description / Subject */ 107 public string $rsubject = ''; 108 109 /** @var array<ReportBaseElement|string> */ 110 public array $headerElements = []; 111 112 /** @var array<ReportBaseElement|string> */ 113 public array $footerElements = []; 114 115 /** @var array<ReportBaseElement|string> */ 116 public array $bodyElements = []; 117 118 public string $currentStyle = ''; 119 120 /** 121 * Clear the Header. 122 * 123 * @return void 124 */ 125 abstract public function clearHeader(): void; 126 127 128 /** 129 * @param ReportBaseElement|string $element 130 * 131 * @return void 132 */ 133 public function addElement($element): void 134 { 135 if ($this->processing === 'B') { 136 $this->addElementToBody($element); 137 } elseif ($this->processing === 'H') { 138 $this->addElementToHeader($element); 139 } elseif ($this->processing === 'F') { 140 $this->addElementToFooter($element); 141 } 142 } 143 144 /** 145 * @param ReportBaseElement|string $element 146 * 147 * @return void 148 */ 149 public function addElementToHeader($element): void 150 { 151 $this->headerElements[] = $element; 152 } 153 154 /** 155 * @param ReportBaseElement|string $element 156 * 157 * @return void 158 */ 159 public function addElementToBody($element): void 160 { 161 $this->bodyElements[] = $element; 162 } 163 164 /** 165 * @param ReportBaseElement|string $element 166 * 167 * @return void 168 */ 169 public function addElementToFooter($element): void 170 { 171 $this->footerElements[] = $element; 172 } 173 174 /** 175 * Run the report. 176 * 177 * @return void 178 */ 179 abstract public function run(): void; 180 181 /** 182 * Create a new Cell object. 183 * 184 * @param float $width cell width (expressed in points) 185 * @param float $height cell height (expressed in points) 186 * @param string $border Border style 187 * @param string $align Text alignement 188 * @param string $bgcolor Background color code 189 * @param string $style The name of the text style 190 * @param int $ln Indicates where the current position should go after the call 191 * @param mixed $top Y-position 192 * @param mixed $left X-position 193 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 1 194 * @param int $stretch Stretch carachter mode 195 * @param string $bocolor Border color 196 * @param string $tcolor Text color 197 * @param bool $reseth 198 * 199 * @return ReportBaseCell 200 */ 201 abstract public function createCell( 202 float $width, 203 float $height, 204 string $border, 205 string $align, 206 string $bgcolor, 207 string $style, 208 int $ln, 209 $top, 210 $left, 211 int $fill, 212 int $stretch, 213 string $bocolor, 214 string $tcolor, 215 bool $reseth 216 ): ReportBaseCell; 217 218 /** 219 * Create a new TextBox object. 220 * 221 * @param float $width Text box width 222 * @param float $height Text box height 223 * @param bool $border 224 * @param string $bgcolor Background color code in HTML 225 * @param bool $newline 226 * @param float $left 227 * @param float $top 228 * @param bool $pagecheck 229 * @param string $style 230 * @param bool $fill 231 * @param bool $padding 232 * @param bool $reseth 233 * 234 * @return ReportBaseTextbox 235 */ 236 abstract public function createTextBox( 237 float $width, 238 float $height, 239 bool $border, 240 string $bgcolor, 241 bool $newline, 242 float $left, 243 float $top, 244 bool $pagecheck, 245 string $style, 246 bool $fill, 247 bool $padding, 248 bool $reseth 249 ): ReportBaseTextbox; 250 251 /** 252 * Create a text element. 253 * 254 * @param string $style 255 * @param string $color 256 * 257 * @return ReportBaseText 258 */ 259 abstract public function createText(string $style, string $color): ReportBaseText; 260 261 /** 262 * Create a line. 263 * 264 * @param float $x1 265 * @param float $y1 266 * @param float $x2 267 * @param float $y2 268 * 269 * @return ReportBaseLine 270 */ 271 abstract public function createLine(float $x1, float $y1, float $x2, float $y2): ReportBaseLine; 272 273 /** 274 * Create a new image object. 275 * 276 * @param string $file Filename 277 * @param float $x 278 * @param float $y 279 * @param float $w Image width 280 * @param float $h Image height 281 * @param string $align L:left, C:center, R:right or empty to use x/y 282 * @param string $ln T:same line, N:next line 283 * 284 * @return ReportBaseImage 285 */ 286 abstract public function createImage(string $file, float $x, float $y, float $w, float $h, string $align, string $ln): ReportBaseImage; 287 288 /** 289 * Create a new image object from Media Object. 290 * 291 * @param MediaFile $media_file 292 * @param float $x 293 * @param float $y 294 * @param float $w Image width 295 * @param float $h Image height 296 * @param string $align L:left, C:center, R:right or empty to use x/y 297 * @param string $ln T:same line, N:next line 298 * @param FilesystemOperator $data_filesystem 299 * 300 * @return ReportBaseImage 301 */ 302 abstract public function createImageFromObject( 303 MediaFile $media_file, 304 float $x, 305 float $y, 306 float $w, 307 float $h, 308 string $align, 309 string $ln, 310 FilesystemOperator $data_filesystem 311 ): ReportBaseImage; 312 313 /** 314 * Create a new Footnote object. 315 * 316 * @param string $style Style name 317 * 318 * @return ReportBaseFootnote 319 */ 320 abstract public function createFootnote(string $style): ReportBaseFootnote; 321 322 /** 323 * Initial Setup 324 * Setting up document wide defaults that will be inherited of the report modules 325 * As DEFAULT A4 and Portrait will be used if not set 326 * 327 * @return void 328 */ 329 public function setup(): void 330 { 331 $this->rtl = I18N::direction() === 'rtl'; 332 333 $this->rkeywords = ''; 334 335 // I18N: This is a report footer. %s is the name of the application. 336 $this->generated_by = I18N::translate('Generated by %s', Webtrees::NAME . ' ' . Webtrees::VERSION); 337 338 // Paper size - defaults to A4 if the report fails to define a size. 339 [$this->page_width, $this->page_height] = self::PAPER_SIZES[$this->page_format] ?? self::PAPER_SIZES['A4']; 340 } 341 342 /** 343 * Process the Header, Body or Footer 344 * 345 * @param string $p Header (H), Body (B) or Footer (F) 346 * 347 * @return void 348 */ 349 public function setProcessing(string $p): void 350 { 351 $this->processing = $p; 352 } 353 354 /** 355 * Add the Title when raw character data is used in Title 356 * 357 * @param string $data 358 * 359 * @return void 360 */ 361 public function addTitle(string $data): void 362 { 363 $this->title .= $data; 364 } 365 366 /** 367 * Add the Description when raw character data is used in Description 368 * 369 * @param string $data 370 * 371 * @return void 372 */ 373 public function addDescription(string $data): void 374 { 375 $this->rsubject .= $data; 376 } 377 378 /** 379 * Add Style to Styles array 380 * 381 * @param array<string> $style 382 * 383 * @return void 384 */ 385 public function addStyle(array $style): void 386 { 387 $this->styles[$style['name']] = $style; 388 } 389 390 /** 391 * Get a style from the Styles array 392 * 393 * @param string $s Style name 394 * 395 * @return array<string> 396 */ 397 public function getStyle(string $s): array 398 { 399 return $this->styles[$s]; 400 } 401} 402