php FPDI/FPDF:水印和打印多页

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10468478/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 22:15:06  来源:igfitidea点击:

FPDI/FPDF: Watermark and Print Multiple Pages

phpfpdffpdi

提问by Mr A

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

我修改了这个堆栈问题:当用户尝试下载文件时在 pdf 文件上应用水印,但我遇到了一个错误,虽然有一条评论说如何修复它,但它不够详细。

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

当我加载超过 2 个页面的 pdf 时,所有页面都合并在一个页面中。我在这篇文章中附上了图片。在此处输入图片说明

Thanks.

谢谢。

回答by vascowhite

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

我发现该脚本有一些问题。要使其正常工作,请将doWatermark()方法更改为:-

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.

我将这条线$this->pdf->addPage();移到循环中,否则所有内容都会输出到一页上。我还添加$this->_rotate(0);了在保存之前将文档恢复直立的功能。真的很简单。我已经为您评论了更改后的行。

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

我在 32 页的 pdf 上测试了它,它似乎工作正常。

回答by yogibear

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).

这篇文章对我开始工作有很大帮助。但我很快发现 FPDF 有一些这里的一些人一直在经历的陷阱。对我来说,我还注意到水印仅显示在某些浏览器实例的第一页上,并且通过 adobe acrobat (Acrobat X Pro) 打开它。

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

相反,我转而使用 TCPDF,它解决了各种问题,包括:

  • 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).

要使用自定义字体,只需取消注释下面的自定义字体块 ( 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.

另外,最后要注意的是,标准的 FPDI 包仅支持 PDF 1.4 版。因此,如果您导入任何高于此的 PDF,导入将无法工作并失败。您需要购买商业版本 ( https://www.setasign.com/products/fpdi-pdf-parser/details/),或者将您的 PDF 保存在 1.4 版,这就是我们所做的。

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!

无论如何,希望有一天这会对某人有所帮助!

回答by Joseph Hamilton

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:

如果有人想使用 FPDF 并动态创建 pdf 而不必从文件中加载它,我就是这样做的。您将使用此类而不是 FPDF 类来实例化您的 PDF 对象。这应该在 pdf 在内存中创建之后运行,但在输出之前:

<?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.

我有一个叫做 Document 的类,我把所有东西都放在了抽象中。这是它的输出函数。我创建了一个名为“applyWaterMark”的类级别变量来控制是否将水印应用于文档。

  protected function OutputDocument() {
    if($this->applyWaterMark) {
      $this->pdf->ApplyWaterMarkToAllPages();
    }    
    $this->pdf->Output();
  }