Skip to main content

PDF Generation with Apache FOP


I wrote a small PHP class for generation of PDF document using XML,XSL-FO and Apache FOP Generation.


Here is a process outline:

  1. Install Apache FOP on your server;

  2. Edit the relevant path to the executible in the declaration part of the class;

  3. Create XML string;

  4. Create XSL file to convert the XML string to XSL-FO;

  5. Create instance of XMLPDFGeneration class

  6. Set debug flag, fo file output directory and pdf file output directory

  7. Call XMLPDFExecute() function;

  8. You are done - the PDF file is generated;

Here is the code:


<?
class XMLPDFGeneration{

private $fop_command="/usr/local/src/apache_fop/fop-0.20.5/fop.sh";

private $debug_pdf = TRUE;
private $debug_file="/var/www/documents/pdfgen/pdfcall.txt";

private $fo_file_prefix="FO_";
private $fo_output_dir="/tmp";

private $pdf_file_prefix="CA_";
private $pdf_output_dir="/var/www/documents/pdfgen/";

private $error_redirect_uri="http://www.google.com";

function __construct(){

}

function SetXMLString($xml_string){
 $this->xml_string=$xml_string;
}

function SetXSLTFile($xslt_file){
 $this->xslt_file=$xslt_file;
}

function GetXSLTFile(){
 return $this->xslt_file;
}

function SetPDFOutputDirectory($pdf_output_dir){
 $this->pdf_output_dir=$pdf_output_dir;
}

function SetPDFFilePrefix($pdf_file_prefix){
 $this->pdf_file_prefix=$pdf_file_prefix;
}

function SetFOFilePrefix($fo_file_prefix){
 $this->fo_file_prefix=$fo_file_prefix;
}

function SetPDFDebug($bool_debug_pdf){
 $this->debug_pdf=$debug_pdf;
}

function SetPDFDebugFile($debug_file){
 $this->debug_file=$debug_file;
}

function SetErrorRedirectURI($error_redirect_uri){
 $this->error_redirect_uri=$error_redirect_uri;
}

function SetFOPCommand($fop_command){
 $this->fop_command=$fop_command;
}

function XMLPDFExecute(){

  // create temporary file
  $fo_file=$this->CreateTempFOFile();

  // chek if file created
  if(strlen($fo_file) > 0){
 
   // perform XSLT transformation to generate fo file
   $xsl_transformation=$this->XML2XSLFOTransformation($fo_file);
 
     // check for successful XSLT transformation
     if ($xsl_transformation != "0"){

        // assign output pdf file location and file prefix
          $output_file = tempnam($this->pdf_output_dir, $this->pdf_file_prefix);
           
        // build command string
         $command_string=$this->fop_command . " ".$fo_file." ".$output_file;
       
        // if in debug mode print some debugging info into a file
         if($this->debug_pdf){ $command_string.=" >> ".$this->debug_file; }  
       
        // record in error log  
         if($this->debug_pdf){ error_log("About to exec: '" . $command_string . "'"); }
         
                // unlink($_SERVER['DOCUMENT_ROOT']."pdfgen/" . $tmpfile);
         
        // perform fo to pdf conversion; Save the result of the conversion in variable for later use
         $execresult = exec($command_string, $output_string, $result);
       
        // record in error log  
         if($this->debug_pdf){ error_log("Fop Execution Result: '" . print_r($result) . "'"); }
       
        // rename output file name to have pdf extension
         $realoutput = $output_file.".pdf";
         $mv_result=exec("mv ".$output_file." ".$realoutput, $output_string, $rename_res);
         $output_file = $realoutput;
       
        // record in error log  
         if($this->debug_pdf){
          error_log("Rename Result: '" . $rename_res . "'");
          error_log("New Output File Name: '" . $output_file . "'");
         }
       
         // check if file created and written successfully
         if (file_exists($output_file)) {
       
           // strip the /var/www/ from the output file name  
           $output_url=substr($output_file, 8);
         
           // record in error log  
           if($this->debug_pdf){ error_log("Output URL: '" . $output_url . "' ".$output_file); }
         
           // redirect to the output file location
           header("Location: ".$output_url);
 
         // the file was not created successfully
         }else{
           // record in error log  
           if($this->debug_pdf){ error_log("PDF Output file not found ($output_file)"); }
           header("Location: ".$this->error_redirect_uri);
         }
 
     // the xslt transformation not sucessful
     }else{
     // record in error log  
      if($this->debug_pdf){
         error_log("XSLT transformation not sucessful'". $xsl_transformation ."'");
      }
      header("Location: ".$this->error_redirect_uri);
      return 0;
    }
   
  // fo file not created
  }else{
  // record in error log  
   if($this->debug_pdf){ error_log("FO file not created '". $xsl_transformation ."'"); }
   header("Location: ".$this->error_redirect_uri);
   return 0;
 }

 // return to the calling function
 return 1;

} // end of function


function CreateTempFOFile(){
// assign output fo file location and fo file prefix
    $fo_file = tempnam($this->fo_output_dir, $this->fo_file_prefix);

   if($this->debug_pdf){
  error_log("FO file name and location: '" . $fo_file . "'");
  error_log("Does the FO file exists: '" . is_file($fo_file) . "'");
  }  

// rename fo file name to have fo extension
    $realoutput = $fo_file.".fo";
    $mv_result=exec("mv ".$fo_file." ".$realoutput, $output_string, $rename_res);

   if($this->debug_pdf){  error_log("Result of the renaming of the FO file: '" . $rename_res . "'"); }  

    $fo_file = $realoutput;

   if($this->debug_pdf){ error_log("Renamed file exists: '" . is_file($fo_file) . "'"); }  
 
 return $fo_file;
}



function XML2XSLFOTransformation($fo_file){
libxml_use_internal_errors(true);

// Load the XML source
  $xml = new DOMDocument;
  $xml->loadXML($this->xml_string);
 
// Load the XSL source
  $xsl = new DOMDocument;
  $xsl->load($this->xslt_file);
 
// Configure the xslt processor
  $proc = new XSLTProcessor;
  $proc->importStyleSheet($xsl); // attach the xsl rules
 
// create array to hold the xslt transformation parameters
  $params=array();
 
// assign some xslt parameters
  $params["sectionid"]="HR";
 
// set parameters
  foreach ($params as $param => $value) { $proc->setParameter("", $param, $value); }

  // perform XSLT transformation to generate fo file
  $xsl_transformation=$proc->transformToURI($xml, $fo_file);
 
  if (!$xsl_transformation) {
     $errors = libxml_get_errors();
 
     foreach ($errors as $error) {
      error_log("XML Error: '" . $this->display_xml_error($error). "'");    
     }
 
     libxml_clear_errors();
  }
 
  return $xsl_transformation;
}

function display_xml_error($error)
{
   $return  = $xml[$error->line - 1] . "\n";
   $return .= str_repeat('-', $error->column) . "^\n";

   switch ($error->level) {
       case LIBXML_ERR_WARNING:
           $return .= "Warning $error->code: ";
           break;
         case LIBXML_ERR_ERROR:
           $return .= "Error $error->code: ";
           break;
       case LIBXML_ERR_FATAL:
           $return .= "Fatal Error $error->code: ";
           break;
   }

   $return .= trim($error->message) .
               "\n  Line: $error->line" .
               "\n  Column: $error->column";

   if ($error->file) {
       $return .= "\n  File: $error->file";
   }

   return "$return\n\n--------------------------------------------\n\n";
}


} // end of class declaration

?>

Comments

Popular posts from this blog

Enable pgpass under Windows 7

Last year I have written a post about enabling pgpass under Windows XP . Today I repeated the procedure for Windows 7. Basically everything worked as before, with the following exception - the APPDATA directory in Win 7 is different. What I did first was to check the name of the user running PostgreSQL server process. Then log in as that user and find out where is the APPDATA directory for that user. echo %APPDATA% From that point on, just follow the instructions in the previous post. Good luck!

Numeric Drop-down With PHP ranges

Here is an easy way to generate form numeric drop-down with ranges. $form = new Zend_Form(); $ranges = array( range(1, 20, 1) , range(30, 100, 10) , range(200, 1000, 100) ); $a = array(); foreach ( $ranges as $range ) { foreach ( $range as $r ) { $a[$r] = $r; } } $form->addElement('select', 'quantity', array( 'label' => 'Quantity' , 'required' => false , 'class' => 'form-control' , 'multiOptions' => array( '' => '--Select--' ) + $a )); Please note that the ranges are with different steps. Here is the result: