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