Commit cb18a5fe authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Nouvelle méthode `PdfExporter::prepare()` à appeler obligatoirement pour...

Nouvelle méthode `PdfExporter::prepare()` à appeler obligatoirement pour garantir la construction de l'instance Mpdf au bon moment et la bonne prise en compte des propriétés spécifiées.
[FIX] Construction obsolète de l'instance Mpdf (clés de config manquantes).
Cf. [migration.md](doc/migration.md#700).
parent b37c439d
Loading
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
CHANGELOG
=========

7.0.0
-----
- Nouvelle méthode `PdfExporter::prepare()` à appeler obligatoirement pour garantir la construction de l'instance Mpdf au bon moment et la bonne prise en compte des propriétés spécifiées.
  [FIX] Construction obsolète de l'instance Mpdf (clés de config manquantes).
  Cf. [migration.md](doc/migration.md#700).

6.1.0
-----
- Ajout des commandes shell de concaténation de fichiers PDF avec Ghostscript ou QPDF.

doc/migration.md

0 → 100644
+58 −0
Original line number Diff line number Diff line
Migration
=========

Vous trouvez ici, les instructions permettant de passer à une version particulière qui introduit une imcompatibilité par
rapport aux précédentes versions.

7.0.0
-----

Pour passer à la version 7.0.0, vous devez modifier votre code pour appeler la nouvelle méthode `PdfExporter::prepare()`.

Prenons l'exemple d'une classe héritant de `PdfExporter`, le code suivant est obsolète et doit être modifié : 

```php
class ConventionFormationDoctoraleExporter extends PdfExporter
{
    // ...
    public function export($filename = null, $destination = self::DESTINATION_BROWSER, $memoryLimit = null)
    {
        // ...
        $rendu = $this->getRenduService()->generateRenduByTemplateCode(PdfTemplates::ADMISSION_CONVENTION_FORMATION_DOCTORALE, $vars);

        $this->getMpdf()->SetMargins(0,0,60); // $this->getMpdf() va retourner `null` à ce stade
        $this->addBodyHtml($rendu->getCorps());

        return parent::export($filename, $destination, $memoryLimit);
    }
```

Il doit devenir :

```php
class ConventionFormationDoctoraleExporter extends PdfExporter
{
    // ...
    public function export($filename = null, $destination = self::DESTINATION_BROWSER, $memoryLimit = null)
    {
        // ...
        $rendu = $this->getRenduService()->generateRenduByTemplateCode(PdfTemplates::ADMISSION_CONVENTION_FORMATION_DOCTORALE, $vars);

        $this->setMarginTop(60); // utiliser les `PdfExporter::set*()` redevient possible 
        $this->addBodyHtml($rendu->getCorps());
        $this->prepare(); // <<<<<<<<<< appel obligatoire de `PdfExporter::prepare()`
        // $this->getMpdf()->SetTitle($titre); // après l'appel, $this->getMpdf() pourra être utilisé si besoin

        return parent::export($filename, $destination, $memoryLimit);
    }
```

Si vous ne sous-classez pas `PdfExporter`, c'est la même idée :

```php
$exporter = new PdfExporter();
$exporter->setMarginTop(60); 
$exporter->addBodyHtml($corps);
$exporter->prepare();
//$exporter->getMpdf()->SetTitle($titre);
```
+2 −0
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@ interface ExporterInterface
     */
    public function getRenderer(): \Laminas\View\Renderer\PhpRenderer;

    public function prepare(): void;
    
    /**
     * @param string|null $filename
     */
+88 −56
Original line number Diff line number Diff line
@@ -45,10 +45,9 @@ class PdfExporter implements ExporterInterface
     * @var PhpRenderer
     */
    private $renderer;
    /**
     * @var mPDF
     */
    private $mpdf;

    protected ?mPDF $mpdf = null;

    /**
     * @var array
     */
@@ -140,6 +139,11 @@ class PdfExporter implements ExporterInterface
     */
    private $logo;

    protected ?string $waterMark = null;

    protected array $permissions = [];
    protected ?string $userPassword = null;

    /**
     * Chemin absolu du répertoire contenant les scripts de vue par défaut.
     *
@@ -147,6 +151,8 @@ class PdfExporter implements ExporterInterface
     */
    protected $defaultScriptsDirPath = __DIR__ . '/scripts';

    protected bool $isPrepared = false;

    /**
     * Constructeur.
     *
@@ -323,7 +329,58 @@ class PdfExporter implements ExporterInterface
    }

    /**
     * Génère le document PDF et l'envoie éventuellement au navigateur.
     * Création (sauf si vous avez utilisé la méthode `setMpdf()`) et préparation de l'instance de Mpdf nécessaire
     * à la génération PDF.
     *
     * **L'appel à cette méthode est requis depuis la version 7.0 de la bibliothèque,
     * sauf si vous savez ce que vous faites !**
     *
     * Typiquement, l'appel doit être fait juste avant la génération PDF (export).
     *
     * @throws \Mpdf\MpdfException
     */
    public function prepare(): void
    {
        if ($this->isPrepared) {
            return;
        }

        if ($this->mpdf === null) {
            $this->mpdf = new mPDF([
                'mode' => 's',
                'format' => $this->format . ($this->orientationPaysage ? '-L' : null),
                'default_font_size' => $this->defaultFontSize,
                'default_font' => '',
                'margin_left' => $this->marginLeft,
                'margin_right' => $this->marginRight,
                'margin_top' => $this->marginTop,
                'margin_bottom' => $this->marginBottom,
                'margin_header' => $this->marginHeader,
                'margin_footer' => $this->marginFooter,
                'orientation' => $this->orientationPaysage ? 'L' : 'P',
                'tempDir' => sys_get_temp_dir() . '/mpdf', // pris en compte ??
            ]);
        }

        $this->mpdf->useSubstitutions = false;
        //$this->mpdf->simpleTables = true; // ne respecte pas les styles de border
        $this->mpdf->mirrorMargins = true; // different header and footer on odd/even pages numbers

        if ($this->waterMark !== null) {
            $this->mpdf->SetWatermarkText($this->waterMark);
            $this->mpdf->watermarkTextAlpha = 0.2;
            $this->mpdf->showWatermarkText = true;
        }

        if ($this->permissions || $this->userPassword) {
            $this->mpdf->SetProtection($this->permissions, $this->userPassword);
        }

        $this->isPrepared = true;
    }

    /**
     * Génère le document PDF et l'envoie éventuellement au client.
     *
     * @param string|null $filename Nom du document PDF (avec extension)
     * PdfExporter::DESTINATION_BROWSER :
@@ -371,7 +428,7 @@ class PdfExporter implements ExporterInterface
        // real: Display at real size
        // default: User's default setting in Adobe Reader
        // INTEGER: Display at a percentage zoom (e.g. 90 will display at 90% zoom)
        $this->getMpdf()->SetDisplayMode('default');
        $this->mpdf->SetDisplayMode('default');

        $exit = true;
        if (self::DESTINATION_FILE == $destination) {
@@ -382,7 +439,7 @@ class PdfExporter implements ExporterInterface
        }

        // Output pdf
        $out = $this->getMpdf()->Output($filename, $destination);
        $out = $this->mpdf->Output($filename, $destination);

        ini_set('display_errors', $displayErrors);

@@ -446,18 +503,18 @@ class PdfExporter implements ExporterInterface
        // styles de base fournis par la librairie Unicaen
        if (file_exists(($filepath = $this->getDefaultScriptsPath() . '/pdf.css'))) {
            $css = file_get_contents($filepath);
            $this->getMpdf()->WriteHTML($css, 1);
            $this->mpdf->WriteHTML($css, 1);
            $parts[] = $css;
        }
//        // styles spécifiques éventuels fournis par chaque application
//        if (file_exists(($filepath = APPLICATION_PATH . '/../public/styles/pdf.css'))) {
//            $css = file_get_contents($filepath);
//            $this->getMpdf()->WriteHTML($css, 1);
//            $this->mpdf->WriteHTML($css, 1);
//            $parts[] = $css;
//        }
//        else if (file_exists(($filepath = APPLICATION_PATH . '/../public/css/pdf.css'))) {
//            $css = file_get_contents($filepath);
//            $this->getMpdf()->WriteHTML($css, 1);
//            $this->mpdf->WriteHTML($css, 1);
//            $parts[] = $css;
//        }

@@ -483,7 +540,7 @@ class PdfExporter implements ExporterInterface
        $scriptVars = array_merge($this->headerVars, $scriptVars);

        // le logo doit être passé ainsi pour pouvoir être référencé dans la balise <img> sous la forme "var:logo"
        $this->getMpdf()->imageVars['logo'] = $this->logo;
        $this->mpdf->imageVars['logo'] = $this->logo;

        if (isset($this->headerScripts['O'])) {
            $headerOdd = $this->getRenderer()->render($this->headerScripts['O'], $scriptVars);
@@ -494,10 +551,10 @@ class PdfExporter implements ExporterInterface
        }

        if ($headerOdd) {
            $this->getMpdf()->SetHTMLHeader($headerOdd, 'O');
            $this->mpdf->SetHTMLHeader($headerOdd, 'O');
        }
        if ($headerEven) {
            $this->getMpdf()->SetHTMLHeader($headerEven, 'E');
            $this->mpdf->SetHTMLHeader($headerEven, 'E');
        }

        return $this;
@@ -537,9 +594,9 @@ class PdfExporter implements ExporterInterface

            // write body
            if ($report['_newPage']) {
                $this->getMpdf()->AddPage('', '', $resetPageNum);
                $this->mpdf->AddPage('', '', $resetPageNum);
            }
            $this->getMpdf()->WriteHTML($part);
            $this->mpdf->WriteHTML($part);

            $bodyParts[] = $part;
        }
@@ -572,10 +629,10 @@ class PdfExporter implements ExporterInterface
        }

        if ($footerOdd) {
            $this->getMpdf()->SetHTMLFooter($footerOdd, 'O');
            $this->mpdf->SetHTMLFooter($footerOdd, 'O');
        }
        if ($footerEven) {
            $this->getMpdf()->SetHTMLFooter($footerEven, 'E');
            $this->mpdf->SetHTMLFooter($footerEven, 'E');
        }

        return $this;
@@ -612,49 +669,22 @@ class PdfExporter implements ExporterInterface
    }

    /**
     * Spécifie l'objet de fabrication du document PDF.
     *
     * @param \Mpdf\Mpdf|null $mPdf
     * @return self
     * Spécifie l'instance de fabrication du document PDF.
     */
    public function setMpdf(mPDF $mPdf = null): self
    public function setMpdf(mPDF $mPdf): self
    {
        $this->mpdf = $mPdf;
        $this->isPrepared = false;

        return $this;
    }

    /**
     * Retourne l'objet de fabrication du document PDF.
     *
     * @return mPDF
     * @throws \Mpdf\MpdfException
     * Retourne l'instance de fabrication du document PDF.
     * **Si aucun appel à prepare() ou à setMpdf() n'a été fait, cette méthode retournera null.**
     */
    public function getMpdf(): mPDF
    {
        if (null === $this->mpdf) {

//            define("_MPDF_TEMP_PATH", sys_get_temp_dir() . '/mpdf');

            // create object mpdf
            $this->mpdf = new mPDF([
                $mode = 's',
                $this->format . ($this->orientationPaysage ? '-L' : null),
                $this->defaultFontSize,
                '' /* $default_font */,
                $this->marginLeft,
                $this->marginRight,
                $this->marginTop,
                $this->marginBottom,
                $this->marginHeader,
                $this->marginFooter,
                'tempDir' => sys_get_temp_dir() . '/mpdf',
            ]);

            $this->mpdf->useSubstitutions = false;
//            $this->mpdf->simpleTables = true; // ne respecte pas les styles de border
            $this->mpdf->mirrorMargins = true; // different header and footer on odd/even pages numbers
        }

        return $this->mpdf;
    }

@@ -707,7 +737,9 @@ class PdfExporter implements ExporterInterface
     */
    public function setPermissions(array $permissions, string $userPassword = ''): self
    {
        $this->getMpdf()->SetProtection($permissions, $userPassword);
        $this->permissions = $permissions;
        $this->userPassword = $userPassword;

        return $this;
    }

@@ -719,9 +751,8 @@ class PdfExporter implements ExporterInterface
     */
    public function setWatermark(string $text): self
    {
        $this->getMpdf()->SetWatermarkText($text);
        $this->getMpdf()->watermarkTextAlpha = 0.2;
        $this->getMpdf()->showWatermarkText = true;
        $this->waterMark = $text;

        return $this;
    }

@@ -875,10 +906,11 @@ class PdfExporter implements ExporterInterface
    {
        try {
            $exporter = new PdfExporter();
            $exporter->getMpdf()->SetTitle($titre);
            $exporter->setHeaderScript($headerScript, null, $vars);
            $exporter->setFooterScript($footerScript, null, $vars);
            $exporter->addBodyHtml($corps);
            $exporter->prepare();
            $exporter->getMpdf()->SetTitle($titre);
            return $exporter->export($filename);
        } catch (MpdfException $e) {
            throw new RuntimeException("Un problème lié à MPDF est survenue",0,$e);
+8 −5
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ class PdfExporterTest extends TestCase
     * Sets up the fixture, for example, open a network connection.
     * This method is called before a test is executed.
     */
    protected function setUp()
    protected function setUp(): void
    {
        $this->memoryLimit = ini_get('memory_limit');
        $this->tempDirectoryPath = sys_get_temp_dir();
@@ -52,7 +52,7 @@ class PdfExporterTest extends TestCase
     * Tears down the fixture, for example, close a network connection.
     * This method is called after a test is executed.
     */
    protected function tearDown()
    protected function tearDown(): void
    {
//        ini_set('memory_limit', $this->memoryLimit);
    }
@@ -70,9 +70,10 @@ class PdfExporterTest extends TestCase
        $this->assertSame($renderer, $exporter->getRenderer());
    }
    
    public function testCanGetDefaultMpdfObject()
    public function testCanGetPrepareMpdfObject()
    {
        $exporter = new PdfExporter();
        $exporter->prepare();
        $this->assertInstanceOf(mPDF::class, $exporter->getMpdf());
    }
    
@@ -112,8 +113,10 @@ class PdfExporterTest extends TestCase
                   ->with($text);
                
        $this->exporter->setWatermark($text);
        $this->exporter->prepare();
        $mpdf = $this->exporter->getMpdf();

        $this->assertTrue($this->exporter->getMpdf()->showWatermarkText);
        $this->assertTrue($mpdf->showWatermarkText);
    }

    public function getDefaultScriptFileNames()