From ef0779ca72c458e3515a3bfda250600041a799be Mon Sep 17 00:00:00 2001 From: Maxim Kerstens Date: Sun, 10 Oct 2021 10:12:37 +0200 Subject: [PATCH] Add `fonts:convert` command allow for config cache to be exported update readme --- readme.md | 23 +++- src/Commands/ConvertFont.php | 203 +++++++++++++++++++++++++++++++++++ src/ServiceProvider.php | 9 +- 3 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 src/Commands/ConvertFont.php diff --git a/readme.md b/readme.md index 2564668..a88775b 100644 --- a/readme.md +++ b/readme.md @@ -126,7 +126,28 @@ You can use the CSS `page-break-before`/`page-break-after` properties to create

Page 1

Page 2

- + +### Fonts + +dompdf uses a specific font file format to render them, because of this there is limited support for font families. + +However, dompdf allows you to convert [custom fonts](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding), +this package includes a command that makes it easy to convert your fonts, so you can use them for rendering your PDFs. + +Make sure that your `dompdf.defines.font_dir` directory exists. + +If you want to know what default fonts are bundled you can run `php artisan vendor:publish --tag=pdf-fonts`. + +You can convert your own fonts to the supported format, +You will have to register `\Barryvdh\DomPDF\Commands\ConvertFont` in `\App\Console\Kernel::$commands`. + +After this you'll be able to run `php artisan font:convert fontFamilyName fontFilePath` + +You are able to define separate font faces for Italic, Bold and Italic Bold, however, if not defined, the command will try to look for these instead. + +````bash +php artisan font:convert "Font Family Name" "./storage/FontFamilyName.ttf" --italic "./storage/FontFamilyNameItalic.ttf" --bold "./storage/FontFamilyNameBold.ttf" --bold-italic "./storage/FontFamilyNameBoldItalic.ttf" +```` ### License This DOMPDF Wrapper for Laravel is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) diff --git a/src/Commands/ConvertFont.php b/src/Commands/ConvertFont.php new file mode 100644 index 0000000..a086148 --- /dev/null +++ b/src/Commands/ConvertFont.php @@ -0,0 +1,203 @@ +getOptions()->set('fontDir', $fontDir); + } + + $this->convertAndCacheFont( + $dompdf, + $this->argument('fontFamily'), + $this->argument('fileName'), + $this->option('bold'), + $this->option('italic'), + $this->option('bold-italic') + ); + + return 0; + } + + protected function convertAndCacheFont( + Dompdf $dompdf, + string $fontName, + string $normal, + ?string $bold, + ?string $italic, + ?string $bold_italic + ) + { + $fontMetrics = $dompdf->getFontMetrics(); + + $fonts = $this->fontFaces($normal, $bold, $italic, $bold_italic); + $entry = []; + + // Copy the files to the fonts directory. + foreach ($fonts as $fontFace => $srcFile) { + // Font face has no source file + if (is_null($srcFile)) { + // Default to normal fonts face + $entry[$fontFace] = $dompdf->getOptions()->get('fontDir') . '/' . mb_substr(basename($normal), 0, -4); + continue; + } + + // Verify that the fonts exist and are readable + if (!is_readable($srcFile)) { + throw new Exception("Requested fonts '$srcFile' is not readable"); + } + + $destFile = $dompdf->getOptions()->get('fontDir') . '/' . basename($srcFile); + + if (!is_writeable(dirname($destFile))) { + throw new Exception("Unable to write to destination '$destFile'."); + } + + $this->info(__( + 'Copying :srcFile to :destFile', + compact('srcFile', 'destFile') + )); + + if (!copy($srcFile, $destFile)) { + throw new Exception("Unable to copy '$srcFile' to '$destFile'"); + } + + $entry_name = mb_substr($destFile, 0, -4); + + $this->comment(__( + ' > Generating Adobe Font Metrics for :entry_name', + compact('entry_name') + )); + + $font_obj = Font::load($destFile); + $font_obj->saveAdobeFontMetrics($entry_name . '.ufm'); + $font_obj->close(); + + $entry[$fontFace] = $entry_name; + } + + // Store the fonts in the lookup table + $fontMetrics->setFontFamily($fontName, $entry); + + // Save the changes + $fontMetrics->saveFontFamilies(); + } + + /** + * Generates a list that contains a file path to each font face type. + * If a font face type isn't defined, we'll attempt to locate it. + * + * @param string $normal + * @param string|null $bold + * @param string|null $italic + * @param string|null $bold_italic + * @return array + * @throws Exception + */ + protected function fontFaces(string $normal, string $bold = null, string $italic = null, string $bold_italic = null): array + { + // Check if the base filename is readable + if (!is_readable($normal)) { + throw new Exception("Unable to read '$normal'."); + } + + $dir = dirname($normal); + $basename = basename($normal); + + // Get the file name & extension + $last_dot = strrpos($basename, '.'); + if ($last_dot !== false) { + $file = substr($basename, 0, $last_dot); + $ext = strtolower(substr($basename, $last_dot)); + } else { + $file = $basename; + $ext = ''; + } + + if (!in_array($ext, static::SUPPORTED_FILES)) { + throw new Exception("Unable to process fonts of type '$ext'."); + } + + // Path to regular fonts face + $regularFace = $dir . '/' . $file; + $path = str_replace(static::$PATTERN_REGULAR, '', $regularFace); + + // Suffix patters used to find undefined fonts faces + $patterns = [ + 'bold' => static::$PATTERN_BOLD, + 'italic' => static::$PATTERN_ITALIC, + 'bold_italic' => static::$PATTERN_BOLD_ITALIC, + ]; + + foreach ($patterns as $type => $_patterns) { + // Font face either isn't defined or exists + if (!isset($$type) || !is_readable($$type)) { + foreach ($_patterns as $_pattern) { + // Is there a file, matching the pattern as a suffix + if (is_readable($path . $_pattern . $ext)) { + $$type = $path . $_pattern . $ext; + break; + } + } + + if (is_null($$type)) { + $this->warn( + __( + 'Unable to find :type face file.', + compact('type') + ) + ); + } + } + } + + return compact('normal', 'bold', 'italic', 'bold_italic'); + } +} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index b1a09fa..449787f 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -78,8 +78,15 @@ protected function isLumen(): bool public function boot(): void { if (! $this->isLumen()) { + // Publish config $configPath = __DIR__ . '/../config/dompdf.php'; - $this->publishes([$configPath => config_path('dompdf.php')], 'config'); + $this->publishes([$configPath => config_path('dompdf.php')], ['config', 'dompdf']); + + // Publish fonts cache + $this->publishes([ + base_path('vendor/dompdf/dompdf/lib/fonts') => config('dompdf.defines.font_dir') + ], + ['pdf-fonts', 'dompdf']); } }