Setasign / FPDI

FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF.
https://www.setasign.com/fpdi
MIT License
1.09k stars 159 forks source link

Added Text missing except on first page when viewing with some PDF software (incl. Acrobat) #187

Closed Maxtab closed 1 year ago

Maxtab commented 1 year ago

Hello and thank you for your tool which is very useful.

I notice a bug but I'm not sure if it concerns FPDI or Acrobat.

I'm generating some edits to an existing PDF using the following script which adds text to each page of a PDF.

/**
     * Ajoute des SKU à chaque page d'un PDF existant.
     *
     * Cette fonction prend en entrée une liste de SKU et un chemin de fichier PDF.
     * Elle parcourt chaque page du PDF et ajoute le SKU correspondant à une position spécifique.
     * Enfin, elle sauvegarde le PDF modifié en écrasant le fichier original.
     *
     * @param array  $skuList  Liste des SKU à ajouter. Chaque élément de la liste correspond à une page du PDF.
     * @param string $pdfPath  Chemin du fichier PDF qui sera modifié.
     * @param string $typeEtiquettes  Etiquettes GLS ou DHL
     * 
     *
     * @return string          Retourne le chemin du PDF modifié.
     *
     * @throws Exception       Lance une exception si le PDF ne peut pas être chargé ou modifié.
     *                         ou si le type d'étiquette n'est ni GLS ni DHL (valeur issue du formulaire, radio button)
     */
    private function addSKUtoPDF($skuList, $pdfPath, $typeEtiquettes)
    {
        // Charger le PDF existant
        $pdf = new PdfRotate();
        $pdf->setSourceFile($pdfPath);

        $numPages = $pdf->setSourceFile($pdfPath);

        if ($typeEtiquettes === "gls") {
            // Bloc de code pour le cas où "GLS" est sélectionné
            for ($pageNo = 1; $pageNo <= $numPages; $pageNo++) {
                $tplIdx = $pdf->importPage($pageNo);
                $size = $pdf->getTemplateSize($tplIdx);

                // Ajouter une page avec les dimensions du template importé
                $pdf->AddPage($size['width'] > $size['height'] ? 'L' : 'P', [$size['width'], $size['height']]);
                $pdf->useTemplate($tplIdx);

                $x = 68;
                $y = 90;
                $pdf->SetFont('Arial', '', 8);
                $pdf->Rotate(90, $x, $y);

                $newY = $y - 3;
                $pdf->Text($x, $newY, "[SNS]");
                // Diviser la chaîne en utilisant | comme séparateur
                $lines = explode('|', $skuList[$pageNo - 1]);

                foreach ($lines as $line) {
                    $pdf->Text($x, $y, trim($line)); // Utiliser trim() pour enlever les espaces en début et fin de ligne
                    $y += 3; // Augmenter la position y pour la prochaine ligne
                }
                $pdf->Rotate(0); // Remettre la rotation à zéro
            }
        } else {
            // Bloc de code pour le cas où aucune des options n'est sélectionnée ou une valeur inattendue est rencontrée
            throw new Exception("Type d'étiquette non valide");
        }

        // Sauvegarder le PDF modifié (écrase l'original)
        $pdf->Output($pdfPath, 'F');
        // Retourner le chemin du PDF enregistré comme message de succès
        return $pdfPath;
    }

By retrieving the transformed PDF, when I view it in a browser, I can clearly see the text on each page which has been added as expected. But on Acrobat (or even on Default document viewer on Ubuntu), I only see the text added on the first page and not the following ones, it's quite strange. Has anyone ever noticed this? Does this have anything to do with how the PDF is saved?

Versions used :

Example of file generated with this script : packing_slip_2572251_bis_HIOLLE_HIOLLE (1).pdf

There should be text similar to what is seen on the first page on each of the 7 pages. If you view this PDF in you browser such as Chrome or Firefow, you will see the text fine on the 7 pages, but on Acrobat or default Document viewer as I said, it does not display except for the first page (and does not print either)

Thank you for your help.

Maxtab commented 1 year ago

OK I managed to resolve this bug which does not seem to be linked to FPDI but rather FPDF. My function uses a rotation function which was constructed like this:

use setasign\Fpdi\Fpdi;

class PdfRotate extends Fpdi
{
    // Variable pour stocker l'angle de rotation
    private $angle = 0;

    public function rotate(float $angle, ?float $x = null, ?float $y = null): void
    {
        $x = $x ?? $this->x;
        $y = $y ?? $this->y;

        if ($this->angle !== 0) {
            $this->_out('Q');
        }

        $this->angle = $angle;

        if ($angle !== 0) {
            $angleRad = deg2rad($angle);
            $c = cos($angleRad);
            $s = sin($angleRad);
            $cx = $x * $this->k;
            $cy = ($this->h - $y) * $this->k;

            $this->_out(sprintf('q %.5F %.5F %.5F %.5F %.2F %.2F cm 1 0 0 1 %.2F %.2F cm', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy));
        }
    }

    protected function endPage(): void
    {
        if ($this->angle !== 0) {
            $this->angle = 0;
            $this->_out('Q');
        }

        parent::_endpage();
    }
}

After using the code found on the FPDF site (FPDF doc about Rotations), which is this:

use setasign\Fpdi\Fpdi;

class PdfRotate extends Fpdi
{
    var $angle=0;

    function Rotate($angle,$x=-1,$y=-1)
    {
        if($x==-1)
            $x=$this->x;
        if($y==-1)
            $y=$this->y;
        if($this->angle!=0)
            $this->_out('Q');
        $this->angle=$angle;

        if($angle!=0)
        {
            $angle*=M_PI/180;
            $c=cos($angle);
            $s=sin($angle);
            $cx=$x*$this->k;
            $cy=($this->h-$y)*$this->k;
            $this->_out(sprintf('q %.5F %.5F %.5F %.5F %.2F %.2F cm 1 0 0 1 %.2F %.2F cm',$c,$s,-$s,$c,$cx,$cy,-$cx,-$cy));
        }
    }

    function _endpage()
    {
        if($this->angle!=0)
        {
            $this->angle=0;
            $this->_out('Q');
        }
        parent::_endpage();
    }

}

I no longer encounter this bug at all. I didn't quite understand what changed the result, but it works. I'll leave this here if anyone has the same problem.