Advertisement
Paulius

ODT-PHP Conditionals

Mar 31st, 2012
300
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 14.24 KB | None | 0 0
  1. <?php
  2. require 'zip/PclZipProxy.php';
  3. require 'zip/PhpZipProxy.php';
  4. require 'Segment.php';
  5. class OdfException extends Exception
  6. {}
  7. /**
  8.  * Templating class for odt file
  9.  * You need PHP 5.2 at least
  10.  * You need Zip Extension or PclZip library
  11.  * Encoding : ISO-8859-1
  12.  * Last commit by $Author: neveldo $
  13.  * Date - $Date: 2009-06-17 11:11:57 +0200 (mer., 17 juin 2009) $
  14.  * SVN Revision - $Rev: 42 $
  15.  * Id : $Id: odf.php 42 2009-06-17 09:11:57Z neveldo $
  16.  *
  17.  * @copyright  GPL License 2008 - Julien Pauli - Cyril PIERRE de GEYER - Anaska (http://www.anaska.com)
  18.  * @license    http://www.gnu.org/copyleft/gpl.html  GPL License
  19.  * @version 1.3
  20.  */
  21. class Odf
  22. {
  23.     protected $config = array(
  24.         'ZIP_PROXY' => 'PclZipProxy',
  25.         'DELIMITER_LEFT' => '{',
  26.         'DELIMITER_RIGHT' => '}',
  27.         'PATH_TO_TMP' => null
  28.     );
  29.     protected $file;
  30.     protected $contentXml;
  31.     protected $manifestXml;
  32.     protected $stylesXml;
  33.     protected $tmpfile;
  34.     protected $images = array();
  35.     protected $vars = array();
  36.     protected $manifestVars = array();
  37.     protected $styleVars = array();
  38.     protected $conditionals = array();
  39.     protected $segments = array();
  40.     const PIXEL_TO_CM = 0.026458333;
  41.     /**
  42.      * Class constructor
  43.      *
  44.      * @param string $filename the name of the odt file
  45.      * @throws OdfException
  46.      */
  47.     public function __construct($filename, $config = array())
  48.     {
  49.         if (! is_array($config)) {
  50.             throw new OdfException('Configuration data must be provided as array');
  51.         }
  52.         foreach ($config as $configKey => $configValue) {
  53.             if (array_key_exists($configKey, $this->config)) {
  54.                 $this->config[$configKey] = $configValue;
  55.             }
  56.         }
  57.         if (! class_exists($this->config['ZIP_PROXY'])) {
  58.             throw new OdfException($this->config['ZIP_PROXY'] . ' class not found - check your php settings');
  59.         }
  60.         $zipHandler = $this->config['ZIP_PROXY'];
  61.         $this->file = new $zipHandler();
  62.         if ($this->file->open($filename) !== true) {
  63.             throw new OdfException("Error while Opening the file '$filename' - Check your odt file");
  64.         }
  65.         if (($this->contentXml = $this->file->getFromName('content.xml')) === false) {
  66.             throw new OdfException("Nothing to parse - check that the content.xml file is correctly formed");
  67.         }
  68.         if (($this->stylesXml = $this->file->getFromName('styles.xml')) === false) {
  69.             throw new OdfException("Missing styles.xml file");
  70.         }
  71.         if (($this->manifestXml = $this->file->getFromName('META-INF/manifest.xml')) === false) {
  72.             throw new OdfException("Nothing to parse - check that the manifest.xml file is correctly formed");
  73.         }
  74.  
  75.         $this->file->close();
  76.        
  77.         $tmp = tempnam($this->config['PATH_TO_TMP'], md5(uniqid()));
  78.         copy($filename, $tmp);
  79.         $this->tmpfile = $tmp;
  80.         $this->_moveRowSegments();
  81.     }
  82.     /**
  83.      * Assing a template variable
  84.      *
  85.      * @param string $key name of the variable within the template
  86.      * @param string $value replacement value
  87.      * @param bool $encode if true, special XML characters are encoded
  88.      * @throws OdfException
  89.      * @return odf
  90.      */
  91.     public function setVars($key, $value, $encode = true, $charset = 'UTF-8')
  92.     {
  93.         if (strpos($this->contentXml, $this->config['DELIMITER_LEFT'] . $key . $this->config['DELIMITER_RIGHT']) === false) {
  94.             throw new OdfException("var $key not found in the document");
  95.         }
  96.         $value = $encode ? htmlspecialchars($value) : $value;
  97.         $value = ($charset == 'ISO-8859') ? utf8_encode($value) : $value;
  98.         $this->vars[$this->config['DELIMITER_LEFT'] . $key . $this->config['DELIMITER_RIGHT']] = str_replace("\n", "<text:line-break/>", $value);
  99.         return $this;
  100.     }
  101.     /**
  102.      * Assing a variable in styles.xml (mostly for heading)
  103.      *
  104.      * @param string $key name of the variable within the template
  105.      * @param string $value replacement value
  106.      * @param bool $encode if true, special XML characters are encoded
  107.      * @throws OdfException
  108.      * @return odf
  109.      */
  110.     public function setStyleVars($key, $value, $encode = true, $charset = 'UTF-8')
  111.     {
  112.         if (strpos($this->stylesXml, $this->config['DELIMITER_LEFT'] . $key . $this->config['DELIMITER_RIGHT']) === false) {
  113.             throw new OdfException("var $key not found in the document");
  114.         }
  115.         $value = $encode ? htmlspecialchars($value) : $value;
  116.         $value = ($charset == 'ISO-8859') ? utf8_encode($value) : $value;
  117.         $this->styleVars[$this->config['DELIMITER_LEFT'] . $key . $this->config['DELIMITER_RIGHT']] = str_replace("\n", "<text:line-break/>", $value);
  118.         return $this;
  119.     }
  120.     /**
  121.      * Adds a conditional. If $value is false, removes everything between [!-- IF <$tag> --] and [!-- ENDIF <$tag> --]
  122.      * @param string $tag Conditional name
  123.      * @param bool $value Value
  124.      */
  125.     public function setConditional($tag, $value)
  126.     {
  127.         $this->conditionals[$tag] = $value;
  128.     }
  129.     /**
  130.      * Assign a template variable as a picture
  131.      *
  132.      * @param string $key name of the variable within the template
  133.      * @param string $value path to the picture
  134.      * @throws OdfException
  135.      * @return odf
  136.      */
  137.     public function setImage($key, $value)
  138.     {
  139.         $filename = strtok(strrchr($value, '/'), '/.');
  140.         $file = substr(strrchr($value, '/'), 1);
  141.         $size = @getimagesize($value);
  142.         if ($size === false) {
  143.             throw new OdfException("Invalid image");
  144.         }
  145.         list ($width, $height) = $size;
  146.         $width *= self::PIXEL_TO_CM;
  147.         $height *= self::PIXEL_TO_CM;
  148.         $width = sprintf("%F", $width);
  149.         $height = sprintf("%F", $height);
  150.         $xml = <<<IMG
  151. <draw:frame draw:style-name="fr1" draw:name="$filename" text:anchor-type="aschar" svg:width="{$width}cm" svg:height="{$height}cm" draw:z-index="3"><draw:image xlink:href="Pictures/$file" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/></draw:frame>
  152. IMG;
  153.         $this->images[$value] = $file;
  154.         $this->manifestVars[] = $file;  //save image name as array element
  155.         $this->setVars($key, $xml, false);
  156.         return $this;
  157.     }
  158.     /**
  159.      * Move segment tags for lines of tables
  160.      * Called automatically within the constructor
  161.      *
  162.      * @return void
  163.      */    
  164.     private function _moveRowSegments()
  165.     {
  166.         // Search all possible rows in the document
  167.         $reg1 = "#<table:table-row[^>]*>(.*)</table:table-row>#smU";
  168.         preg_match_all($reg1, $this->contentXml, $matches);
  169.         for ($i = 0, $size = count($matches[0]); $i < $size; $i++) {
  170.             // Check if the current row contains a segment row.*
  171.             $reg2 = '#\[!--\sBEGIN\s(row.[\S]*)\s--\](.*)\[!--\sEND\s\\1\s--\]#sm';
  172.             if (preg_match($reg2, $matches[0][$i], $matches2)) {
  173.                 $balise = str_replace('row.', '', $matches2[1]);
  174.                 // Move segment tags around the row
  175.                 $replace = array(
  176.                     '[!-- BEGIN ' . $matches2[1] . ' --]'   => '',
  177.                     '[!-- END ' . $matches2[1] . ' --]'     => '',
  178.                     '<table:table-row'                          => '[!-- BEGIN ' . $balise . ' --]<table:table-row',
  179.                     '</table:table-row>'                        => '</table:table-row>[!-- END ' . $balise . ' --]'
  180.                 );
  181.                 $replacedXML = str_replace(array_keys($replace), array_values($replace), $matches[0][$i]);
  182.                 $this->contentXml = str_replace($matches[0][$i], $replacedXML, $this->contentXml);
  183.             }
  184.         }
  185.     }
  186.     /**
  187.      * Merge template variables
  188.      * Called automatically for a save
  189.      *
  190.      * @return void
  191.      */
  192.     private function _parse()
  193.     {
  194.         $this->contentXml = str_replace(array_keys($this->vars), array_values($this->vars), $this->contentXml);
  195.         $this->stylesXml = str_replace(array_keys($this->styleVars), array_values($this->styleVars), $this->stylesXml);
  196.         foreach($this->conditionals as $key => $value)
  197.         {
  198.             if($value)
  199.             {
  200.                 $this->contentXml = str_replace('[!-- IF '.$key.' --]', '', $this->contentXml);
  201.                 $this->contentXml = str_replace('[!-- ENDIF '.$key.' --]', '', $this->contentXml);
  202.             }
  203.             else
  204.             {
  205.                 $reg = '@\[!--\sIF\s' . $key . '\s--\](.*)\[!--.+ENDIF\s' . $key . '\s--\]@smU';
  206.                 $this->contentXml = preg_replace($reg, '', $this->contentXml);
  207.             }
  208.         }
  209.     }
  210.     /**
  211.      * Add the merged segment to the document
  212.      *
  213.      * @param Segment $segment
  214.      * @throws OdfException
  215.      * @return odf
  216.      */
  217.     public function mergeSegment(Segment $segment)
  218.     {
  219.         if (! array_key_exists($segment->getName(), $this->segments)) {
  220.             throw new OdfException($segment->getName() . 'cannot be parsed, has it been set yet ?');
  221.         }
  222.         $string = $segment->getName();
  223.         // $reg = '@<text:p[^>]*>\[!--\sBEGIN\s' . $string . '\s--\](.*)\[!--.+END\s' . $string . '\s--\]<\/text:p>@smU';
  224.         $reg = '@\[!--\sBEGIN\s' . $string . '\s--\](.*)\[!--.+END\s' . $string . '\s--\]@smU';
  225.         $this->contentXml = preg_replace($reg, $segment->getXmlParsed(), $this->contentXml);
  226.         foreach ($segment->manifestVars as $val)
  227.             $this->manifestVars[] = $val;   //copy all segment image names into current array
  228.         return $this;
  229.     }
  230.     /**
  231.      * Display all the current template variables
  232.      *
  233.      * @return string
  234.      */
  235.     public function printVars()
  236.     {
  237.         return print_r('<pre>' . print_r($this->vars, true) . '</pre>', true);
  238.     }
  239.     /**
  240.      * Display the XML content of the file from odt document
  241.      * as it is at the moment
  242.      *
  243.      * @return string
  244.      */
  245.     public function __toString()
  246.     {
  247.         return $this->contentXml;
  248.     }
  249.     /**
  250.      * Display loop segments declared with setSegment()
  251.      *
  252.      * @return string
  253.      */
  254.     public function printDeclaredSegments()
  255.     {
  256.         return '<pre>' . print_r(implode(' ', array_keys($this->segments)), true) . '</pre>';
  257.     }
  258.     /**
  259.      * Declare a segment in order to use it in a loop
  260.      *
  261.      * @param string $segment
  262.      * @throws OdfException
  263.      * @return Segment
  264.      */
  265.     public function setSegment($segment)
  266.     {
  267.         if (array_key_exists($segment, $this->segments)) {
  268.             return $this->segments[$segment];
  269.         }
  270.         // $reg = "#\[!--\sBEGIN\s$segment\s--\]<\/text:p>(.*)<text:p\s.*>\[!--\sEND\s$segment\s--\]#sm";
  271.         $reg = "#\[!--\sBEGIN\s$segment\s--\](.*)\[!--\sEND\s$segment\s--\]#sm";
  272.         if (preg_match($reg, html_entity_decode($this->contentXml), $m) == 0) {
  273.             throw new OdfException("'$segment' segment not found in the document");
  274.         }
  275.         $this->segments[$segment] = new Segment($segment, $m[1], $this);
  276.         return $this->segments[$segment];
  277.     }
  278.     /**
  279.      * Save the odt file on the disk
  280.      *
  281.      * @param string $file name of the desired file
  282.      * @throws OdfException
  283.      * @return void
  284.      */
  285.     public function saveToDisk($file = null)
  286.     {
  287.         if ($file !== null && is_string($file)) {
  288.             if (file_exists($file) && !(is_file($file) && is_writable($file))) {
  289.                 throw new OdfException('Permission denied : can\'t create ' . $file);
  290.             }
  291.             $this->_save();
  292.             copy($this->tmpfile, $file);    
  293.         } else {
  294.             $this->_save();
  295.         }
  296.     }
  297.     /**
  298.      * Internal save
  299.      *
  300.      * @throws OdfException
  301.      * @return void
  302.      */
  303.     private function _save()
  304.     {
  305.         $this->file->open($this->tmpfile);
  306.         $this->_parse();
  307.         if (! $this->file->addFromString('content.xml', $this->contentXml)) {
  308.             throw new OdfException('Error during file export');
  309.         }
  310.         if (! $this->file->addFromString('styles.xml', $this->stylesXml))
  311.             throw new OdfException('Error during file export');
  312.         $lastpos=strrpos($this->manifestXml, "\n", -15); //find second last newline in the manifest.xml file
  313.         $manifdata = "";  
  314.  
  315.         //Enter all images description in $manifdata variable
  316.  
  317.         foreach ($this->manifestVars as $val)
  318.         {
  319.             $manifdata = $manifdata.'<manifest:file-entry manifest:media-type="image/png" manifest:full-path="Pictures/'.$val.'"/>'."\n";
  320.         }
  321.         //Place content of $manifdata variable in manifest.xml file at appropriate place
  322.         $this->manifestXml = substr_replace($this->manifestXml, "\n".$manifdata, $lastpos+1, 0);
  323.  
  324.         if (! $this->file->addFromString('META-INF/manifest.xml', $this->manifestXml)) {
  325.             throw new OdfException('Error during manifest file export');
  326.         }
  327.         foreach ($this->images as $imageKey => $imageValue) {
  328.             $this->file->addFile($imageKey, 'Pictures/' . $imageValue);
  329.         }
  330.         $this->file->close(); // seems to bug on windows CLI sometimes
  331.     }
  332.     /**
  333.      * Export the file as attached file by HTTP
  334.      *
  335.      * @param string $name (optionnal)
  336.      * @throws OdfException
  337.      * @return void
  338.      */
  339.     public function exportAsAttachedFile($name="")
  340.     {
  341.         $this->_save();
  342.         if (headers_sent($filename, $linenum)) {
  343.             throw new OdfException("headers already sent ($filename at $linenum)");
  344.         }
  345.        
  346.         if( $name == "" )
  347.         {
  348.                 $name = md5(uniqid()) . ".odt";
  349.         }
  350.        
  351.         header('Content-type: application/vnd.oasis.opendocument.text');
  352.         header('Content-Disposition: attachment; filename="'.$name.'"');
  353.         readfile($this->tmpfile);
  354.     }
  355.     /**
  356.      * Returns a variable of configuration
  357.      *
  358.      * @return string The requested variable of configuration
  359.      */
  360.     public function getConfig($configKey)
  361.     {
  362.         if (array_key_exists($configKey, $this->config)) {
  363.             return $this->config[$configKey];
  364.         }
  365.         return false;
  366.     }
  367.     /**
  368.      * Returns the temporary working file
  369.      *
  370.      * @return string le chemin vers le fichier temporaire de travail
  371.      */
  372.     public function getTmpfile()
  373.     {
  374.         return $this->tmpfile;
  375.     }
  376.     /**
  377.      * Delete the temporary file when the object is destroyed
  378.      */    
  379.     public function __destruct() {
  380.           if (file_exists($this->tmpfile)) {
  381.             unlink($this->tmpfile);
  382.         }
  383.     }
  384. }
  385.  
  386. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement