xref: /plugin/dw2pdf/src/Writer.php (revision c5c184fdaba658e458c406a092988e58f765c81a)
1<?php
2
3namespace dokuwiki\plugin\dw2pdf\src;
4
5use Mpdf\HTMLParserMode;
6use Mpdf\MpdfException;
7
8/**
9 * @todo handle actual writing in a separate method to allow for centralized error handling and debug writing to HTML
10 */
11class Writer
12{
13
14    protected DokuPdf $mpdf;
15    protected Template $template;
16    protected bool $breakBeforeNext = false;
17
18    /**
19     * @param DokuPdf $mpdf
20     * @param Template $template
21     */
22    public function __construct(DokuPdf $mpdf, Template $template)
23    {
24        $this->mpdf = $mpdf;
25        $this->template = $template;
26    }
27
28    /**
29     * Initialize the document
30     *
31     * @param string $title
32     * @return void
33     * @throws MpdfException
34     */
35    public function startDocument(string $title): void
36    {
37        $this->mpdf->SetTitle($title);
38
39        // Set the styles FIXME to be moved into Styles class
40        $styles = '@page landscape-page { size:landscape }';
41        $styles .= 'div.dw2pdf-landscape { page:landscape-page }';
42        $styles .= '@page portrait-page { size:portrait }';
43        $styles .= 'div.dw2pdf-portrait { page:portrait-page }';
44        // FIXME$styles .= $this->loadCSS();
45        $this->mpdf->WriteHTML($styles, HTMLParserMode::HEADER_CSS);
46
47        //start body html
48        $this->mpdf->WriteHTML('<div class="dokuwiki">', HTMLParserMode::HTML_BODY, true, false);
49    }
50
51    /**
52     * Insert a page break
53     *
54     * @return void
55     * @throws MpdfException
56     */
57    public function pageBreak(): void
58    {
59        $this->mpdf->WriteHTML('<pagebreak />', 2, false, false);
60    }
61
62    /**
63     * Write a wiki page into the PDF
64     *
65     * @param string $html The rendered HTML of the wiki page
66     * @return void
67     * @throws MpdfException
68     */
69    public function wikiPage(string $html): void
70    {
71        $this->conditionalPageBreak();
72
73        $this->applyHeaderFooters();
74
75        $this->mpdf->WriteHTML($html, HTMLParserMode::HTML_BODY, false, false);
76
77        // add citation box if any
78        $cite = $this->template->getHTML('citation');
79        if ($cite) {
80            $this->mpdf->WriteHTML($cite, HTMLParserMode::HTML_BODY, false, false);
81        }
82
83        $this->breakAfterMe();
84    }
85
86    public function toc(): void
87    {
88        $this->conditionalPageBreak();
89        // FIXME
90        $this->breakAfterMe();
91    }
92
93    /**
94     * Insert a cover page
95     *
96     * Should be called once at the beginning of the PDF generation. Will do nothing if
97     * no cover page is configured.
98     *
99     * @return void
100     * @throws MpdfException
101     */
102    public function cover(): void
103    {
104        $this->conditionalPageBreak();
105
106        $html = $this->template->getHTML('cover');
107        if (!$html) return;
108
109        $this->mpdf->WriteHTML($html, HTMLParserMode::HTML_BODY, false, false);
110
111        $this->breakAfterMe();
112    }
113
114    /**
115     * Insert a back page
116     *
117     * Should be called once at the end of the PDF generation. Will do nothing if
118     * no back page is configured.
119     *
120     * @return void
121     * @throws MpdfException
122     */
123    public function back(): void
124    {
125        $this->conditionalPageBreak();
126
127        $html = $this->template->getHTML('back');
128        if (!$html) return;
129
130        $this->mpdf->WriteHTML($html, HTMLParserMode::HTML_BODY, false, false);
131    }
132
133    /**
134     * Finalize the document
135     *
136     * @return void
137     * @throws MpdfException
138     */
139    public function endDocument(): void
140    {
141        // adds the closing div and finalizes the document
142        $this->mpdf->WriteHTML('</div>', HTMLParserMode::HTML_BODY, false, true);
143    }
144
145    /**
146     * Set new headers and footers
147     *
148     * This will call the appropriate mpdf methods to set headers and footers. It should be called
149     * before each wiki page is added to the PDF.
150     *
151     * On first call on this instance it will set the headers/footers for the first page, afterwards
152     * it will use the standard headers/footers.
153     *
154     * We always set even and odd headers/footers, though they may be identical.
155     * @return void
156     */
157    protected function applyHeaderFooters(): void
158    {
159        if ($this->isFirstPage) {
160            $header = $this->template->getHTML('header', 'first');
161            $footer = $this->template->getHTML('footer', 'first');
162
163            if ($header) {
164                $this->mpdf->SetHTMLHeader($header, 'O');
165                $this->mpdf->SetHTMLHeader($header, 'E');
166            }
167            if ($footer) {
168                $this->mpdf->SetHTMLFooter($footer, 'O');
169                $this->mpdf->SetHTMLFooter($footer, 'E');
170            }
171            $this->isFirstPage = false;
172        } else {
173            $headerOdd = $this->template->getHTML('header', 'odd');
174            $headerEven = $this->template->getHTML('header', 'even');
175            $footerOdd = $this->template->getHTML('footer', 'odd');
176            $footerEven = $this->template->getHTML('footer', 'even');
177
178            if ($headerOdd) {
179                $this->mpdf->SetHTMLHeader($headerOdd, 'O');
180            }
181            if ($headerEven) {
182                $this->mpdf->SetHTMLHeader($headerEven, 'E');
183            }
184            if ($footerOdd) {
185                $this->mpdf->SetHTMLFooter($footerOdd, 'O');
186            }
187            if ($footerEven) {
188                $this->mpdf->SetHTMLFooter($footerEven, 'E');
189            }
190        }
191    }
192
193    /**
194     * Insert a page break if there was previous content
195     *
196     * @return void
197     */
198    protected function conditionalPageBreak(): void
199    {
200        if ($this->breakBeforeNext) {
201            $this->pageBreak();
202            $this->breakBeforeNext = false;
203        }
204    }
205
206    /**
207     * Signal that a page break should be inserted before the next content
208     *
209     * @return void
210     */
211    protected function breakAfterMe(): void
212    {
213        $this->breakBeforeNext = true;
214    }
215}
216