'Macros disponibles :', 'style' => array( 'bold' => true, 'font-size' => 16 ) ); $rows = array_merge($rows, $this->recursive_sample($dataset,$level,$parent)); $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; } public function recursive_sample($dataset,$level = 0,$parent=''){ $stream = array(); $parent = ($parent!=''?$parent.'.':''); $indentation = str_repeat("\t", $level); $titleSize = 18-($level*2); $titleSize = $titleSize < 12 ? $titleSize:12; foreach($dataset as $macro => $infos){ $infos['type'] = isset($infos['type']) ? $infos['type'] : ''; switch($infos['type']){ case 'list': $stream[]= array( 'content'=> $indentation.$parent.$macro.' '.(isset($infos['label'])?': '.$infos['label']:'').' (liste)', 'style' => array( 'bold' => true, 'font-size' => $titleSize ) ); $stream[]= array('content'=> $indentation.'{{#'.$parent.$macro.'}}'); if(is_array($infos['value']) && isset($infos['value'][0])) $stream= array_merge($stream,self::recursive_sample($infos['value'][0],$level+1)); $stream[]= array('content'=> $indentation.'{{/'.$parent.$macro.'}}'); break; case 'object': $stream[]= array( 'content'=> $indentation.$parent.$macro.' '.(!empty($infos['label'])?': '.$infos['label']:''), 'style' => array( 'bold' => true, 'font-size' => $titleSize ) ); $stream= array_merge( $stream,self::recursive_sample($infos['value'],$level+1,$parent.$macro)); break; case 'image' : $stream[]= array('content'=> $indentation.'{{'.$parent.$macro.'::image}} : '.( !isset($infos['label']) ? '': $infos['label'])); break; default : $stream[]= array('content'=> $indentation.'{{'.$parent.$macro.'}} : '.( !isset($infos['label']) ? '': $infos['label'])); break; } } return $stream; } /** ROOT **/ //Fichier [Content_Types].xml public static function content_types(){ return ' '; } /** DOCPROPS **/ //Fichier docProps/app.xml public static function app(){ return ' 0100Microsoft 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 .= ''; } if(isset($row['content'])) $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($img){ //Unité utilisé en OOXML, 1px = 9525 EMU $emu = 9525; $finfo = new finfo(FILEINFO_MIME); $mime = $finfo->buffer($img); $ext = 'jpg'; switch($mime){ case 'image/jpeg': $ext = 'jpeg'; break; case 'image/png': $ext = 'png'; break; case 'image/gif': $ext = 'gif'; break; } if($mime == 'image/jpg') $mime = 'image/jpeg'; list($width, $height) = getimagesizefromstring($img); $mimeTypes = $this->zip->getFromName('[Content_Types].xml', 0, ZipArchive::OVERWRITE); if(strrpos($mimeTypes, '') === false) { $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->addFromString( 'word/media/'.$uid.'.'.$ext,$img); $rel = ''; $this->relationships = str_replace('',$rel.'',$this->relationships); $xmlPic = ''; return $xmlPic; } public function start(&$modelStream, $data){ $this->destination = File::dir().'tmp'.SLASH.'template.'.time().'-'.rand(0,100).'.docx'; file_put_contents($this->destination, utf8_decode($modelStream) ); $this->parse($this->destination); $modelStream = $this->body; } public function from_template($stream, $datas){ //if / loop $loopIfRegex = '/\{\{\#([^\/\#}]*)\}\}(.*?)\{\{\/\1\}\}/is'; $stream = preg_replace_callback($loopIfRegex,function($matches) use ($datas) { $key = $matches[1]; $streamTpl = $matches[2]; $keyInfos = explode('::',$key); $key = $keyInfos[0]; $type = isset($keyInfos[1]) ? $keyInfos[1] : 'string'; $value = $this->decomposeKey($datas,$key); //gestion des boucles if(is_array($value)){ $stream = ''; foreach($value as $line){ $localData = is_array($line) ? array_merge($datas,$line) : $datas; $stream .= $this->from_template($streamTpl,$localData); } return $stream; //gestion des if }else{ return !isset($value) || (!is_array($value) && empty($value)) || (is_array($value) && count($value)==0) ? '' : $this->from_template($streamTpl,$datas); } },$stream); //gestion des elses $elseRegex = '/\{\{\^([^\/\#}]*)\}\}(.*?)\{\{\/\1\}\}/is'; $stream = preg_replace_callback($elseRegex,function($matches) use ($datas) { $key = $matches[1]; $streamTpl = $matches[2]; $keyInfos = explode('::',$key); $key = $keyInfos[0]; $type = isset($keyInfos[1]) ? $keyInfos[1] : 'string'; $value = $this->decomposeKey($datas,$key); if(is_array($value)){ $stream = ''; foreach($value as $line){ $localData = is_array($line) ? array_merge($datas,$line) : $datas; $stream .= $this->from_template($streamTpl,$localData); } return $stream; //gestion des else }else{ return !isset($value) || (!is_array($value) && empty($value)) || (is_array($value) && count($value)==0) ? $this->from_template($streamTpl,$datas) : ''; } },$stream); //gestion des simples variables $stream = preg_replace_callback('/{{([^#\/}]*)}}/',function($matches) use ($datas) { $key = $matches[1]; $keyInfos = explode('::',$key); $key = $keyInfos[0]; $type = isset($keyInfos[1]) ? $keyInfos[1] : 'string'; $value = $this->decomposeKey($datas,$key); if(!isset($value)) return $this->formatValue($type,$matches[0]); if(is_array($value)) return 'Array'; return $this->formatValue($type,$value); },$stream); return $stream; } public function formatValue($type,$value){ if($type == 'image') $value = $this->add_image($value); if($type == 'html') $value = self::convert_text_format($value); if($type == 'string'){ //Remplace les & par des & $value = str_replace(array('&'), array('&'), $value); //remplace les balises non word (ne commencant pas par ]*)>|is', '<$1>', $value); } return $value; } public function end($stream,$data){ $this->body = $stream; $this->footer = $this->from_template($this->footer, $data); $this->header = $this->from_template($this->header, $data); $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); $this->zip->close(); $stream = file_get_contents($this->destination); unlink($this->destination); return $stream; } public function decomposeKey($datas,$key){ if(array_key_exists($key, $datas)) return isset($datas[$key]) ? $datas[$key] : '' ; $attributes = explode('.',$key); $current = $datas; $value = null; foreach ($attributes as $attribute) { if(!array_key_exists($attribute, $current)) break; $current = $current[$attribute]; $value = isset($current) ? $current : ''; } return $value; } // Converti les data wysiwyg en wysiwyg word 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); return $value; } public static function strip($content){ $content = preg_replace_callback( '#\{\{([^\]]+)\}\}#U', function ($match) { return strip_tags($match[0]); }, $content ); return $content; } } ?>