FPDI/FPDF: Watermark and Print Multiple Pages
Asked Answered
O

3

5

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.

Ordeal answered 6/5, 2012 at 5:56 Comment(4)
@vascowhite Any Idea on how to tackle this?Ordeal
Not really, but I'd start by checking what is being inserted. It looks like the whole page rotated. Sorry, but I don't have much experience with fpdf. I have used it, but only for very basic stuff. If I have a chance to play with it later today I'll see what I can find. Good luck.Extravasate
Have you seen this? setasign.de/products/pdf-php-solutions/setapdf-stamperExtravasate
@MrA I have posted in github. Could you have please look this chinmay235.github.io/php-pdf-watermarkJinx
E
8

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.

Extravasate answered 6/5, 2012 at 12:40 Comment(4)
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.phpOrdeal
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.pdfExtravasate
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 #13069604Adust
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);Formation
D
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!

Dreher answered 7/9, 2014 at 1:9 Comment(4)
Helped me. You are a life saver!Epigene
Helped me. You are a life saver! ² =)Corked
Great help! Although I needed to modify the fontFamily usage, had to remove SetPrintHeader() and SetPrintFooter() because they are no longer included in TCPDF.Boating
@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);Boating
B
0

In case anyone wants to use FPDF AND create the pdf on the fly without having to load it from a file here's how I did it. You will instantiate your PDF object with this class instead of the FPDF class. This should be run AFTER the pdf is created in memory but BEFORE it is output:

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

class PDF_Rotate extends FPDF
{
    var $angle=0;
    public function ApplyWaterMarkToAllPages() {
        for($i = 0; $i < count($this->pages); $i++) {
            $this->page = $i;
            $this->ApplyWaterMark();
        }
    }

    private function ApplyWaterMark()
    {
        //Put the watermark
        $this->SetFont('Arial','B',60);
        $this->SetTextColor(255,192,203);
        $this->RotatedText(15,280,'D O N O T F I L E - S A M P L E ',50);
        $this->SetTextColor(0,0,0);
    }

    private function RotatedText($x, $y, $txt, $angle)
    {
        //Text rotated around its origin
        $this->rotate($angle,$x,$y);
        $this->Text($x,$y,$txt);
        $this->Rotate(0);
    }

    private 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 had a class called Document that I put everything in for abstraction. Here is the output function from it. I created a class level variable called "applyWaterMark" to control whether or not the watermark is applied to the document.

  protected function OutputDocument() {
    if($this->applyWaterMark) {
      $this->pdf->ApplyWaterMarkToAllPages();
    }    
    $this->pdf->Output();
  }
Brost answered 31/3, 2020 at 0:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.