FPDI/FPDF: Watermark and Print Multiple Pages

23,851

Solution 1

I have found a couple of things wrong with that script. To get it working change the doWatermark() method to this:-

public function doWaterMark()
{
    $currentFile = $this->file;
    $newFile = $this->newFile;

    $pagecount = $this->pdf->setSourceFile($currentFile);

    for($i = 1; $i <= $pagecount; $i++){
        $this->pdf->addPage();//<- moved from outside loop
        $tplidx = $this->pdf->importPage($i);
        $this->pdf->useTemplate($tplidx, 10, 10, 100);
        // now write some text above the imported page
        $this->pdf->SetFont('Arial', 'I', 40);
        $this->pdf->SetTextColor(255,0,0);
        $this->pdf->SetXY(25, 135);
        $this->_rotate(55);
        $this->pdf->Write(0, $this->wmText);
        $this->_rotate(0);//<-added
    }

    $this->pdf->Output($newFile, 'F');
}

I moved the line $this->pdf->addPage(); into the loop, as otherwise everything is output onto one page. I also added $this->_rotate(0); to bring the document back upright before saving it out. Pretty simple really. I have commented the changed lines for you.

I tested it on a 32 page pdf and it seemed to work fine.

Solution 2

This post was a great help to me in getting things started. But I quickly found that FPDF had some pitfalls a few people here have been experiencing. For me, I also noticed that the watermark only displays on the first page in some browser instances as well as opening it up through adobe acrobat (Acrobat X Pro).

Instead, I switched to using TCPDF, which solved a variety of problems including:

  • No errors when setting angles
  • Having ability to set transparency
  • Custom fonts
  • Updated Feature: Updating the text

To use the custom fonts, just uncomment the custom font block below (http://www.tcpdf.org/fonts.php).

Also, final note, the standard FPDI package only supports PDF version 1.4. So if your importing any PDF's that are above that, the import will not work and blow up. You will need to buy a commercial version (https://www.setasign.com/products/fpdi-pdf-parser/details/), or just save your PDFs at version 1.4, thats what we did.

Here's my updated code:

require_once(APPPATH . 'third_party/tcpdf/tcpdf.php');
require_once(APPPATH . 'third_party/fpdi/fpdi.php');

class WatermarkerTCPDF extends FPDI {
    public $pdf, $file, $newFile,
            $wmText = "STACKOVERFLOW",
            $fontsize = 24,
            $fontfamily = 'ptsansnarrow400';

    /** $file and $newFile have to include the full path. */
    public function __construct($file = null, $newFile = null) {
        $this->pdf = new FPDI();
        //custom fonts
        //$this->fontfamily = $this->pdf->addTTFfont(APPPATH . 'third_party/tcpdf/ttf/ptsansnarrow400.ttf', 'TrueTypeUnicode', '');
        if (!empty($file)) {
            $this->file = $file;
        }
        if (!empty($newFile)) {
            $this->newFile = $newFile;
        }
    }

    /** $file and $newFile have to include the full path. */
    public static function applyAndSpit($file, $newFile = null) {
        $wm = new Watermarker($file, $newFile);

        if ($wm->isWaterMarked())
            return $wm->spitWaterMarked();
        else {
            $wm->doWaterMark();
            return $wm->spitWaterMarked();
        }
    }

    /** @todo Make the text nicer and add to all pages */
    public function doWaterMark() {
        $currentFile = $this->file;
        $newFile = $this->newFile;

        $pagecount = $this->pdf->setSourceFile($currentFile);

        for ($i = 1; $i <= $pagecount; $i++) {
            $tplidx = $this->pdf->importPage($i);
            $specs = $this->pdf->getTemplateSize($tplidx);
            $this->pdf->SetPrintHeader(false);
            $this->pdf->SetPrintFooter(false);
            $this->pdf->addPage($specs['h'] > $specs['w'] ? 'P' : 'L');
            $this->pdf->useTemplate($tplidx, null, null, 0, 0, true);

            // now write some text above the imported page
            $this->pdf->SetFont($this->fontfamily, '', $this->fontsize);
            $this->pdf->SetTextColor(204, 204, 204);
            //$this->pdf->SetXY($specs['w']/2, $specs['h']/2);
            $_x = ($specs['w']/2) - ($this->pdf->GetStringWidth($this->wmText, $this->fontfamily, '', $this->fontsize)/2.8);
            $_y = $specs['h']/2;
            $this->pdf->SetXY($_x, $_y);
            //$this->pdf->SetXY(0, 0);
            $this->pdf->setAlpha(0.3);
            $this->_rotate(45, 100, 100);
            $this->pdf->Write(0, $this->wmText);
            //$this->pdf->writeHTMLCell($w=0, $h=0, $x='', $y='', $this->wmText, $border=0, $ln=1, $fill=0, $reseth=true, $align='', $autopadding=true);
        }

        if (empty($newFile)) {
            header('Content-Type: application/pdf');
            $this->pdf->Output();
        } else {
            $this->pdf->Output($newFile, 'F');
        }
    }

    public function isWaterMarked() {
        //return (file_exists($this->newFile));
        $_file = $this->newFile;
        $file = file_get_contents($_file);
        force_download($file);
    }

    public function spitWaterMarked() {
        $_file = $this->newFile;
        $file = file_get_contents($_file);
        force_download($file);
        //return readfile($this->newFile);
    }

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

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

            $this->pdf->_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));
        }
    }

    public function wmText($text = null)
    {
        $total = 20;
        if (!empty($text)) {
            $this->wmText = '';
            for ($i = 0; $i < $total; $i++) {
                $this->wmText .= ' ' . $text;
            }

        }

        return $this;
    }
}

