[TikiWiki-commits] [Git][tikiwiki/tiki][master] [ENH] mpdf: Add debug output pref which saves the html sent to mPDF
Benoit Grégoire (@benoitg) via TikiWiki-cvs <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <695fc18f1fd01_2c1805c45350@gitlab-sidekiq-low-urgency-cpu-bound-v2-7bf775f96c-4jnhl.mail> |
Benoit Grégoire pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
53caad51 by Jonny Bradley at 2026-01-08T14:30:56+00:00
[ENH] mpdf: Add debug output pref which saves the html sent to mPDF
---
* [FIX] mpdf: Add a link to the debug output and make the warning clearer
* [FIX] phpcs: Indentation fail #2
* [FIX] phpcs: Indentation fail
* [FIX] mpdf: Don't add empty (whitespace) values to the css, and include the actual data for inline `url(data:...)` to avoid invalid css being sent to mPDF (also add a missing semicolon on the `opacity:0` replacement)
* [FIX] mpdf: Don't add an unnecessary and incorrect `div.row` if not adding modules in `cols` within it
* [ENH] mpdf: Add debug output pref which saves the html sent to mPDF in an html file (with a warning that this html file does not obey tiki permissions)
See merge request tikiwiki/tiki!9320
- - - - -
3 changed files:
- lib/pdflib.php
- lib/prefs/print.php
- templates/admin/include_print.tpl
Changes:
=====================================
lib/pdflib.php
=====================================
@@ -530,13 +530,17 @@ class PdfGenerator
$cssStyles = str_replace(
[".tiki","opacity: 0;","page-break-inside: avoid;"],
- ["","fill: #fff;opacity:0.3;stroke:black","page-break-inside: auto;"],
+ ["","fill: #fff;opacity:0.3;stroke:black;","page-break-inside: auto;"],
$allCss
);
//PDF import templates will not work if background color is set, need to replace in css
$cssStyles = $this->replaceCssVariables($cssStyles);
$cssStyles = $this->evaluateCalcExpressions($cssStyles);
+ if ($prefs['print_pdf_mpdf_debug'] === 'y') {
+ $debugOutput = '';
+ }
+
if (
array_filter(array_column($pdfPages, 'pageContent'), function ($var) {
return preg_match("/\bpdfinclude\b/i", $var);
@@ -579,14 +583,28 @@ class PdfGenerator
return date($matches[1]);
}, $coverPage[$i]);
}
- $htm = '<body style="' . $coverPageBgColor . 'margin:0px;padding:0px"><div style="height:100%;background-image:url(' . $coverImage . ');background-size:cover;background-repeat: no-repeat;background-position: center;padding:20px;">';
- if (! empty($coverPage[0]) || ! empty($coverPage[1])) {
- $htm .= '<div style="' . $coverPageBorder . 'height:95%;">
- <div style="text-align:' . $textAlign . ';margin-top:30%;' . $textColor . '">
- <div style="' . $textBgColor . $coverPageTextBorder . 'margin-bottom:10px;font-size:50px;">' . $coverPage[0] . '</div>' . $coverPage[1] . '</div></div>';
+ $bodyStyle = "{$coverPageBgColor}margin:0px;padding:0px";
+ $containerStyle = "height:100%;background-image:url({$coverImage});padding:20px;background-repeat:no-repeat;background-position:center;";
+ $borderStyle = "{$coverPageBorder}height:95%;";
+ $contentStyle = "text-align:{$textAlign};margin-top:30%;{$textColor}";
+ $titleStyle = "{$textBgColor}{$coverPageTextBorder}margin-bottom:10px;font-size:50px;";
+
+ $htmlChunk = <<<HTML
+ <body style="$bodyStyle">
+ <div style="$containerStyle">
+ <div style="$borderStyle">
+ <div style="$contentStyle">
+ <div style="$titleStyle">{$coverPage[0]}</div>
+ {$coverPage[1]}
+ </div>
+ </div>
+ </div>
+ </body>
+ HTML;
+ if ($prefs['print_pdf_mpdf_debug'] === 'y') {
+ $debugOutput .= $htmlChunk;
}
- $htm .= '</div></body>';
- $mpdf->WriteHTML($htm);
+ $mpdf->WriteHTML($htmlChunk);
}
//Checking bookmark
if (is_array($pdfSettings['autobookmarks'])) {
@@ -677,7 +695,11 @@ class PdfGenerator
}
}
} catch (Exception $e) {
- $mpdf->WriteHTML("PDF not supported");
+ $htmlChunk = "PDF not supported";
+ if ($prefs['print_pdf_mpdf_debug'] === 'y') {
+ $debugOutput .= $htmlChunk;
+ }
+ $mpdf->WriteHTML($htmlChunk);
}
}
unlink($tmpExtPDF);
@@ -727,16 +749,27 @@ class PdfGenerator
$pdfPage['pageContent'] = $this->getHtmlLayout($pdfPage['pageContent']);
$this->_getImages($pdfPage['pageContent'], $tempImgArr);
- $mpdf->WriteHTML('<html><body class="' . $bodycss . '" style="margin:0px;padding:0px;">' . $cssStyles);
+ $htmlChunk = '<html><body class="' . $bodycss . '" style="margin:0px;padding:0px;">' . $cssStyles;
+ if ($prefs['print_pdf_mpdf_debug'] === 'y') {
+ $debugOutput .= $htmlChunk;
+ }
+ $mpdf->WriteHTML($htmlChunk);
$pagesTotal += floor(strlen($pdfPage['pageContent']) / 3000);
//checking if page content is less than mPDF character limit, otherwise split it and loop to writeHTML
for ($charLimit = 0; $charLimit <= strlen($pdfPage['pageContent']); $charLimit += $pdfLimit) {
$content_slice = substr($pdfPage['pageContent'], $charLimit, $pdfLimit);
if ($content_slice) {
+ if ($prefs['print_pdf_mpdf_debug'] === 'y') {
+ $debugOutput .= $content_slice;
+ }
$mpdf->WriteHTML($content_slice);
}
}
- $mpdf->WriteHTML('</body></html>');
+ $htmlChunk = '</body></html>';
+ if ($prefs['print_pdf_mpdf_debug'] === 'y') {
+ $debugOutput .= $htmlChunk;
+ }
+ $mpdf->WriteHTML($htmlChunk);
$pageNo++;
$cssStyles = ''; //set to blank after added with first page
}
@@ -755,6 +788,11 @@ class PdfGenerator
$this->clearTempImg($tempImgArr);
$tempFile = fopen("temp/public/pdffile_" . session_id() . ".txt", "w");
fwrite($tempFile, ($pagesTotal * 30));
+
+ if ($prefs['print_pdf_mpdf_debug'] === 'y') {
+ file_put_contents('temp/mpdf_debug_output.html', $debugOutput);
+ }
+
return $mpdf->Output('', 'S'); // Return as a string
}
@@ -864,45 +902,50 @@ class PdfGenerator
$htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="col-xs-12">' . $modules_to_print_contents['topbar_modules'] . '</div>';
}
- $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="row">';
+ if (array_filter($modules_to_print_contents)) {
+ $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="row">';
- if ($modules_to_print_contents['left_modules'] || $modules_to_print_contents['right_modules']) {
- $sideColumn = 'col-xs-4';
+ if ($modules_to_print_contents['left_modules'] || $modules_to_print_contents['right_modules']) {
+ $sideColumn = 'col-xs-4';
- if ($modules_to_print_contents['left_modules'] && $modules_to_print_contents['right_modules']) {
- $sideColumn = 'col-xs-2';
- }
+ if ($modules_to_print_contents['left_modules'] && $modules_to_print_contents['right_modules']) {
+ $sideColumn = 'col-xs-2';
+ }
- if ($modules_to_print_contents['left_modules']) {
- $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="' . $sideColumn . '">' . $modules_to_print_contents['left_modules'] . '</div>';
- }
+ if ($modules_to_print_contents['left_modules']) {
+ $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="' . $sideColumn . '">'
+ . $modules_to_print_contents['left_modules'] . '</div>';
+ }
- $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="col-xs-8">';
+ $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="col-xs-8">';
- if ($modules_to_print_contents['pagetop_modules']) {
- $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div>' . $modules_to_print_contents['pagetop_modules'] . '</div>';
- }
+ if ($modules_to_print_contents['pagetop_modules']) {
+ $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div>' . $modules_to_print_contents['pagetop_modules'] . '</div>';
+ }
- if ($modules_to_print_contents['pagebottom_modules']) {
- $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '<div>' . $modules_to_print_contents['pagebottom_modules'] . '</div>';
- }
+ if ($modules_to_print_contents['pagebottom_modules']) {
+ $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '<div>' . $modules_to_print_contents['pagebottom_modules'] . '</div>';
+ }
- $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '</div>';
+ $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '</div>';
- if ($modules_to_print_contents['right_modules']) {
- $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '<div class="' . $sideColumn . '">' . $modules_to_print_contents['right_modules'] . '</div>';
- }
- } else {
- if ($modules_to_print_contents['pagetop_modules']) {
- $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="col-xs-12">' . $modules_to_print_contents['pagetop_modules'] . '</div>';
- }
- if ($modules_to_print_contents['pagebottom_modules']) {
- $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '<div class="col-xs-12">' . $modules_to_print_contents['pagebottom_modules'] . '</div>';
+ if ($modules_to_print_contents['right_modules']) {
+ $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '<div class="' . $sideColumn . '">'
+ . $modules_to_print_contents['right_modules'] . '</div>';
+ }
+ } else {
+ if ($modules_to_print_contents['pagetop_modules']) {
+ $htmlLayout["staringPart"] = $htmlLayout["staringPart"] . '<div class="col-xs-12">'
+ . $modules_to_print_contents['pagetop_modules'] . '</div>';
+ }
+ if ($modules_to_print_contents['pagebottom_modules']) {
+ $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '<div class="col-xs-12">'
+ . $modules_to_print_contents['pagebottom_modules'] . '</div>';
+ }
}
- }
-
- $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '</div>';
+ $htmlLayout["endingPart"] = $htmlLayout["endingPart"] . '</div>';
+ }
//check if Module contains navbar and force display (when printing nav is by default display none)
if (str_contains($htmlLayout["staringPart"], '<nav') || str_contains($htmlLayout["endingPart"], '<nav')) {
@@ -1681,16 +1724,19 @@ TEXT;
private function runReplaceVars($css, $variables)
{
+ // FIXME this pattern doesn't match multi-argument var calls
+ // e.g. "var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)))"
+ // from vendor_bundled/vendor/twbs/bootstrap/dist/css/bootstrap.css:1884
+
return preg_replace_callback('/var\(--([a-zA-Z0-9-]+)(?:,\s*(.+?))?\)/', function ($matches) use ($variables) {
$var_name = $matches[1];
$fallback = $matches[2] ?? null; // Optional fallback value
// Check if the variable exists
- if (isset($variables[$var_name])) {
- $value = $variables[$var_name];
-
+ $value = trim($variables[$var_name]);
+ if ($value) {
// If the variable contains another `var()`, recursively resolve it
- if (preg_match('/var\(--([a-zA-Z0-9-]+)\)/', $value)) {
+ if (preg_match('/var\(--([a-zA-Z0-9-]+)\)/U', $value)) {
$value = $this->runReplaceVars($value, $variables);
}
return $value; // Return resolved value
@@ -1721,6 +1767,15 @@ TEXT;
// Create an associative array of variable names to their values
$variables = array_combine($matches[1], $matches[2]);
+ // match again to find inline url data: values
+ preg_match_all('/--([a-zA-Z0-9-]+)\s*:\s*(url\(data:[^)]+\));/', $css, $matches);
+ // Reverse so the light variables are processed first?
+ $matches[1] = array_reverse($matches[1]);
+ $matches[2] = array_reverse($matches[2]);
+
+ // Create an associative array of variable names to their values
+ $variables = array_merge($variables, array_combine($matches[1], $matches[2]));
+
// Replace all `var(--variable)` occurrences in the CSS
$css = $this->runReplaceVars($css, $variables);
=====================================
lib/prefs/print.php
=====================================
@@ -323,6 +323,15 @@ function prefs_print_list()
'tags' => ['advanced'],
'default' => 'y',
],
+ 'print_pdf_mpdf_debug' => [
+ 'name' => tra('Debug mPDF output'),
+ 'description' => tra('Saves a copy of the html sent to mPDF in the temp directory'),
+ 'warning' => tra('Does not respect permissions, do not use with production data.'),
+ 'view' => 'temp/mpdf_debug_output.html',
+ 'type' => 'flag',
+ 'tags' => ['advanced'],
+ 'default' => 'n',
+ ],
'print_wiki_authors' => [
'name' => tra('Print wiki authors'),
'description' => tra('Include wiki page authors and date in print versions of wiki pages.'),
=====================================
templates/admin/include_print.tpl
=====================================
@@ -73,6 +73,7 @@
{preference name=print_pdf_mpdf_coverpage_settings}
{preference name=print_pdf_mpdf_coverpage_image_settings}
{preference name=print_pdf_mpdf_allow_unsafe_ssl_requests}
+ {preference name=print_pdf_mpdf_debug}
</div>
</fieldset>
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/53caad511811cd437967b29a8becff4b00028102
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/53caad511811cd437967b29a8becff4b00028102
You're receiving this email because of your account on gitlab.com.
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs