mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-05-04 18:08:46 +03:00
Merge pull request #6109 from BookStackApp/dompdf_font_loading
PDF: Started building system to allow custom DOMPDF font loading
This commit is contained in:
@@ -68,7 +68,7 @@ return [
|
||||
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
|
||||
* Symbol, ZapfDingbats.
|
||||
*/
|
||||
'font_dir' => storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
|
||||
'font_dir' => storage_path('fonts/dompdf'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
|
||||
|
||||
/**
|
||||
* The location of the DOMPDF font cache directory.
|
||||
@@ -78,7 +78,7 @@ return [
|
||||
*
|
||||
* Note: This directory must exist and be writable by the webserver process.
|
||||
*/
|
||||
'font_cache' => storage_path('fonts/'),
|
||||
'font_cache' => storage_path('fonts/dompdf/cache'),
|
||||
|
||||
/**
|
||||
* The location of a temporary directory.
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace BookStack\Exports;
|
||||
|
||||
use BookStack\Exceptions\PdfExportException;
|
||||
use Dompdf\Dompdf;
|
||||
use FontLib\Font;
|
||||
use Illuminate\Support\Str;
|
||||
use Knp\Snappy\Pdf as SnappyPdf;
|
||||
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
||||
use Symfony\Component\Process\Process;
|
||||
@@ -60,12 +62,65 @@ class PdfGenerator
|
||||
$domPdf = new Dompdf($options);
|
||||
$domPdf->setBasePath(base_path('public'));
|
||||
|
||||
$fontMetrics = $domPdf->getFontMetrics();
|
||||
$userFontfamilies = $this->getUserDomPdfFontFamilies();
|
||||
foreach ($userFontfamilies as $fontFamily => $fonts) {
|
||||
try {
|
||||
$fontMetrics->setFontFamily($fontFamily, $fonts);
|
||||
} catch (\Exception $exception) {
|
||||
$expectedPath = storage_path('fonts/dompdf');
|
||||
throw new PdfExportException("Failed to create required font data in {$expectedPath}, Ensure all content in this location is writable by the web server");
|
||||
}
|
||||
}
|
||||
|
||||
$domPdf->loadHTML($this->convertEntities($html));
|
||||
$domPdf->render();
|
||||
|
||||
return (string) $domPdf->output();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, string>>
|
||||
*/
|
||||
protected function getUserDomPdfFontFamilies(): array
|
||||
{
|
||||
$fontStore = storage_path('fonts/dompdf');
|
||||
if (!is_dir($fontStore)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fontFamilies = [];
|
||||
$fontFiles = glob($fontStore . DIRECTORY_SEPARATOR . '*.ttf');
|
||||
foreach ($fontFiles as $fontFile) {
|
||||
$fontFileName = basename($fontFile, '.ttf');
|
||||
$expectedUfm = $fontStore . DIRECTORY_SEPARATOR . $fontFileName . '.ufm';
|
||||
if (!file_exists($expectedUfm)) {
|
||||
$font = Font::load($fontFile);
|
||||
$font->parse();
|
||||
try {
|
||||
$font->saveAdobeFontMetrics($expectedUfm);
|
||||
} catch (\Exception $exception) {
|
||||
throw new PdfExportException("Failed to create required font data at $expectedUfm, Ensure this location is writable by the web server");
|
||||
}
|
||||
}
|
||||
|
||||
$nameParts = explode('-', $fontFileName);
|
||||
if (count($nameParts) === 1 || $nameParts[1] === 'Regular') {
|
||||
$nameParts[1] = 'Normal';
|
||||
}
|
||||
|
||||
$family = trim(strtolower(preg_replace('/([A-Z])/', ' $1', $nameParts[0])));
|
||||
$variation = Str::snake($nameParts[1]);
|
||||
if (!isset($fontFamilies[$family])) {
|
||||
$fontFamilies[$family] = [];
|
||||
}
|
||||
|
||||
$fontFamilies[$family][$variation] = $fontStore . DIRECTORY_SEPARATOR . $fontFileName;
|
||||
}
|
||||
|
||||
return $fontFamilies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PdfExportException
|
||||
*/
|
||||
|
||||
6
storage/fonts/.gitignore
vendored
6
storage/fonts/.gitignore
vendored
@@ -1,2 +1,6 @@
|
||||
# Font cache files have once been stored directly in this folder
|
||||
# therefore its important the contents non-ignored by git
|
||||
# are chosen selectively
|
||||
*
|
||||
!.gitignore
|
||||
!.gitignore
|
||||
!dompdf/
|
||||
3
storage/fonts/dompdf/.gitignore
vendored
Normal file
3
storage/fonts/dompdf/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!.gitignore
|
||||
!cache/
|
||||
2
storage/fonts/dompdf/cache/.gitignore
vendored
Normal file
2
storage/fonts/dompdf/cache/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -79,6 +79,39 @@ class PdfExportTest extends TestCase
|
||||
$this->assertStringContainsString('<details open="open"', $pdfHtml);
|
||||
}
|
||||
|
||||
public function test_custom_fonts_loaded_for_dom_pdf_when_used()
|
||||
{
|
||||
// Set up custom font usage
|
||||
$page = $this->entities->page()->forceFill([
|
||||
'html' => '<p><strong>Bold</strong>text</p>',
|
||||
]);
|
||||
$page->save();
|
||||
$this->setSettings([
|
||||
'app-custom-head' => '<style>* { font-family: "meow words"}</style>'
|
||||
]);
|
||||
$normalFont = $this->files->testFilePath('fonts/Cardiff.ttf');
|
||||
$normalFontTarget = storage_path('fonts/dompdf/MeowWords.ttf');
|
||||
$boldFont = $this->files->testFilePath('fonts/Cardiff-Bold.ttf');
|
||||
$boldFontTarget = storage_path('fonts/dompdf/MeowWords-Bold.ttf');
|
||||
copy($normalFont, $normalFontTarget);
|
||||
copy($boldFont, $boldFontTarget);
|
||||
|
||||
$resp = $this->asEditor()->get($page->getUrl('/export/pdf'));
|
||||
$resp->assertStatus(200);
|
||||
|
||||
// Existance of UFM files indicates the metrics have been generated
|
||||
$this->assertFileExists(storage_path('fonts/dompdf/MeowWords.ufm'));
|
||||
$this->assertFileExists(storage_path('fonts/dompdf/MeowWords-Bold.ufm'));
|
||||
// Existence of cache json files indicates the fonts have been used
|
||||
$this->assertFileExists(storage_path('fonts/dompdf/cache/MeowWords.ufm.json'));
|
||||
$this->assertFileExists(storage_path('fonts/dompdf/cache/MeowWords-Bold.ufm.json'));
|
||||
|
||||
$filesToCleanUp = [...glob(storage_path('fonts/dompdf/Meow*')), ...glob(storage_path('fonts/dompdf/cache/Meow*'))];
|
||||
foreach ($filesToCleanUp as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_wkhtmltopdf_only_used_when_allow_untrusted_is_true()
|
||||
{
|
||||
$page = $this->entities->page();
|
||||
|
||||
BIN
tests/test-data/fonts/Cardiff-Bold.ttf
Normal file
BIN
tests/test-data/fonts/Cardiff-Bold.ttf
Normal file
Binary file not shown.
BIN
tests/test-data/fonts/Cardiff.ttf
Normal file
BIN
tests/test-data/fonts/Cardiff.ttf
Normal file
Binary file not shown.
2
tests/test-data/fonts/attribution.txt
Normal file
2
tests/test-data/fonts/attribution.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Font files by Roger White, in public domain.
|
||||
https://web.archive.org/web/20110609213636/http://www.rogersfonts.org.uk/
|
||||
Reference in New Issue
Block a user