'Macros disponibles :',
			'style' => array(
				'bold' => true,
				'font-size' => 16
			)
		);
		foreach($dataset as $macro => $infos) {
			$rows[] = array(
				'content' => ($infos['type']=='list') ? '{{#'.$macro.'}}{{/'.$macro.'}} : '.$infos['desc'] : '{{'.$macro.'}} : '.$infos['desc']
			);
		}
		$temp_file = tempnam(sys_get_temp_dir(), mt_rand(0,10000));
		self::write($temp_file, $rows);
		$stream = file_get_contents($temp_file);
		unlink($temp_file);
		return $stream;
	}
	/** ROOT **/
	//Fichier [Content_Types].xml
	public static function content_types(){
		return '
';
	}
	/** DOCPROPS **/
	//Fichier docProps/app.xml
	public static function app(){
		return '
Normal.dotm0100Microsoft Office Word011falseTitre1false0falsefalse16.0000';
	}
	//Fichier docProps/core.xml
	public static function core(){
		global $myUser;
		return '
'.$myUser->fullName().''.$myUser->fullName().'1'.date('Y-m-d').'T'.date('H:i:s').'Z'.''.date('Y-m-d').'T'.date('H:i:s').'Z'.'';
	}
	/** _RELS **/
	//Fichier _rels/.rels
	public static function single_rels(){
		return '
';
	}
	/** WORD **/
	//Fichier word/_rels/document.xml.rels
	public static function word_rels(){
		return '
';
	}
	//Fichier word/webSettings.xml
	public static function web_settings(){
		return '
';
	}
	//Fichier word/settings.xml
	public static function settings(){
		return '
';
	}
	//Fichier word/fontTable.xml
	public static function font_table(){
		return '
';
	}
	//créée le document d'exemple
	public static function document($rows){
		$stream = '
';
		foreach($rows as $row){
			$stream .= '';
			if(isset($row['style'])) {
				$stream .= '';
				foreach ($row['style'] as $property => $value) {
					$stream .= self::custom_style_map($property, $value);
				}
				$stream .= '';
			}
			$stream .= ''. htmlspecialchars($row['content'], ENT_COMPAT).'';
			$stream .= '';
		}
		$stream .= '';
		return $stream;
	}
	//Retourne les différentes balises de style
	public static function custom_style_map($property=null, $value=null){
		$stdFontSize = 22; //11pt * 2
		$stdColor = '000000';
		$stdHighlight = 'none';
		$stdHighlightColors= array('black','blue','cyan','green','magenta','red','yellow','white','darkBlue','darkCyan','darkGreen','darkMagenta','darkRed','darkYellow','darkGray','lightGray');
		$basic = array(
			'bold' => array('tag'=>''),
			'italic' => array('tag'=>''),
			'underline' => array('tag'=>''),
			'strike' => array('tag'=>''),
			'caps' => array('tag'=>''),
		);
		if(isset($value)){
			$advanced =	array(
				'font-size' => array('tag'=>''),
				'color' => array('tag'=>''),
				'highlight' => array('tag'=>'')
			);
		}
		$styles = array_merge($basic, $advanced);
		return isset($property) && isset($styles[$property]) ? $styles[$property]['tag'] : '';
	}
	//Crée un fichier Word (format .docx) au chemin
	//$path indiqué avec les données $rows fournies
	//en paramètres
	/** Tableau de données attendu :
	 *	$rows = array(
	 *		0 => array(
	 *			'content' => 'Macros disponibles :',
	 *			'style' => array(
	 *				'bold' => true,
	 *				'font-size' => 16
	 *			)
	 *		),
	 *		1 => array(
	 *			'content' => '[programme.date]'
	 *		),
	 *		2 => array(
	 *			'content' => '[programme.heure]'
	 *		)
	 *	);
	 *
	 * Chaque entrée dans $rows correspond à une ligne (paragraphe) sur le fichier
	 **/
	public static function write($path, $rows){
		$docx = new ZipArchive();
		if(file_exists($path)) unlink($path);
		$docx->open($path, ZipArchive::CREATE);
		//root
		$docx->addFromString('[Content_Types].xml', self::content_types());
		//_rels folder
		$docx->addFromString('_rels/.rels', self::single_rels());
		//docProps folder
		$docx->addFromString('docProps/app.xml', self::app());
		$docx->addFromString('docProps/core.xml', self::core());
		//word folder
		$docx->addFromString('word/_rels/document.xml.rels', self::word_rels());
		$docx->addFromString('word/document.xml', self::document($rows));
		$docx->addFromString('word/fontTable.xml', self::font_table());
		$docx->addFromString('word/settings.xml', self::settings());
		$docx->addFromString('word/webSettings.xml', self::web_settings());
		$docx->close();
	}
	public function parse($filePath){
		$this->zip = new ZipArchive();
		$res = $this->zip->open($filePath);
		if($res !== TRUE)  throw new Exception('Impossible d\'ouvrir le ZIP, code:' . $res);
		$this->body = self::strip($this->zip->getFromName('word/document.xml'));
		$this->footer = self::strip($this->zip->getFromName('word/footer1.xml'));
		$this->header = self::strip($this->zip->getFromName('word/header1.xml'));
		$this->relationships = $this->zip->getFromName('word/_rels/document.xml.rels');
	}
	public function add_image($tag, $img, $return=false){
		//Unité utilisé en OOXML, 1px = 9525 EMU
		$emu = 9525;
		$pathParts = explode(SLASH, $img);
		$imgParts = explode('.', end($pathParts));
		preg_match('/(jpg|jpeg|png)/', end($imgParts), $ext);
		$ext = $ext[0];
		$imgParts[array_search(end($imgParts), $imgParts)] = $ext;
		$cType = in_array($ext, array('jpg', 'jpeg')) ? 'jpeg' : $ext;
		$pathParts[array_search(end($pathParts), $pathParts)] = implode('.', $imgParts);
		$imgUrl = implode(SLASH, $pathParts);
		$img = strpos($imgUrl, __ROOT__) !== false ? $img : __ROOT__.$imgUrl;
		//Récupération taille image
		list($width, $height) = getimagesize($img);
		$mimeTypes = $this->zip->getFromName('[Content_Types].xml', 0, ZipArchive::OVERWRITE);
		if(strrpos($mimeTypes, '') === false) {
			$mimeTypes = str_replace('', '', $mimeTypes);
			// $mimeTypes = str_replace('', '', $mimeTypes);
			$this->zip->addFromString('[Content_Types].xml', $mimeTypes);
		}
		$i = 100;
		$uid = 'img'.$i;
		while(strpos($this->relationships, 'Id="'.$uid.'"') !== false){
			$i++;
			$uid = 'img'.$i;
		}
		$this->zip->addFile($img, 'word/media/'.$uid.'.'.$ext);
		$rel = '';
		$this->relationships = str_replace('',$rel.'',$this->relationships);
		$xmlPic = '';
		$this->body = str_replace('{{'.$tag.'}}',$xmlPic, $this->body);
		if($return){
			$datas['picture'] = $xmlPic;
			$datas['cType'] = $mimeTypes;
			return $datas;
		}
	}
	//crée le document final avec les tags remplacés
	public static function from_template($source, $data, $return){ 
		$destination = File::dir().'tmp'.SLASH.'template.'.time().'-'.rand(0,100).'.docx';
		$source = File::dir().$source;
		copy($source,$destination);
		$docx = new self();
		$docx->parse($destination);
		foreach($data as $tag => $value){ 
			if(is_array($value)){
				$docx->body = preg_replace_callback('#\{\{\#'.$tag.'\}\}(.*)\{\{\/'.$tag.'\}\}#si', function ($match) use($value, $docx) {
					$content = '';
					foreach($value as $line){
						$bloc = $match[1];
						foreach($line as $subTag=>$subValue){
							if(substr($subValue,0,2)=='::' && strpos($bloc, '{{'.$subTag.'}}') !== false) {
								$picDatas = $docx->add_image($subTag, substr($subValue,2), true);
								$bloc = str_replace('{{'.$subTag.'}}',$picDatas['picture'],$bloc);
								continue;
							}
							$subValue = self::convert_text_format($subValue);
							$bloc = str_replace('{{'.$subTag.'}}',$subValue,$bloc);
						}
						$content .=$bloc;
					}
					return $content;
				},$docx->body);
				continue;
			}
			if(substr($value,0,2)=='::'){
				$docx->add_image($tag, substr($value,2));
				continue;
			}
			$value = self::convert_text_format($value);
			$docx->body = str_replace('{{'.$tag.'}}',$value,$docx->body);
			$docx->footer = str_replace('{{'.$tag.'}}',$value,$docx->footer);
			$docx->header = str_replace('{{'.$tag.'}}',$value,$docx->header);
		}
	
		//conditions - if
		$ifRegex = '/\{\{\#([^\/\:\#}]*)\}\}(.*?)\{\{\/\1\}\}/is';
		$docx->body = preg_replace_callback($ifRegex,function($matches) use ($data) {
			$key = $matches[1];
			$docx->body = $matches[2];
			return !isset($data[$key]) || (!is_array($data[$key]) && empty($data[$key])) || (is_array($data[$key]) && count($data[$key])==0) ?'':$docx->body;
		},$docx->body);
		
		//conditions - else
		$elseRegex = '/\{\{\^([^\/\:\#}]*)\}\}(.*?)\{\{\/\1\}\}/is';
		$docx->body = preg_replace_callback($elseRegex,function($matches) use ($data) {
			$key = $matches[1];
			$docx->body = $matches[2];
			return (isset($data[$key]) && !is_array($data[$key]) && !empty($data[$key])) || (is_array($data[$key]) && count($data[$key])>0) ?'':$docx->body;
		},$docx->body);
		$docx->save();
		$docx->close();
		if($return!='stream') return $destination;
		$stream = file_get_contents($destination);
		unlink($destination);
		return $stream;
	}
	public static function convert_text_format($value){
		//Remplace les 
 et les 
 par les w:br word
		$value = str_replace(array('
','
','
',''),'',$value);
		//todo ameliorable
		$value = str_replace(array('- '),' - ',$value);
		$value = str_replace(array(''),'',$value);
		$value = str_replace('
','',$value);
		
        //todo - $value = preg_replace('|([^<]*)|is', '$1', $value);
		
		//Remplace les & par des &
		$value = str_replace(array('&'), array('&'), $value);
		//remplace les balises non word (ne commencant pas par ]*)>|is', '<$1>', $value);
		return $value;
	}
	//Ajoute au fichier les différentes modifications effectuées
	public function save(){
		$this->zip->addFromString('word/document.xml', $this->body);
		if(!empty($this->header)) $this->zip->addFromString('word/header1.xml', $this->header);
		if(!empty($this->footer)) $this->zip->addFromString('word/footer1.xml', $this->footer);
		$this->zip->addFromString('word/_rels/document.xml.rels', $this->relationships);
	}
	//Ferme le fichier
	public function close(){
		$this->zip->close();
	}
	public static function strip($content){
		$content = preg_replace_callback(
			'#\{\{([^\]]+)\}\}#U',
			function ($match) {
				return strip_tags($match[0]);
			},
			$content
		);
		return $content;
	}
}
?>