Source of file PDFService.php
Size: 14,967 Bytes - Last Modified: 2023-11-16T22:56:03+01:00
/home/websites/teicee/packagist/site/phpdoc/conf/../vendor/teicee/dpdf-bundle/src/Base/PDFService.php
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 | <?phpnamespace TIC\DpdfBundle\Base; use Twig\Environment; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; /** * Classe parent pour les services de génération de PDF * (à partir d'un contenu HMTL ou d'une vue Twig). */abstract class PDFService {protected $twig; protected $encore; protected $temp_files = array(); protected $debug = false; /** * Types mimes & extensions de fichiers disponibles */const MIME_TYPES = array( 'PDF' => [ ".pdf", "application/pdf" ], 'PNG' => [ ".png", "image/png" ], # 'JPG' => [ ".jpg", "image/jpeg" ],); /** * Valeurs par défaut (options & chemins) pour les générations Twig|HTML vers PDF|PNG */protected $confs = array( 'debug' => false, // dump des options, debug CSS et fichier de log 'generator' => "auto", // auto|pdf = PDFLib|CPDF|wkhtmltopdf ; image|png = GD|wkhtmltoimage 'pdflib_license' => null, // [dompdf] si usage de PDFLib avec une licence 'media_type' => "screen", // CSS3 media type: screen | print 'paper_size' => "A4", // page format: A4, letter... 'orientation' => "portrait", // page orientation: portrait | landscape 'default_font' => "DejaVu Sans", // [dompdf] helvetica, times-roman, courier... 'font_ratio' => 1.1, // [dompdf] ratio sur la hauteur (pour rendu proche des navigateurs) 'dpi' => 96, // DPI setting (images & fonts) 'default_twigview' => "layout_print", // nom de la vue twig par défault (sans l'extension ".html.twig") 'default_filename' => "document", // nom du fichier de sortie (sans l'extension ".pdf") 'base_url' => null, // URL complète absolue pour résoudre les liens relatifs ); protected $paths = array( 'root_dir' => null, // racine des fichiers accessibles en chargement (chroot|allow) 'path_source' => null, // dossier parent par défaut pour les fichiers chargés (HTML) 'path_output' => null, // dossier parent par défaut pour les fichiers générés (PDF|PNG) 'path_logdir' => null, // dossier parent pour les fichiers de log (debug) 'path_tmpdir' => null, // dossier parent pour les fichiers temporaires (défaut sys_get_temp_dir) 'wkhtml_pdf' => __DIR__ . '../../../../bin/wkhtmltopdf-amd64', // [wkhtml] 'wkhtml_image' => __DIR__ . '../../../../bin/wkhtmltoimage-amd64', // [wkhtml] ); /** * Construction du service en chargeant la configuration des parameters. * * @param array $confs Liste des options pour le paramétrage du service * @param array $paths Liste des chemins pour le paramétrage du service * @param Environment $twig Instance du moteur de rendu pour générer les vues HTML * @param TranslatorInterface $translator Service d'internationalisation pour changer la locale * @param EntrypointLookupInterface $encore Accès à la réinitialisation de Webpack Encore */public function __construct(?array $confs = [], ?array $paths = [], ?Environment $twig = null, ?TranslatorInterface $translator = null, ?EntrypointLookupInterface $encore = null) { $this->twig = $twig; $this->trans = $translator; $this->encore = $encore; $this->confs = \array_merge($this->confs, $confs); $this->paths = \array_merge($this->paths, $paths); if (isset($this->confs['debug'])) $this->debug = (bool)$this->confs['debug']; # \register_shutdown_function([$this, 'purgeTempFiles']); // ne fonctionne pas et empêche le destruct (qui marche sinon) !} public function __destruct() { $this->purgeTempFiles(); } /** * Suppression des fichiers temporaires (sauf en mode debug). */protected function purgeTempFiles() { if ($this->debug) return; foreach ($this->temp_files as $file_path) { if (\file_exists($file_path)) \unlink($file_path); } $this->temp_files = array(); } /** * Création d'un fichier temporaire avec le contenu donné. * * @param string $prefix Chaine pour le début du nom du fichier (sous-dossier si commence par un /) * @param string $suffix Chaine à utiliser comme extension du nom du fichier * @param string $file_data Contenu à placer dans le fichier à générer * @return string Chemin absolu du fichier temporaire (généré/à générer) */protected function createTempFile(string $prefix = "", ?string $suffix = null, string $file_data = null): string { $folder = ""; if (\strlen($prefix) && (\substr($prefix,0,1) === "/")) { $folder = $prefix; $prefix = ""; } $tmpdir = \sys_get_temp_dir(); if (! empty($this->paths['path_tmpdir'])) { if (! \is_dir($this->paths['path_tmpdir'].$folder)) @\mkdir($this->paths['path_tmpdir'].$folder, 0750, true); $tmpdir = $this->paths['path_tmpdir'].$folder; } if (! \is_writable($tmpdir)) throw new RuntimeException(\sprintf("Unable to write in directory: %s\n", $tmpdir)); $file_path = $tmpdir . "/" . \uniqid($prefix, true); if (null !== $suffix) $file_path.= "." . $suffix; if (null !== $file_data) \file_put_contents($file_path, $file_data); $this->temp_files[] = $file_path; return $file_path; } // ------------------------------------------------------------------------------------------ CONFIG/** * Initialisation du moteur html2pdf avec ses options. * * @param array $pdf_options Surcharge des options du moteur html2pdf * @return PDFService */abstract public function initEngine(array $pdf_options = []): PDFService; // ------------------------------------------------------------------------------------------ LOADER/** * Initialisation du moteur html2pdf à partir d'un fichier HTML. * * @param string $html_file Chemin du fichier HTML à charger * @param array $pdf_options Surcharge des options du moteur html2pdf * @return PDFService */abstract public function loadHtmlFile(string $html_file, array $pdf_options = []): PDFService; /** * Initialisation du moteur html2pdf à partir d'un contenu HTML. * * @param string $html_data Contenu du document HTML à charger * @param array $pdf_options Surcharge des options du moteur html2pdf * @return PDFService */abstract public function loadHtmlData(string $html_data, array $pdf_options = []): PDFService; /** * Initialisation du moteur html2pdf à partir d'une vue Twig à générer. * NOTE: avec gestion de la locale et reset de webpack encore (en cas d'usages multiples) * * @param string $twig_view Chemin du template twig à générer * @param array $view_params Variables à injecter dans le template (peut contenir 'locale') * @param array $pdf_options Surcharge des options du moteur html2pdf * @return PDFService */public function loadTwigView(?string $twig_view = null, array $view_params = [], array $pdf_options = []): PDFService { if (! $this->twig) throw new \Exception("Twig environment not found!"); if ($this->encore) $this->encore->reset(); // @see symfony/webpack-encore-bundle#33 if ($this->trans && isset($view_params['locale']) && \strlen($view_params['locale'])) { $orig_locale = $this->trans->getLocale(); $this->trans->setLocale($view_params['locale']); } $html_data = $this->twig->render($this->fixTwigView($twig_view), $view_params); if (isset($orig_locale)) $this->trans->setLocale($orig_locale); return $this->loadHtmlData($html_data, $pdf_options); } // ------------------------------------------------------------------------------------------ RENDER/** * Returns the PDF data as a string. * * @param bool $nocompress Désactivation de la compression PDF * @return string Données binaires du document PDF */abstract public function renderData(bool $nocompress = false): string; /** * Returns the PDF in a local file. * * @param string $filepath Chemin du fichier PDF à enregistrer * @param bool $nocompress Désactivation de la compression PDF * @return string Chemin du fichier PDF généré */public function renderFile(?string $filepath = null, bool $nocompress = false): string { $filepath = $this->fixFilepath($filepath); \file_put_contents($filepath, $this->renderData($nocompress)); return $filepath; } /** * Returns the PDF in a symfony Response object (clean integration). * * @param string $filename Nom du document PDF à télécharger * @param bool $inline Affichage du PDF sans forcer son enregistrement * @param bool $nocompress Désactivation de la compression PDF * @return Response Objet à retourner par le controlleur */public function renderResponse(?string $filename = null, bool $inline = false, bool $nocompress = false): Response { return new Response( $this->renderData($nocompress), 200, $this->getHttpHeaders($filename, $inline) ); } /** * Returns the PDF in a symfony StreamedResponse object (less memory usage). * * @param string $filename Nom du document PDF à télécharger * @param bool $inline Affichage du PDF sans forcer son enregistrement * @param bool $nocompress Désactivation de la compression PDF * @return StreamedResponse Objet à retourner par le controlleur */public function renderStream(?string $filename = null, bool $inline = false, bool $nocompress = false): StreamedResponse { return new StreamedResponse( function() use ($nocompress) { echo $this->renderData($nocompress); }, 200, $this->getHttpHeaders($filename, $inline) ); } // ------------------------------------------------------------------------------------------ COMBOS/** * Initialisation à partir d'une vue Twig pour générer un PDF retourné dans une variable. * * @param string $twig_view Chemin du template twig à générer * @param array $view_params Variables à injecter dans le template (peut contenir 'locale') * @param array $pdf_options Surcharge des options du moteur html2pdf * @return string Données binaires du document PDF */public function view2data(?string $twig_view = null, array $view_params = [], array $pdf_options = []): string { return $this->loadTwigView($twig_view, $view_params, $pdf_options)->renderData(); } /** * Initialisation à partir d'une vue Twig pour générer un PDF retourné dans un fichier local. * * @param string $filepath Chemin du fichier PDF à enregistrer * @param string $twig_view Chemin du template twig à générer * @param array $view_params Variables à injecter dans le template (peut contenir 'locale') * @param array $pdf_options Surcharge des options du moteur html2pdf * @return string Chemin du fichier PDF généré */public function view2file(?string $filepath, ?string $twig_view = null, array $view_params = [], array $pdf_options = []): string { return $this->loadTwigView($twig_view, $view_params, $pdf_options)->renderFile($filepath); } /** * Initialisation à partir d'une vue Twig pour générer un PDF retourné dans une StreamedResponse. * * @param string $filename Nom du document PDF à télécharger * @param string $twig_view Chemin du template twig à générer * @param array $view_params Variables à injecter dans le template (peut contenir 'locale') * @param array $pdf_options Surcharge des options du moteur html2pdf * @return StreamedResponse Objet à retourner par le controlleur */public function view2http(?string $filename, ?string $twig_view = null, array $view_params = [], array $pdf_options = []): StreamedResponse { return $this->loadTwigView($twig_view, $view_params, $pdf_options)->renderStream($filename); } // ------------------------------------------------------------------------------------------ CHECKSprotected function fixTwigView(?string $twig_view = null): string { if ($twig_view === null) $twig_view = $this->confs['default_twigview']; return \preg_match('/[^\/]+\.[^\/]+$/', $twig_view) ? $twig_view : $twig_view . ".html.twig"; } protected function fixHtmlFile(string $html_file): string { // si relatif : utilisation de l'option "path_source" pour ajouter le dossier parent par défaut return (\substr($html_file,0,1) !== '/') ? $this->paths['path_source'] . '/' . $html_file : $html_file; } protected function fixFilepath(?string $filepath = null): string { list($extension) = $this->getFileType(); // récupération de l'éventuelle partie "dossier" du chemin $filepath $folder = \is_string($filepath) ? \dirname($filepath . "X") : "."; // si relatif : utilisation de l'option "path_output" pour ajouter le dossier parent par défaut if (\substr($folder,0,1) !== '/') $folder = $this->paths['path_output'] . '/' . $folder; // si dossier non existant : création automatique (récursive) if (! \is_dir($folder)) @\mkdir($folder, 0750, true); // récupération de l'éventuelle partie "fichier" du chemin $filepath $filename = (\is_string($filepath) && \substr($filepath,-1) !== '/') ? \trim(\basename($filepath, $extension)) : ""; // si vide : génération automatique d'un nom de fichier (datetime + random) if (! \strlen($filename)) $filename = \sprintf('%s-%s', \date('Ymd_His'), \bin2hex(\random_bytes(8))); // retourne le chemin complet (absolu) avec extension automatique return \str_replace('/./', '/', $folder) . '/' . $filename . $extension; } protected function fixFilename(?string $filename = null, ?string $extension = null): string { if ($extension === null) list($extension) = $this->getFileType(); $filename = \is_string($filename) ? \trim(\basename($filename, $extension)) : ""; if (! \strlen($filename)) $filename = \trim(\basename($this->confs['default_filename'], $extension)); return \str_replace('"', '', $filename) . $extension; } protected function getHttpHeaders(?string $filename = null, bool $inline = false): array { list($extension, $mime_type) = $this->getFileType(); return array( 'Content-Type' => $mime_type, 'Content-Disposition' => \sprintf('%s; filename="%s"', ($inline) ? "inline" : "attachment", $this->fixFilename($filename, $extension) ), ); } protected function getFileType(string $type = "PDF"): ?array { if (! isset(self::MIME_TYPES[$type])) $type = "PDF"; return self::MIME_TYPES[$type]; } } |