Code snippets for symfony 1.x

Navigation

TCPDF filter to transform pages to PDF

[DEPRECATED SEE COMMENTS]

{-- DEPRECATED SEE COMMENTS --}

---DEPRECATED SEE COMMENTS---

This basic filter can transform pages into pdf using the sfTCPDFPlugin. You can also save the generated PDF to the database using the sfPropelFileStoragePlugin.

this is the first version so please post comments if you like it or use it !

Create a file in apps/myapp/lib/pdfFilter.class.php

<?php
/**
 * Filter for redirecting to PDF for the pages that need it
 *
 * @author Laurent Marchal
 * @version 1
 */
 
class pdfFilter extends sfFilter
{
    /**
     * Execute filter
     *
     * @param FilterChain $filterChain The symfony filter chain
     */
 
    public function execute ($filterChain)
    {
 
        if ($this->getContext()->getActionName() == 'pdf')
        {
        $pdf = $this->initPDF();
 
        // Next filter
        $filterChain->execute();
 
        $rawpdf = $this->writePDF($pdf, $this->getContext()->getResponse()->getContent());
 
        $pdf->Output(); //shows the pdf to the user
        //$this->savePdfToDatabase($rawpdf);
        return;
        }
        $filterChain->execute();
    }
 
    /**
     * initialyse the TCPDF object, must be instanciated before
     * filterChain->execute() in order to analyse the request.
     *
     */
    protected function initPDF()
    {
        //create new PDF document
        $pdf = new sfTCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true);
        $pdf->SetFont("FreeSerif", "", 12);
        return $pdf;
    }
 
    /**
     * write the contents to the PDF generally
     * getContext()->getResponse()->getContent()
     *
     *@param sfTCPDF $pdf the TCPDF object created with function initPDF()
     *@param stream $html_content the contents to transform to PDF
     */
    protected function writePDF($pdf, $html_content)
    {
        $pdf->AddPage();
        $pdf->writeHTML($html_content, true, 0);
        //$this->logMessage('ficheduActions::createPDF '.$html_content , 'info');
        //$document = $pdf->Output('test.pdf', 's');
        return $pdf->Output('test.pdf', 's');
    }
 
    /**
     * Save the PDF in the database using sfPropelFileStoragePlugin
     *
     *@param stream $pdf_content output of function writePDF()
     */
    protected function  savePdfToDatabase($pdf_content)
    {
        //File Info
      $file_info = new sfPropelFileStorageInfo();
      $file_info->setName('DU_Entreprise_v'.$fichedu->getFdVersion().'.pdf');
      $file_info->setSize(null);
      $file_info->setMimeType('application/pdf');
        //File Data
      $file_data = new sfPropelFileStorageData();
      $file_data->setBinaryData($pdf_content);
      $file_info->addsfPropelFileStorageData($file_data);
      $file_info->save();
    }
 
}

and in apps/myapp/config/filters.yml

rendering: ~
web_debug: ~
security:  ~
 
pdfFilter:
    class:  pdfFilter
 
cache:     ~
common:    ~
flash:     ~
execution: ~

then in your module,you will have to create this function in the action.class.php :

  public function executePdf()
  {
    $this->renderPDF = true;
    //use a special layout to clean the pdf
    //remove if you want your entire layout to be generated into pdf
    $this->setLayout ('pdf');
    //the show action generally fit
    $this->setTemplate ('show');
  }

then go to http://mywebsite/yourmodule/pdf to generate the pdf

H@ve Fun !

by Laurent Marchal on 2007-05-07, tagged database  filter  pdf  plugin  sfpropelfilestorageplugin  sftcpdfplugin  tcpdf 

Comments on this snippet

gravatar icon
#1 Jan Markmann on 2007-05-08 at 11:57

Maybe that one would better be implemented as a view that extends sfPHPView. Overloading the public method sfPHPView::render($templateVars = null) you could easily wrap the original rendered presentation into a context that generates a pdf from it. Storing the generated PDF might be better done with the native caching system. Using native caching PDF-output would be handled like regular output. Making it depend on configuration what and how it is cached. That would also include the possibility to use a DB-based cache but as well file system based caching. That approach would fit a little more into a symfony stile architecture. Since there is already a filter in the default chain that handles rendering, whos underlaying mechanisms can be extended to archive the goal of PDF-rendering but with keeping the regular rendering features. That all together released as a plugin like sfSmartyViewPlugin would be a great solution. I'm not quite sure what would be the best way to specify a view-class at action level, since in the sfSmartyViewPlugin-wikipage I only found a config for whole apps or projects, but that should be solveable, so you could make a own action for the something to be shown as pdf or let a action decide based in params or other condition whether to render html only or use the TCPDFView to convert that to PDF.

After a quick view inside sfMailView hiere is a pseudocode approach: (most comments striped, if using sfMailView as a start you should keep those comments.)

<?php
class sfTCPDFView extends sfPHPView
{
  public function getEngine()
  {
    return 'sfTCPDF';
  }
 
  public function configure()
  {
    parent::configure();
 
    // load some config values for this view
    $moduleName = $this->moduleName;
    require(sfConfigCache::getInstance()->checkConfig(sfConfig::get('sf_app_module_dir_name').'/'.$this->moduleName.'/'.sfConfig::get('sf_app_module_config_dir_name').'/tcpdf.yml'));
 
  }
 
  protected function initPDF()
  {
    //the used values should go into config
    $pdf = new sfTCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true);
    $pdf->SetFont("FreeSerif", "", 12);
    return $pdf;
  }
 
  public function render($templateVars = null)
  {
    $retval = parent::render($templateVars);
 
    $pdf = $this->initPDF();
 
    $pdf->AddPage();
    $pdf->writeHTML($retval, true, 0);
 
    return $pdf->Output('foo.pdf', 's');
  }
}

Spend some time with the execution, rendering and cache filter so see how caching entire pages if configured so is done to see if you need to add something for that or is already done when using the TCPDFView. Caching of subparts in PDF is no possible unless one combines TCPDF and FPDI (TCPDF is based on FPDF not FPDI right?), but that would be some more complex extending and reassembling.

Looking forward to see a sfTCPDFViewPlugin released soon =)

Is that enough of fresh ideas from someone who likes the idea? =)

gravatar icon
#2 Laurent Marchal on 2007-05-14 at 10:50

Thanks fo this very nice solution Jan Markmann ! I will create a plugin if i think my code is mature enough at the end of my project.

Here is the modified working code for the view You need to create a file called apps/myproject/lib/view/sfTCPDFView.class.php and in the action.class.php set the view class to create the pdf.

Example:

  public function executePdf()
  {
    $this->setViewClass('sfTCPDF');
    $this->setLayout (false);
  }

sfTCPDFView.class.php

<?php
class sfTCPDFView extends sfPHPView
{
 
  public function getEngine()
  {
    return 'sfTCPDF';
  }
 
  public function configure()
  {
    parent::configure();
 
    // load some config values for this view
    //$moduleName = $this->moduleName;
    //require(sfConfigCache::getInstance()->checkConfig(sfConfig::get('sf_app_module_dir_name').'/'.$this->moduleName.'/'.sfConfig::get('sf_app_module_config_dir_name').'/tcpdf.yml'));
 
  }
 
  protected function initPDF()
  {
    if (sfConfig::get('sf_logging_enabled'))
    {
      $this->getContext()->getLogger()->info('{sfTCPDFView} initPDF');
    }
    //the used values should go into config
    $pdf = new sfTCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true);
    $pdf->SetFont("FreeSerif", "", 12);
    return $pdf;
  }
 
  public function render($templateVars = null)
  {
    $template = $this->getDirectory().'/'.$this->getTemplate();
    $context = $this->getContext();
    $response = $context->getResponse();
 
    if (sfConfig::get('sf_logging_enabled'))
    {
      $context->getLogger()->info('{sfTCPDFView} render "'.$template.'"');
    }
 
    //backup render mode
    $renderMode = $context->getController()->getRenderMode();
    // override render mode : We dont want sfPHPView to render directly to the client
    $context->getController()->setRenderMode(sfView::RENDER_VAR);
    //let sfPHPView render our template to $retval
    $retval = parent::render($templateVars);
    //saved render mode
    $context->getController()->setRenderMode($renderMode);
 
    $pdf = $this->initPDF();
    $pdf->AddPage();
    $pdf->writeHTML($retval, true, 0);
    $pdfcontent = $pdf->Output('foo.pdf', 's');
 
    $response->setContentType('application/pdf');
 
    // render to client
    if ($renderMode == sfView::RENDER_CLIENT)
    {
        $response->setContent($pdfcontent);
    }
    return $pdfcontent;
  }
}
gravatar icon
#3 Jevon Wright on 2008-04-03 at 09:25

Here's a version of render() that allows you to directly return the PDF as a string, without setting the response content type etc. It also disables any layout you'd normally have.

<?php public function render($templateVars = null) { $template = $this->getDirectory().'/'.$this->getTemplate(); $context = $this->getContext(); $response = $context->getResponse();

if (sfConfig::get(&#039;sf_logging_enabled&#039;))
{
  $context-&gt;getLogger()-&gt;info(&#039;{sfPdfView} render &quot;&#039;.$template.&#039;&quot;&#039;);
}
 
// disable layout
$response-&gt;setParameter($this-&gt;moduleName.&#039;_&#039;.$this-&gt;actionName.&#039;_layout&#039;, null);
 
// backup render mode
$renderMode = $context-&gt;getController()-&gt;getRenderMode();
 
// override render mode : We dont want sfPHPView to render directly to the client
$context-&gt;getController()-&gt;setRenderMode(sfView::RENDER_VAR);
 
// let sfPHPView render our template to $retval
$retval = parent::render($templateVars);
 
// saved render mode
$context-&gt;getController()-&gt;setRenderMode($renderMode);
 
// get PDF data
$pdf = $this-&gt;initPDF();
$pdf-&gt;AddPage();
$pdf-&gt;writeHTML($retval, true, 0);
$pdfcontent = $pdf-&gt;Output(&#039;nowhere.pdf&#039;, &#039;s&#039;);          // get output as string
 
// $response-&gt;setContentType(&#039;application/pdf&#039;);
 
// render to client
// if ($renderMode == sfView::RENDER_CLIENT)
// {
//      $response-&gt;setContent($pdfcontent);
// }
return $pdfcontent;
 

} ?>