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