PDF: Added implmentation of command PDF option

Tested quickly manually but not yet covered by PHPUnit tests.
This commit is contained in:
Dan Brown 2024-04-24 16:09:53 +01:00
parent 40200856af
commit 1c7128c2cb
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
3 changed files with 60 additions and 10 deletions

View File

@ -2,8 +2,10 @@
namespace BookStack\Entities\Tools; namespace BookStack\Entities\Tools;
use BookStack\Exceptions\PdfExportException;
use Knp\Snappy\Pdf as SnappyPdf; use Knp\Snappy\Pdf as SnappyPdf;
use Dompdf\Dompdf; use Dompdf\Dompdf;
use Symfony\Component\Process\Process;
class PdfGenerator class PdfGenerator
{ {
@ -13,19 +15,15 @@ class PdfGenerator
/** /**
* Generate PDF content from the given HTML content. * Generate PDF content from the given HTML content.
* @throws PdfExportException
*/ */
public function fromHtml(string $html): string public function fromHtml(string $html): string
{ {
$engine = $this->getActiveEngine(); return match ($this->getActiveEngine()) {
self::ENGINE_COMMAND => $this->renderUsingCommand($html),
if ($engine === self::ENGINE_WKHTML) { self::ENGINE_WKHTML => $this->renderUsingWkhtml($html),
return $this->renderUsingWkhtml($html); default => $this->renderUsingDomPdf($html)
} else if ($engine === self::ENGINE_COMMAND) { };
// TODO - Support PDF command
return '';
}
return $this->renderUsingDomPdf($html);
} }
/** /**
@ -34,6 +32,10 @@ class PdfGenerator
*/ */
public function getActiveEngine(): string public function getActiveEngine(): string
{ {
if (config('exports.pdf_command')) {
return self::ENGINE_COMMAND;
}
if ($this->getWkhtmlBinaryPath() && config('app.allow_untrusted_server_fetching') === true) { if ($this->getWkhtmlBinaryPath() && config('app.allow_untrusted_server_fetching') === true) {
return self::ENGINE_WKHTML; return self::ENGINE_WKHTML;
} }
@ -63,6 +65,46 @@ class PdfGenerator
return (string) $domPdf->output(); return (string) $domPdf->output();
} }
/**
* @throws PdfExportException
*/
protected function renderUsingCommand(string $html): string
{
$command = config('exports.pdf_command');
$inputHtml = tempnam(sys_get_temp_dir(), 'bs-pdfgen-html-');
$outputPdf = tempnam(sys_get_temp_dir(), 'bs-pdfgen-output-');
$replacementsByPlaceholder = [
'{input_html_path}' => $inputHtml,
'{output_html_path}' => $outputPdf,
];
foreach ($replacementsByPlaceholder as $placeholder => $replacement) {
$command = str_replace($placeholder, escapeshellarg($replacement), $command);
}
file_put_contents($inputHtml, $html);
$process = Process::fromShellCommandline($command);
$process->setTimeout(15);
$process->run();
if (!$process->isSuccessful()) {
throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}");
}
$pdfContents = file_get_contents($outputPdf);
unlink($outputPdf);
if ($pdfContents === false) {
throw new PdfExportException("PDF Export via command failed, unable to read PDF output file");
} else if (empty($pdfContents)) {
throw new PdfExportException("PDF Export via command failed, PDF output file is empty");
}
return $pdfContents;
}
protected function renderUsingWkhtml(string $html): string protected function renderUsingWkhtml(string $html): string
{ {
$snappy = new SnappyPdf($this->getWkhtmlBinaryPath()); $snappy = new SnappyPdf($this->getWkhtmlBinaryPath());

View File

@ -0,0 +1,7 @@
<?php
namespace BookStack\Exceptions;
class PdfExportException extends \Exception
{
}

View File

@ -51,6 +51,7 @@
<server name="LOG_FAILED_LOGIN_MESSAGE" value=""/> <server name="LOG_FAILED_LOGIN_MESSAGE" value=""/>
<server name="LOG_FAILED_LOGIN_CHANNEL" value="testing"/> <server name="LOG_FAILED_LOGIN_CHANNEL" value="testing"/>
<server name="WKHTMLTOPDF" value="false"/> <server name="WKHTMLTOPDF" value="false"/>
<server name="EXPORT_PDF_COMMAND" value="false"/>
<server name="APP_DEFAULT_DARK_MODE" value="false"/> <server name="APP_DEFAULT_DARK_MODE" value="false"/>
<server name="IP_ADDRESS_PRECISION" value="4"/> <server name="IP_ADDRESS_PRECISION" value="4"/>
</php> </php>