. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ namespace Espo\Tools\Pdf\Dompdf; use Dompdf\Dompdf; use Dompdf\FontMetrics; use Dompdf\Options; use Espo\Core\Utils\Config; use Espo\Core\Utils\File\Manager as FileManager; use Espo\Core\Utils\Metadata; use Espo\Tools\Pdf\Params; use Espo\Tools\Pdf\Template; class DompdfInitializer { private string $defaultFontFace = 'DejaVu Sans'; private string $cacheDir = 'data/cache/application/dompdf'; private string $pdfaCacheDir = 'data/cache/application/pdfa-dompdf'; private const PT = 2.83465; /** @var array */ private array $standardFontMapping = [ 'courier' => 'DejaVu Sans Mono', 'fixed' => 'DejaVu Sans Mono', 'helvetica' => 'DejaVu Sans', 'monospace' => 'DejaVu Sans Mono', 'sans-serif' => 'DejaVu Sans', 'serif' => 'DejaVu Serif', 'times' => 'DejaVu Serif', 'times-roman' => 'DejaVu Serif', ]; public function __construct( private Config $config, private Metadata $metadata, private FileManager $fileManager, ) {} public function initialize(Template $template, Params $params): Dompdf { $options = new Options(); $options ->setIsPdfAEnabled($params->isPdfA()) ->setDefaultFont($this->getFontFace($template)) ->setIsJavascriptEnabled(false); $dir = $params->isPdfA() ? $this->pdfaCacheDir : $this->cacheDir; $options->setFontDir($dir); $options->setFontCache($dir); if (!$this->fileManager->isDir($dir)) { $this->fileManager->mkdir($dir); } $pdf = new Dompdf($options); $this->mapFonts($pdf, $params->isPdfA(), $dir); $size = $template->getPageFormat() === Template::PAGE_FORMAT_CUSTOM ? [0.0, 0.0, $template->getPageWidth() * self::PT, $template->getPageHeight() * self::PT] : $template->getPageFormat(); $orientation = $template->getPageOrientation() === Template::PAGE_ORIENTATION_PORTRAIT ? 'portrait' : 'landscape'; $pdf->setPaper($size, $orientation); return $pdf; } private function getFontFace(Template $template): string { return $template->getFontFace() ?? $this->config->get('pdfFontFace') ?? $this->defaultFontFace; } private function mapFonts(Dompdf $pdf, bool $isPdfA, string $dir): void { $file = $dir . '/' . FontMetrics::USER_FONTS_FILE; if ($this->fileManager->exists($file)) { return; } // When fonts are included in PDF/A, we need to map standard fonts to open source analogues. // Also need to support popular fonts specified in CSS styles. $fontMetrics = $pdf->getFontMetrics(); if ($isPdfA) { foreach ($this->standardFontMapping as $key => $value) { $fontMetrics->setFontFamily($key, $fontMetrics->getFamily($value)); } return; } /** @var string[] $fontList */ $fontList = $this->metadata->get('app.pdfEngines.Dompdf.fontFaceList') ?? []; $fontList = array_map(fn ($it) => strtolower($it), $fontList); foreach ($this->standardFontMapping as $key => $value) { if (in_array(strtolower($key), $fontList)) { continue; } $fontMetrics->setFontFamily($key, $fontMetrics->getFamily($value)); } } }