To use this, just:

try {
    //this is for CodeIgniter
    $this->load->library('WatermarkerTCPDF');

    //if your using it as a standard class in vanilla PHP just do:
    //require_once('PATH_TO_LIBRARY/WatermarkerPDF.php');

    //If you want to output the PDF to another file, you can supply
    //a second parameter: new WatermarkerTCPDF($file_path, $new_file_path);
    //just remember, the full path is required
    $watermark = new WatermarkerTCPDF($file_path);
    $watermark->wmText($this->session->userdata('email'));
    $watermark->doWaterMark();
} catch (Exception $e) {
    exit($e->getMessage());
}

Anyway, hope this will help someone someday!

Share:
23,851
Mr A
Author by

Mr A

Updated on March 31, 2020

Comments

  • Mr A
    Mr A about 4 years

    I modified this stack question: Applying watermarks on pdf files when users try to download the files but I encountered an error, though there was a comment that says on how to fix it, it wasn't elaborate enough.

    Here is the code:

    require_once('fpdf/fpdf.php');
    require_once('fpdi/fpdi.php');
    
    class WaterMark
    
    {
        public $pdf, $file, $newFile,
            $wmText = "STACKOVERFLOW";
    
    /** $file and $newFile have to include the full path. */
    public function __construct($file, $newFile)
    {
        $this->pdf = new FPDI();
        $this->file = $file;
        $this->newFile = $newFile;
    }
    
    /** $file and $newFile have to include the full path. */
    public static function applyAndSpit($file, $newFile)
    {
        $wm = new WaterMark($file, $newFile);
    
        if($wm->isWaterMarked())
            return $wm->spitWaterMarked();
        else{
            $wm->doWaterMark();
            return $wm->spitWaterMarked();
        }
    }
    
    /** @todo Make the text nicer and add to all pages */
    public function doWaterMark()
    {
        $currentFile = $this->file;
        $newFile = $this->newFile;
    
        $this->pdf->addPage();
        $pagecount = $this->pdf->setSourceFile($currentFile);
    
        for($i = 1; $i <= $pagecount; $i++){
            $tplidx = $this->pdf->importPage($i);
            $this->pdf->useTemplate($tplidx, 10, 10, 100);
            // now write some text above the imported page
            $this->pdf->SetFont('Arial', 'I', 40);
            $this->pdf->SetTextColor(255,0,0);
            $this->pdf->SetXY(25, 135);
            $this->_rotate(55);
            $this->pdf->Write(0, $this->wmText);
        }
    
        $this->pdf->Output($newFile, 'F');
    }
    
    public function isWaterMarked()
    {
        return (file_exists($this->newFile));
    }
    
    public function spitWaterMarked()
    {
        return readfile($this->newFile);
    }
    
    protected function _rotate($angle,$x=-1,$y=-1) {
    
        if($x==-1)
            $x=$this->pdf->x;
        if($y==-1)
            $y=$this->pdf->y;
        if($this->pdf->angle!=0)
            $this->pdf->_out('Q');
        $this->pdf->angle=$angle;
    
        if($angle!=0){
            $angle*=M_PI/180;
            $c=cos($angle);
            $s=sin($angle);
            $cx=$x*$this->pdf->k;
            $cy=($this->pdf->h-$y)*$this->pdf->k;
    
            $this->pdf->_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));
        }
        } 
    
    }
    header('Content-type: application/pdf');
    //header('Content-Disposition: attachment; filename="downloaded.pdf"');
    WaterMark::applyAndSpit('C:\xampp\htdocs\tst\test0.pdf','C:\xampp\htdocs\tst\output0.pdf');
    

    When I load a pdf that has more than 2 all of the pages merges in one page. I attached the image in this post. enter image description here

    Thanks.

  • Mr A
    Mr A about 12 years
    Any idea on how to set the transparency? I got changed the values that is similar to this but didn't got any result. fpdf.org/en/script/script9.php
  • vascowhite
    vascowhite about 12 years
    That's not transparent, just a pale colour and they've cheated by putting in the watermark first, then putting the page text on top of that. fpdf.org/en/script/watermark.pdf
  • Steve Smith
    Steve Smith over 11 years
    Has anyone not noticed that on acrobat reader for PC if the text is at an angle then the whole pdf bugs out. Does anyone have a workaround for this?See my question stackoverflow.com/questions/13069104/…
  • Iman
    Iman over 9 years
    Helped me. You are a life saver!
  • Elton da Costa
    Elton da Costa over 9 years
    Helped me. You are a life saver! ² =)
  • Aaron Marton
    Aaron Marton about 6 years
    Great help! Although I needed to modify the fontFamily usage, had to remove SetPrintHeader() and SetPrintFooter() because they are no longer included in TCPDF.
  • Aaron Marton
    Aaron Marton about 6 years
    @yogibear, useTemplate($tplidx, null, null, 0, 0, true); doesn't work with 0 values for width and height, so I had to pass the dynamically obtained width and height values, with "false" as the last argument to the useTemplate() method. The way it worked for me was: $this->pdf->useTemplate($tplidx, null, null, $specs['width'], $specs['height'], false);
  • Russell Mazonde
    Russell Mazonde over 5 years
    Not sure if anyone is still interested but I achieved the transparency effect i wanted using the following approach: $this->pdf->SetAlpha(0.1); $this->pdf->StartTransform(); $this->pdf->Rotate(45, $watermarkXCord, $watermarkYCord); $this->pdf->SetFont("", "", 120); $this->pdf->Text($watermarkXCord, $watermarkYCord, $this->watermarkAttribute); $this->pdf->StopTransform(); $this->pdf->SetAlpha(1);