web-dev-qa-db-fra.com

Comment générer en vrac PDF des modèles HTML prêts pour l'impression recto verso en PHP?

Cela fait un moment que je me bats avec cela et je me sens impuissant . Prestashop utilise tcpdf pour générer des factures et des bons de livraison à partir de modèles HTML renseignés à l'aide de Smarty. Nous travaillons actuellement à la mise à jour du modèle de facture et avons constaté que tcpdf manquait de prise en charge CSS. Après quelques recherches, nous avons opté pour wkhtmltopdf comme outil approprié pour convertir les modèles HTML/CSS en PDF.

Le problème

Le magasin dispose d'une fonctionnalité permettant d'exporter plusieurs factures dans un seul PDF. En utilisant TCPDF, j'ai pu préparer le fichier batch pour l'impression recto verso en insérant une page vierge après chaque facture comportant un nombre impair de pages avant la génération du fichier. Mais maintenant que nous sommes passés à wkhtmltopdf, je ne peux pas obtenir le même résultat.

Le problème crucial est que, bien que wkhtmltopdf permette l’utilisation de plusieurs modèles HTML, il semble qu’il n’existe aucun moyen fiable de déterminer le nombre de pages qu’ils auront avant la génération du fichier. Les modèles d'en-tête et de pied de page peuvent recevoir le nombre de pages de la facture mais ils sont distincts du contenu principal. Par conséquent, je ne peux pas insérer de saut de page en conséquence. 

J'ai également essayé de calculer le height of the contentPDF page height mais il y avait divers problèmes avec cela une fois que j'ai commencé à exporter plusieurs modèles (cela fonctionnait bien avec un seul modèle). Cette approche n’est pas bonne non plus, car l’insertion d’une page vierge dans le contenu même fait que le pied de page apparaît également sur la nouvelle page, ce qui n’est pas ce que je veux.

_/Ma meilleure tentative

La seule façon pour moi de résoudre ces problèmes est très inefficace. Chaque fois qu'un modèle est ajouté au lot, je peux le pré-générer à l'aide d'une instance distincte d'un wrapper pour wkhtmltopdf, obtenir le nom du fichier temporaire, déterminer le nombre de pages qu'il contient à l'aide de pdfinfo et ajouter un modèle HTML vierge à l'instance principale en conséquence. Voici un brouillon d'une fonction pour obtenir le nombre de pages du dernier modèle ajouté (à partir d'une classe qui étend l'encapsuleur, en se basant sur d'autres pdfinfoquestions que j'ai trouvées sur SO):

/**
* Return the number of pages in the last invoice template added
* $complete === true => return the length of the entire document
*/
public function getNumPages($complete = false)
{
    if (!$complete) {
        // Generate PDF of last template added

        $tmpPdf = new WKPdf($this->_options);
        $tmpPdf->addPage($this->content, Array(
            'footer-html' => $this->footer
        ));

        /**
           The createPdf method is protected so I need to get 
           the content as string here to force the wrapper to 
           call wkhtmltopdf.
        */
        $tmpPdf->toString();
        $document = $tmpPdf->getPdfFilename();
    } else {

        // Generate entire batch
        $this->createPdf();
        $document = $this->getPdfFilename();
    }

    // Use pdfinfo to get the PDF page count
    $cmd = 'pdfinfo';
    exec("$cmd \"$document\"", $output);

    $pagecount = 0;
    foreach($output as $op)
    {
        // Extract the number
        if(preg_match("/Pages:\s*(\d+)/i", $op, $matches) === 1)
        {
            $pagecount = intval($matches[1]);
            break;
        }
    }

    return $pagecount;
}

Ceci est très inefficace - il faut environ 80 secondes pour générer un lot de 25 factures, car je dois appeler wkhtmltopdf 25 fois pour créer les fichiers temporaires PDF afin de pouvoir appeler pdfinfo 25 fois pour obtenir leurs longueurs individuelles et les insérer. des pages vierges si nécessaire, puis générez le document final. 

L’avantage de TCPDF est qu’il peut vous donner le nombre de pages à la volée et qu’une fonctionnalité similaire prend environ 5 secondes pour générer un fichier de traitement par lots de 25 factures.

Quelqu'un a une idée sur la façon d'accélérer les choses? Ou une meilleure idée de le faire tout à fait. J'ai envisagé divers outils pour la génération, notamment dompdf, mais wkhtmltopdf est tout simplement le plus puissant. La génération de lots n’est utilisée que depuis le back-office par les administrateurs du magasin. Ils pourraient donc être patients. Mais reste.

10
PeterTheLobster

Malheureusement, wkhtmltopdf est la bibliothèque, qui est écrite en langage C et nous ne pouvons pas ajouter dynamiquement une page à la volée comme dans les bibliothèques PHP.

Citate de votre commentaire: _ ​​{en fonction du nombre d’articles commandés ou du nombre de données client), chaque facture peut contenir de 1 à 3 pages.

Et à cause de cela, nous ne pouvons pas pré-calculer le nombre de pages et l'écrire dans une base de données.

Je pense que vous n’avez que une possibilité/solution: vous devez écrire derrière chaque facture une page vierge et, une fois l’ensemble PDF généré, vous devez le modifier avec la bibliothèque libre PHP comme FPDI. En combinaison avec FPDI, il est même possible de modifier des documents PDF.

En modifiant PDF, vous pouvez supprimer toutes les pages vierges dont vous n’avez pas besoin si elles commencent par un numéro de page impair (comme 3, 5, etc.). Et dans FPDI, vous avez la possibilité de détecter un numéro de page. C'est beaucoup plus rapide que la solution que vous utilisez maintenant.

Et les pages vides (ou vides) que vous pourriez détecter sur longueur du contenu avec FPFI sont les suivantes:

<?php
require('fpdf.php');
require_once('setasign/Fpdi/autoload.php');

class Pdf extends \setasign\Fpdi\Fpdi
{
    private $pdfReader;
    function Header()
    {
        if(is_null($this->pdfReader))
        {
            $readerId = $this->getPdfReaderId('blank-pages.pdf');
            $this->pdfReader = $this->getPdfReader($readerId);
        }

        $page_fpdi = $this->pdfReader->getPage($this->PageNo());
        $this->content = $page_fpdi->getContentStream();

        $this->Cell(0, 15, 'page content length: '.strlen($this->content));
    }

    protected function _putimages(){}
}

$pdf = new Pdf();
$pdf->SetFont('Arial', '', 12);
$pdf->AddPage(); //page content length: 70 // page with 'Hello World!' string
$pdf->AddPage(); //page content length: 30 // empty page
$pdf->AddPage(); //page content length: 30 // empty page
$pdf->Output();
?>

Mon blank-pages.pdf j'ai généré en utilisant FPDF avec le code suivant:

<?php
require('fpdf.php');

$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial','B',16);
$pdf->Cell(40,10,'Hello World!');
$pdf->AddPage();
$pdf->AddPage();
$pdf->Output();
?>
2
Bharata