Manipulation de fichier OpenDocument - Suite

Administrateur PHPfrance
Administrateur PHPfrance | 658 Messages

15 janv. 2008, 13:41

Bonjour,

J'avais posté une ébauche de classe permettant de manipuler des fichiers Opendocuments avec PHP il y a qq temps. Avec l'aide d'un collègue (julien Pauli), on a fait évoluer cette classe et on gère maintenant les ajouts d'images et les boucles.

Voici le code :
<?php
class odfDocException extends Exception { }

/**
 * Classe de templating pour fichier odt
 * requière PHP 5.2 minimum
 *
 * @copyright  Copyright (c) 2008 Anaska (http://www.anaska.com)
 * @license    http://www.gnu.org/copyleft/gpl.html  GPL License
 * @version 1.0
 */

class odf
{
	const DELIMITER_LEFT  = '{';
	const DELIMITER_RIGHT = '}';

	private $file;
	private $content;
	private $tmpfile;
	private $vars      = array(); // Array with all the data to change
	private $segments  = array();
	private $segmentss = array();

	/**
	 * Constructeur de classe
	 *
	 * @param string $filename nom du fichier odt
	 * @throws odfDocException
	 */
	public function __construct($filename)
	{
		if (!class_exists('ZipArchive'))
		{
			throw new odfDocException('Zip extension not loaded - check your php settings');
		}
		$this->file = new ZipArchive();
		if ($this->file->open($filename) !== true)
		{
			throw new odfDocException("Error while Opening the file '$filename' - Check your odt file");
		}
		$this->content = $this->file->getFromName('content.xml');
		if ($this->content == NULL)
		{
			throw new odfDocException("Nothing to parse - check that the content.xml file is correctly formed\n");
		}
		$tmp = tempnam(null,md5(uniqid())).'.odt';
		copy($filename,$tmp);
		$this->tmpfile = $tmp;
	}
	/**
	 * Affecte une variable de template
	 *
	 * @param string $key nom de la variable dans le template
	 * @param string $value valeur de remplacement
	 * @return $this
	 */
	public function setVars($key, $value)
	{
		$this->vars[self::DELIMITER_LEFT.$key.self::DELIMITER_RIGHT] = utf8_encode($value);
		return $this;
	}

	public function setImage($key, $value)
	{
		$filename = strtok(strrchr($value,'/'),'/.');
		$file = substr(strrchr($value,'/'),1);

		$xml = <<<IMG
<draw:frame draw:style-name="fr1" draw:name="$filename" text:anchor-type="paragraph" svg:width="5cm" svg:height="8cm" draw:z-index="0"><draw:image xlink:href="Pictures/$file" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/></draw:frame>
IMG;
		$this->file->addFile($value,'Pictures/'.$file);
		$this->setVars($key,$xml);
	}

	/**
	 * Fusionne les variables de template
	 *
	 * @param string $segment si précisé : ne fusionne que ce segment
	 * @return $this
	 */
	public function parse($segment = null)
	{
		if (is_null($segment))
		{
			$this->content = str_replace(array_keys($this->vars), array_values($this->vars), $this->content);
		}else{
			@$this->segmentss[$segment] .= str_replace(array_keys($this->vars), array_values($this->vars), $this->segments[$segment]);
		}
		return $this;
	}

	/**
	 * Rajoute le segment fusionné au document
	 *
	 * @param string $segment
	 * @return $this
	 */
	public function mergeSegment($segment)
	{
		$this->content = str_replace(array_values($this->segments), array_values($this->segmentss), html_entity_decode($this->content));
		$reg = "/[ \t]*\[!--\s+BEGIN $segment\s+--\]\s*?\n?(\s*.*?\n?)\s*\[!--\s+END $segment\s+--\]\s*?\n?/sm";
		$this->content = preg_replace($reg, '$1',$this->content);
		return $this;
	}

	/**
	 * Affiche toutes les variables de templates actuelles
	 *
	 */
	public function printVars()
	{
		echo '<pre>';
		print_r($this->vars);
		echo '</pre>';
	}

