1c5c184fdSAndreas Gohr<?php 2c5c184fdSAndreas Gohr 3c5c184fdSAndreas Gohrnamespace dokuwiki\plugin\dw2pdf\src; 4c5c184fdSAndreas Gohr 5bbcdb3feSAndreas Gohruse DOMDocument; 61d334a8cSAndreas Gohruse dokuwiki\ErrorHandler; 7c5c184fdSAndreas Gohruse Mpdf\HTMLParserMode; 8c5c184fdSAndreas Gohruse Mpdf\MpdfException; 9c5c184fdSAndreas Gohr 10c5c184fdSAndreas Gohrclass Writer 11c5c184fdSAndreas Gohr{ 121a52a777SAnna Dabrowska /** @var DokuMpdf Our MPDF instance */ 131a52a777SAnna Dabrowska protected DokuMpdf $mpdf; 141d334a8cSAndreas Gohr 150eefd8f7SAndreas Gohr /** @var Config The configuration */ 160eefd8f7SAndreas Gohr protected Config $config; 170eefd8f7SAndreas Gohr 181d334a8cSAndreas Gohr /** @var Template The template used */ 19c5c184fdSAndreas Gohr protected Template $template; 201d334a8cSAndreas Gohr 21081fc334SAndreas Gohr /** @var Styles The style parser */ 22081fc334SAndreas Gohr protected Styles $styles; 23081fc334SAndreas Gohr 241d334a8cSAndreas Gohr /** @var bool Signal to output a page break before the next output */ 25c5c184fdSAndreas Gohr protected bool $breakBeforeNext = false; 26c5c184fdSAndreas Gohr 271d334a8cSAndreas Gohr /** @var bool Are we debugging? */ 281d334a8cSAndreas Gohr protected bool $debug = false; 291d334a8cSAndreas Gohr 301d334a8cSAndreas Gohr /** @var string Store HTML when debugging */ 311d334a8cSAndreas Gohr protected string $debugHTML = ''; 321d334a8cSAndreas Gohr 33f77f381aSAndreas Gohr /** @var false Is this the first page being written? */ 34f77f381aSAndreas Gohr protected bool $isFirstPage = true; 35f77f381aSAndreas Gohr 36c5c184fdSAndreas Gohr /** 371a52a777SAnna Dabrowska * @param DokuMpdf $mpdf 38c5c184fdSAndreas Gohr * @param Template $template 39f2000117SAndreas Gohr * @param Styles $styles 40c5c184fdSAndreas Gohr */ 411a52a777SAnna Dabrowska public function __construct(DokuMpdf $mpdf, Config $config, Template $template, Styles $styles) 42c5c184fdSAndreas Gohr { 43c5c184fdSAndreas Gohr $this->mpdf = $mpdf; 440eefd8f7SAndreas Gohr $this->config = $config; 45c5c184fdSAndreas Gohr $this->template = $template; 46081fc334SAndreas Gohr $this->styles = $styles; 47f2000117SAndreas Gohr $this->debug = $config->isDebugEnabled(); 48bee95f00SAndreas Gohr 49b08d5470SAndreas Gohr /** 50b08d5470SAndreas Gohr * initialize a new renderer instance (singleton instance will be reused in later p_* calls) 51b08d5470SAndreas Gohr * @var \renderer_plugin_dw2pdf $renderer 52b08d5470SAndreas Gohr */ 53bee95f00SAndreas Gohr $renderer = plugin_load('renderer', 'dw2pdf', true); 54b08d5470SAndreas Gohr $renderer->setConfig($config); 55c5c184fdSAndreas Gohr } 56c5c184fdSAndreas Gohr 57c5c184fdSAndreas Gohr /** 58c5c184fdSAndreas Gohr * Initialize the document 59c5c184fdSAndreas Gohr * 60c5c184fdSAndreas Gohr * @param string $title 61c5c184fdSAndreas Gohr * @return void 62c5c184fdSAndreas Gohr * @throws MpdfException 63c5c184fdSAndreas Gohr */ 64c5c184fdSAndreas Gohr public function startDocument(string $title): void 65c5c184fdSAndreas Gohr { 66c5c184fdSAndreas Gohr $this->mpdf->SetTitle($title); 67c5c184fdSAndreas Gohr 68081fc334SAndreas Gohr // Set the styles 69c5c184fdSAndreas Gohr $styles = '@page landscape-page { size:landscape }'; 70c5c184fdSAndreas Gohr $styles .= 'div.dw2pdf-landscape { page:landscape-page }'; 71c5c184fdSAndreas Gohr $styles .= '@page portrait-page { size:portrait }'; 72c5c184fdSAndreas Gohr $styles .= 'div.dw2pdf-portrait { page:portrait-page }'; 73081fc334SAndreas Gohr $styles .= $this->styles->getCSS(); 741d334a8cSAndreas Gohr $this->write($styles, HTMLParserMode::HEADER_CSS); 75c5c184fdSAndreas Gohr 76c5c184fdSAndreas Gohr //start body html 771d334a8cSAndreas Gohr $this->write('<div class="dokuwiki">', HTMLParserMode::HTML_BODY, true, false); 78c5c184fdSAndreas Gohr } 79c5c184fdSAndreas Gohr 80c5c184fdSAndreas Gohr /** 81c5c184fdSAndreas Gohr * Insert a page break 82c5c184fdSAndreas Gohr * 83c5c184fdSAndreas Gohr * @return void 84c5c184fdSAndreas Gohr * @throws MpdfException 85c5c184fdSAndreas Gohr */ 86c5c184fdSAndreas Gohr public function pageBreak(): void 87c5c184fdSAndreas Gohr { 881d334a8cSAndreas Gohr $this->write('<pagebreak />', 2, false, false); 89c5c184fdSAndreas Gohr } 90c5c184fdSAndreas Gohr 91c5c184fdSAndreas Gohr /** 92c5c184fdSAndreas Gohr * Write a wiki page into the PDF 93c5c184fdSAndreas Gohr * 94c5c184fdSAndreas Gohr * @param string $html The rendered HTML of the wiki page 95c5c184fdSAndreas Gohr * @return void 96c5c184fdSAndreas Gohr * @throws MpdfException 97c5c184fdSAndreas Gohr */ 98c5c184fdSAndreas Gohr public function wikiPage(string $html): void 99c5c184fdSAndreas Gohr { 100c5c184fdSAndreas Gohr $this->conditionalPageBreak(); 101c5c184fdSAndreas Gohr $this->applyHeaderFooters(); 1021d334a8cSAndreas Gohr $this->write($html, HTMLParserMode::HTML_BODY, false, false); 103c5c184fdSAndreas Gohr 104c5c184fdSAndreas Gohr // add citation box if any 105c5c184fdSAndreas Gohr $cite = $this->template->getHTML('citation'); 106c5c184fdSAndreas Gohr if ($cite) { 1071d334a8cSAndreas Gohr $this->write($cite, HTMLParserMode::HTML_BODY, false, false); 108c5c184fdSAndreas Gohr } 109c5c184fdSAndreas Gohr 110c5c184fdSAndreas Gohr $this->breakAfterMe(); 111c5c184fdSAndreas Gohr } 112c5c184fdSAndreas Gohr 113b883aeceSAndreas Gohr /** 114293d84e6SAndreas Gohr * Render and write a wiki page into the PDF 115293d84e6SAndreas Gohr * 116293d84e6SAndreas Gohr * This caches the rendered page individually (unless a specific revision is requested). So even 117293d84e6SAndreas Gohr * when PDF needs to be regenerated, pages that have not changed will be loaded from cache. 118293d84e6SAndreas Gohr * 119293d84e6SAndreas Gohr * @param AbstractCollector $collector The collector providing the page context 120293d84e6SAndreas Gohr * @param string $pageId The page ID to render 121293d84e6SAndreas Gohr * @return void 122293d84e6SAndreas Gohr * @throws MpdfException 123293d84e6SAndreas Gohr */ 124293d84e6SAndreas Gohr public function renderWikiPage(AbstractCollector $collector, string $pageId): void 125293d84e6SAndreas Gohr { 126293d84e6SAndreas Gohr $rev = $collector->getRev(); 127293d84e6SAndreas Gohr $at = $collector->getAt(); 128*6444f9a1SAnna Dabrowska $file = wikiFN($pageId, $rev); 129293d84e6SAndreas Gohr 130293d84e6SAndreas Gohr //ensure $id is in global $ID (needed for parsing) 131293d84e6SAndreas Gohr global $ID; 132293d84e6SAndreas Gohr $keep = $ID; 133293d84e6SAndreas Gohr $ID = $pageId; 134293d84e6SAndreas Gohr 135293d84e6SAndreas Gohr if ($collector->getRev()) { 136293d84e6SAndreas Gohr //no caching on old revisions 1370eefd8f7SAndreas Gohr $html = p_render('dw2pdf', p_get_instructions(io_readWikiPage($file, $pageId, $rev)), $info, $at); 138293d84e6SAndreas Gohr } else { 1390eefd8f7SAndreas Gohr $html = p_cached_output($file, 'dw2pdf', $pageId); 140293d84e6SAndreas Gohr } 141293d84e6SAndreas Gohr 142293d84e6SAndreas Gohr //restore ID (just in case) 143293d84e6SAndreas Gohr $ID = $keep; 144293d84e6SAndreas Gohr 1450eefd8f7SAndreas Gohr // Fix internal links then write the page 1460eefd8f7SAndreas Gohr $html = $this->fixInternalLinks($collector, $html); 1470eefd8f7SAndreas Gohr $this->wikiPage($html); 1480eefd8f7SAndreas Gohr } 1490eefd8f7SAndreas Gohr 1500eefd8f7SAndreas Gohr /** 1510eefd8f7SAndreas Gohr * If the given HTML contains internal links to pages that are part of the exported PDF, 1520eefd8f7SAndreas Gohr * fix the links to point to the correct section within the PDF. 1530eefd8f7SAndreas Gohr * 1540eefd8f7SAndreas Gohr * @param AbstractCollector $collector 1550eefd8f7SAndreas Gohr * @param string $html The rendered HTML of the wiki page 1560eefd8f7SAndreas Gohr * @return string 1570eefd8f7SAndreas Gohr */ 1580eefd8f7SAndreas Gohr protected function fixInternalLinks(AbstractCollector $collector, string $html): string 1590eefd8f7SAndreas Gohr { 160bbcdb3feSAndreas Gohr if ($html === '') return $html; 1610eefd8f7SAndreas Gohr 162bbcdb3feSAndreas Gohr // quick bail out if the page has no internal link markers 163bbcdb3feSAndreas Gohr if (!str_contains($html, 'data-dw2pdf-target')) { 1640eefd8f7SAndreas Gohr return $html; 165293d84e6SAndreas Gohr } 166293d84e6SAndreas Gohr 167bbcdb3feSAndreas Gohr $pages = $collector->getPages(); 168bbcdb3feSAndreas Gohr if ($pages === []) { 169bbcdb3feSAndreas Gohr return $html; 170bbcdb3feSAndreas Gohr } 171bbcdb3feSAndreas Gohr $pages = array_fill_keys($pages, true); 172bbcdb3feSAndreas Gohr 173bbcdb3feSAndreas Gohr $dom = new DOMDocument('1.0', 'UTF-8'); 174bbcdb3feSAndreas Gohr $previous = libxml_use_internal_errors(true); 175bbcdb3feSAndreas Gohr $loaded = $dom->loadHTML( 176bbcdb3feSAndreas Gohr '<?xml encoding="utf-8"?>' . '<div>' . $html . '</div>', 177bbcdb3feSAndreas Gohr LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD 178bbcdb3feSAndreas Gohr ); 179bbcdb3feSAndreas Gohr libxml_clear_errors(); 180bbcdb3feSAndreas Gohr libxml_use_internal_errors($previous); 181bbcdb3feSAndreas Gohr 182bbcdb3feSAndreas Gohr if (!$loaded) { 183bbcdb3feSAndreas Gohr return $html; 184bbcdb3feSAndreas Gohr } 185bbcdb3feSAndreas Gohr 186bbcdb3feSAndreas Gohr $anchors = $dom->getElementsByTagName('a'); 187bbcdb3feSAndreas Gohr if ($anchors->length === 0) { 188bbcdb3feSAndreas Gohr return $html; 189bbcdb3feSAndreas Gohr } 190bbcdb3feSAndreas Gohr 191bbcdb3feSAndreas Gohr $pageAnchors = []; 192bbcdb3feSAndreas Gohr foreach ($anchors as $anchor) { 193bbcdb3feSAndreas Gohr /** @var \DOMElement $anchor */ 194bbcdb3feSAndreas Gohr if (!$anchor->hasAttribute('data-dw2pdf-target')) { 195bbcdb3feSAndreas Gohr continue; 196bbcdb3feSAndreas Gohr } 197bbcdb3feSAndreas Gohr 198bbcdb3feSAndreas Gohr $target = $anchor->getAttribute('data-dw2pdf-target'); 199bbcdb3feSAndreas Gohr if ($target === '' || !isset($pages[$target])) { 200bbcdb3feSAndreas Gohr $anchor->removeAttribute('data-dw2pdf-target'); 201bbcdb3feSAndreas Gohr $anchor->removeAttribute('data-dw2pdf-hash'); 202bbcdb3feSAndreas Gohr continue; 203bbcdb3feSAndreas Gohr } 204bbcdb3feSAndreas Gohr 205bbcdb3feSAndreas Gohr if (!isset($pageAnchors[$target])) { 206bbcdb3feSAndreas Gohr $check = false; 207bbcdb3feSAndreas Gohr $pageAnchors[$target] = sectionID($target, $check); 208bbcdb3feSAndreas Gohr } 209bbcdb3feSAndreas Gohr 210bbcdb3feSAndreas Gohr $hash = $anchor->getAttribute('data-dw2pdf-hash'); 211bbcdb3feSAndreas Gohr $anchor->setAttribute( 212bbcdb3feSAndreas Gohr 'href', 213bbcdb3feSAndreas Gohr '#' . $pageAnchors[$target] . '__' . $hash 214bbcdb3feSAndreas Gohr ); 215bbcdb3feSAndreas Gohr 216bbcdb3feSAndreas Gohr $anchor->removeAttribute('data-dw2pdf-target'); 217bbcdb3feSAndreas Gohr $anchor->removeAttribute('data-dw2pdf-hash'); 218bbcdb3feSAndreas Gohr } 219bbcdb3feSAndreas Gohr 220bbcdb3feSAndreas Gohr $wrapper = $dom->getElementsByTagName('div')->item(0); 221bbcdb3feSAndreas Gohr if (!$wrapper) { 222bbcdb3feSAndreas Gohr return $html; 223bbcdb3feSAndreas Gohr } 224bbcdb3feSAndreas Gohr 225bbcdb3feSAndreas Gohr $result = ''; 226bbcdb3feSAndreas Gohr foreach ($wrapper->childNodes as $node) { 227bbcdb3feSAndreas Gohr $result .= $dom->saveHTML($node); 228bbcdb3feSAndreas Gohr } 229bbcdb3feSAndreas Gohr 230bbcdb3feSAndreas Gohr return $result; 231bbcdb3feSAndreas Gohr } 232bbcdb3feSAndreas Gohr 233293d84e6SAndreas Gohr /** 234b883aeceSAndreas Gohr * Write the Table of Contents 235b883aeceSAndreas Gohr * 236b883aeceSAndreas Gohr * For double-sided documents the ToC is always on an even number of pages, so that the 237b883aeceSAndreas Gohr * following content is on the correct odd/even page. 238b883aeceSAndreas Gohr * The first page of ToC starts always at an odd page, so an additional blank page might 239b883aeceSAndreas Gohr * be included before. 240b883aeceSAndreas Gohr * There is no page numbering at the pages of the ToC. 241b883aeceSAndreas Gohr * 242b883aeceSAndreas Gohr * @param string $header The header text for the ToC (localized)) 243b883aeceSAndreas Gohr * @return void 244b883aeceSAndreas Gohr * @throws MpdfException 245b883aeceSAndreas Gohr */ 246b883aeceSAndreas Gohr public function toc(string $header): void 247c5c184fdSAndreas Gohr { 248b883aeceSAndreas Gohr $this->mpdf->TOCpagebreakByArray([ 249b883aeceSAndreas Gohr 'toc-preHTML' => '<h2>' . $header . '</h2>', 250b883aeceSAndreas Gohr 'toc-bookmarkText' => $header, 251b883aeceSAndreas Gohr 'links' => true, 252b883aeceSAndreas Gohr 'outdent' => '1em', 253b883aeceSAndreas Gohr 'pagenumstyle' => '1' 254b883aeceSAndreas Gohr ]); 255b883aeceSAndreas Gohr 2561d334a8cSAndreas Gohr $this->write('<tocpagebreak>', HTMLParserMode::HTML_BODY, false, false); 257c5c184fdSAndreas Gohr } 258c5c184fdSAndreas Gohr 259c5c184fdSAndreas Gohr /** 260c5c184fdSAndreas Gohr * Insert a cover page 261c5c184fdSAndreas Gohr * 262c5c184fdSAndreas Gohr * Should be called once at the beginning of the PDF generation. Will do nothing if 263c5c184fdSAndreas Gohr * no cover page is configured. 264c5c184fdSAndreas Gohr * 265c5c184fdSAndreas Gohr * @return void 266c5c184fdSAndreas Gohr * @throws MpdfException 267c5c184fdSAndreas Gohr */ 268c5c184fdSAndreas Gohr public function cover(): void 269c5c184fdSAndreas Gohr { 270c5c184fdSAndreas Gohr $html = $this->template->getHTML('cover'); 271c5c184fdSAndreas Gohr if (!$html) return; 272c5c184fdSAndreas Gohr 2737542e5b4SAndreas Gohr $this->conditionalPageBreak(); 2747542e5b4SAndreas Gohr $this->applyHeaderFooters(); 2751d334a8cSAndreas Gohr $this->write($html, HTMLParserMode::HTML_BODY, false, false); 276c5c184fdSAndreas Gohr 277c5c184fdSAndreas Gohr $this->breakAfterMe(); 278c5c184fdSAndreas Gohr } 279c5c184fdSAndreas Gohr 280c5c184fdSAndreas Gohr /** 281c5c184fdSAndreas Gohr * Insert a back page 282c5c184fdSAndreas Gohr * 283c5c184fdSAndreas Gohr * Should be called once at the end of the PDF generation. Will do nothing if 284c5c184fdSAndreas Gohr * no back page is configured. 285c5c184fdSAndreas Gohr * 286c5c184fdSAndreas Gohr * @return void 287c5c184fdSAndreas Gohr * @throws MpdfException 288c5c184fdSAndreas Gohr */ 289c5c184fdSAndreas Gohr public function back(): void 290c5c184fdSAndreas Gohr { 291c5c184fdSAndreas Gohr $html = $this->template->getHTML('back'); 292c5c184fdSAndreas Gohr if (!$html) return; 293c5c184fdSAndreas Gohr 2945eabda9aSAndreas Gohr $this->conditionalPageBreak(); 2951d334a8cSAndreas Gohr $this->write($html, HTMLParserMode::HTML_BODY, false, false); 296c5c184fdSAndreas Gohr } 297c5c184fdSAndreas Gohr 298c5c184fdSAndreas Gohr /** 299c5c184fdSAndreas Gohr * Finalize the document 300c5c184fdSAndreas Gohr * 301c5c184fdSAndreas Gohr * @return void 302c5c184fdSAndreas Gohr * @throws MpdfException 303c5c184fdSAndreas Gohr */ 304c5c184fdSAndreas Gohr public function endDocument(): void 305c5c184fdSAndreas Gohr { 306c5c184fdSAndreas Gohr // adds the closing div and finalizes the document 3071d334a8cSAndreas Gohr $this->write('</div>', HTMLParserMode::HTML_BODY, false, true); 308c5c184fdSAndreas Gohr } 309c5c184fdSAndreas Gohr 310c5c184fdSAndreas Gohr /** 311c5c184fdSAndreas Gohr * Set new headers and footers 312c5c184fdSAndreas Gohr * 313c5c184fdSAndreas Gohr * This will call the appropriate mpdf methods to set headers and footers. It should be called 314c5c184fdSAndreas Gohr * before each wiki page is added to the PDF. 315c5c184fdSAndreas Gohr * 316c5c184fdSAndreas Gohr * On first call on this instance it will set the headers/footers for the first page, afterwards 317c5c184fdSAndreas Gohr * it will use the standard headers/footers. 318c5c184fdSAndreas Gohr * 319c5c184fdSAndreas Gohr * We always set even and odd headers/footers, though they may be identical. 320c5c184fdSAndreas Gohr * @return void 321c5c184fdSAndreas Gohr */ 322c5c184fdSAndreas Gohr protected function applyHeaderFooters(): void 323c5c184fdSAndreas Gohr { 324c5c184fdSAndreas Gohr if ($this->isFirstPage) { 325c5c184fdSAndreas Gohr $header = $this->template->getHTML('header', 'first'); 326c5c184fdSAndreas Gohr $footer = $this->template->getHTML('footer', 'first'); 327c5c184fdSAndreas Gohr 328c5c184fdSAndreas Gohr if ($header) { 329c5c184fdSAndreas Gohr $this->mpdf->SetHTMLHeader($header, 'O'); 330c5c184fdSAndreas Gohr $this->mpdf->SetHTMLHeader($header, 'E'); 331c5c184fdSAndreas Gohr } 332c5c184fdSAndreas Gohr if ($footer) { 333c5c184fdSAndreas Gohr $this->mpdf->SetHTMLFooter($footer, 'O'); 334c5c184fdSAndreas Gohr $this->mpdf->SetHTMLFooter($footer, 'E'); 335c5c184fdSAndreas Gohr } 336c5c184fdSAndreas Gohr $this->isFirstPage = false; 337c5c184fdSAndreas Gohr } else { 338c5c184fdSAndreas Gohr $headerOdd = $this->template->getHTML('header', 'odd'); 339c5c184fdSAndreas Gohr $headerEven = $this->template->getHTML('header', 'even'); 340c5c184fdSAndreas Gohr $footerOdd = $this->template->getHTML('footer', 'odd'); 341c5c184fdSAndreas Gohr $footerEven = $this->template->getHTML('footer', 'even'); 342c5c184fdSAndreas Gohr 343c5c184fdSAndreas Gohr if ($headerOdd) { 344c5c184fdSAndreas Gohr $this->mpdf->SetHTMLHeader($headerOdd, 'O'); 345c5c184fdSAndreas Gohr } 346c5c184fdSAndreas Gohr if ($headerEven) { 347c5c184fdSAndreas Gohr $this->mpdf->SetHTMLHeader($headerEven, 'E'); 348c5c184fdSAndreas Gohr } 349c5c184fdSAndreas Gohr if ($footerOdd) { 350c5c184fdSAndreas Gohr $this->mpdf->SetHTMLFooter($footerOdd, 'O'); 351c5c184fdSAndreas Gohr } 352c5c184fdSAndreas Gohr if ($footerEven) { 353c5c184fdSAndreas Gohr $this->mpdf->SetHTMLFooter($footerEven, 'E'); 354c5c184fdSAndreas Gohr } 355c5c184fdSAndreas Gohr } 356c5c184fdSAndreas Gohr } 357c5c184fdSAndreas Gohr 358c5c184fdSAndreas Gohr /** 359c5c184fdSAndreas Gohr * Insert a page break if there was previous content 360c5c184fdSAndreas Gohr * 361c5c184fdSAndreas Gohr * @return void 3621d334a8cSAndreas Gohr * @throws MpdfException 363c5c184fdSAndreas Gohr */ 364c5c184fdSAndreas Gohr protected function conditionalPageBreak(): void 365c5c184fdSAndreas Gohr { 366c5c184fdSAndreas Gohr if ($this->breakBeforeNext) { 367c5c184fdSAndreas Gohr $this->pageBreak(); 368c5c184fdSAndreas Gohr $this->breakBeforeNext = false; 369c5c184fdSAndreas Gohr } 370c5c184fdSAndreas Gohr } 371c5c184fdSAndreas Gohr 372c5c184fdSAndreas Gohr /** 373c5c184fdSAndreas Gohr * Signal that a page break should be inserted before the next content 374c5c184fdSAndreas Gohr * 375c5c184fdSAndreas Gohr * @return void 376c5c184fdSAndreas Gohr */ 377c5c184fdSAndreas Gohr protected function breakAfterMe(): void 378c5c184fdSAndreas Gohr { 379c5c184fdSAndreas Gohr $this->breakBeforeNext = true; 380c5c184fdSAndreas Gohr } 3811d334a8cSAndreas Gohr 3821d334a8cSAndreas Gohr /** 3831d334a8cSAndreas Gohr * Return the debug HTML collected so far 3841d334a8cSAndreas Gohr * 3851d334a8cSAndreas Gohr * Will return an empty string if debugging is not enabled. 3861d334a8cSAndreas Gohr * 3871d334a8cSAndreas Gohr * @return string The collected debug HTML 3881d334a8cSAndreas Gohr */ 3891d334a8cSAndreas Gohr public function getDebugHTML(): string 3901d334a8cSAndreas Gohr { 3911d334a8cSAndreas Gohr return $this->debugHTML; 3921d334a8cSAndreas Gohr } 3931d334a8cSAndreas Gohr 3941d334a8cSAndreas Gohr /** 395f2000117SAndreas Gohr * Persist the generated PDF to the provided destination file. 396f2000117SAndreas Gohr * 397f2000117SAndreas Gohr * @param string $cacheFile Absolute file path that should receive the PDF output 398f2000117SAndreas Gohr * @return void 399f2000117SAndreas Gohr * @throws MpdfException 400f2000117SAndreas Gohr */ 401f2000117SAndreas Gohr public function outputToFile(string $cacheFile): void 402f2000117SAndreas Gohr { 403f2000117SAndreas Gohr $this->mpdf->Output($cacheFile, 'F'); 404f2000117SAndreas Gohr } 405f2000117SAndreas Gohr 406f2000117SAndreas Gohr /** 4071d334a8cSAndreas Gohr * A wrapper around MPDF::WriteHTML 4081d334a8cSAndreas Gohr * 4091d334a8cSAndreas Gohr * When debugging is enabled, the output is written to a debug buffer instead of the PDF. 4101d334a8cSAndreas Gohr * 4111d334a8cSAndreas Gohr * @param string $html The HTML code to write 4121d334a8cSAndreas Gohr * @param int $mode Use HTMLParserMode constants. Controls what parts of the $html code is parsed. 4131d334a8cSAndreas Gohr * @param bool $init Clears and sets buffers to Top level block etc. 4141d334a8cSAndreas Gohr * @param bool $close If false leaves buffers etc. in current state, so that it can continue a block etc. 4151d334a8cSAndreas Gohr * @throws MpdfException 4161d334a8cSAndreas Gohr */ 4171d334a8cSAndreas Gohr protected function write( 4181d334a8cSAndreas Gohr string $html, 4191d334a8cSAndreas Gohr int $mode = HTMLParserMode::DEFAULT_MODE, 4201d334a8cSAndreas Gohr bool $init = true, 4211d334a8cSAndreas Gohr bool $close = true 4225340eaffSAndreas Gohr ) { 4231d334a8cSAndreas Gohr if (!$this->debug) { 4241d334a8cSAndreas Gohr try { 4251d334a8cSAndreas Gohr $this->mpdf->WriteHTML($html, $mode, $init, $close); 4261d334a8cSAndreas Gohr } catch (MpdfException $e) { 4271d334a8cSAndreas Gohr ErrorHandler::logException($e); // ensure the issue is logged 4281d334a8cSAndreas Gohr throw $e; 4291d334a8cSAndreas Gohr } 4301d334a8cSAndreas Gohr return; 4311d334a8cSAndreas Gohr } 4321d334a8cSAndreas Gohr 4331d334a8cSAndreas Gohr // when debugging, just store the HTML 4341d334a8cSAndreas Gohr if ($mode === HTMLParserMode::HEADER_CSS) { 4351d334a8cSAndreas Gohr $this->debugHTML .= "\n<style>\n" . $html . "\n</style>\n"; 4361d334a8cSAndreas Gohr } else { 4371d334a8cSAndreas Gohr $this->debugHTML .= "\n" . $html . "\n"; 4381d334a8cSAndreas Gohr } 4391d334a8cSAndreas Gohr } 440c5c184fdSAndreas Gohr} 441