xref: /webtrees/app/Report/AbstractRenderer.php (revision 5bfc689774bb9a6401271c4ed15a6d50652c991b)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2022 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