	/**
	 * Affiche le fichier de contenu xml du document odt
	 * tel qu'il est à cet instant
	 *
	 * @return string
	 */
	public function __toString()
	{
		return $this->content;
	}

	/**
	 * Affiche les segments de boucles déclarés avec setblock
	 *
	 */
	public function printSegments()
	{
		echo '<pre>';
		print_r($this->segments);
		echo '</pre>';
	}

	/**
	 * Déclare un segment pour une utilisation en boucle
	 *
	 *
	 * @param string $segment
	 * @return $this
	 */
	public function setBlock($segment)
	{
		$reg = "/[ \t]*\[!--\s+BEGIN $segment\s+--\]\s*?\n?(\s*.*?\n?)\s*\[!--\s+END $segment\s+--\]\s*?\n?/sm";
	    if (preg_match($reg, html_entity_decode($this->content), $m) == 0)
	    {
	    	throw new odfDocException("'$segment' segment not found in the document");
	    }
	    $this->segments[$segment] = $m[0];
	    return $this;
	}

	/**
	 * Sauvegarde le fichier odt sur le disque
	 *
	 * @param string $newfile si précisé: sauve dans ce fichier, sinon écrase
	 * le fichier actuellement ouvert
	 */
	public function saveToDisk($newfile = null)
	{
		if (!is_null($newfile) && $this->file->open($newfile, ZIPARCHIVE::CREATE) !== true)
		{
				throw new odfDocException('Same filename');
		}

		if (!$this->file->addFromString('content.xml', $this->content))
		{
			throw new odfDocException('Error during the file saving');
		}
		$this->file->close();
		unset($this);
	}

	/**
	 * Exporte le contenu complet du fichier odt
	 *
	 * @return string
	 */
	public function export()
	{
		if ($this->file->open($this->tmpfile, ZIPARCHIVE::CREATE) !== true)
		{
			throw new odfDocException('Error');
		}


		if (!$this->file->addFromString('content.xml', $this->content))
		{
			throw new odfDocException('Error during the file exporting');
		}
		$this->file->close();
		return readfile($this->tmpfile);
	}
}
Pour y faire appel on utilise le code suivant :
<?php

$odf = new odf("modele_template.odt");

$odf->setImage('logo',"C:/Users/Administrateur/Desktop/image/sl.gif");

$odf->parse();

header('Content-type: multipart/x-zip');
header("Content-Disposition: attachment; filename=toto.odt");
echo $odf->export();
Dans le fichier OpenDocument il suffit de mettre des "tags" comme :
{societe}
{societe_cp} {societe_ville}
{logo}

La classe va encore évoluer mais si vous avez des remarques sur le code, sur les fonctionnalités ou juste besoin d'infos mail me.

++
cyruss
Co-auteur du livre PHP 7 avancé
Co-auteur du livre Performances PHP : Audit et optimisation LAMP
Co-fondateur de l'Association Française des Utilisateurs de PHP http://www.afup.org
Formateur PHP pour Openska

Mammouth du PHP | 1511 Messages

15 janv. 2008, 14:07

Je me souviens d'un article concernant la manipulation de fichier odf dans phpsolutions ;)
C'était dans le même esprit que ta classe apparement :P

ViPHP
ViPHP | 3300 Messages

15 janv. 2008, 14:32

return $this;
hmm bizarre :)
Fait du php depuis que ca existe ou presque :)

ViPHP
ViPHP | 4674 Messages

15 janv. 2008, 17:46

L'avantage de retourner l'objet lui-même (son instance : $this) permet d'avoir une interface fluide. Par exemple :
$var = $obj->method()->otherMethod()->methodA()->methodZ();
// toutes les méthodes sont appelées sur $obj.
Plus pratique, mais pas toujours adapté. Il faut l'utiliser avec soin. Mais c'est très utile pour les setters et getters par exemple.
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

ViPHP
ViPHP | 3300 Messages

16 janv. 2008, 15:49

yeek j'aime ps ces syntaxes du tout, après effectivement c'est une affaire de gout :)
Fait du php depuis que ca existe ou presque :)