WordExport.class.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. <?php
  2. /**
  3. * Define a docx.
  4. * @author Valentin MORREEL
  5. * @category Plugin
  6. * @license copyright
  7. */
  8. class WordExport {
  9. public $mime = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
  10. public $extension = 'docx';
  11. public $description = 'Fichier de traitement de texte Word';
  12. public $destination;
  13. public $zip,$body,$header,$footer,$relationships;
  14. public function sample($dataset,$level = 0,$parent=''){
  15. $rows = array();
  16. $rows[] = array(
  17. 'content' => 'Macros disponibles :',
  18. 'style' => array(
  19. 'bold' => true,
  20. 'font-size' => 16
  21. )
  22. );
  23. $rows = array_merge($rows, $this->recursive_sample($dataset,$level,$parent));
  24. $temp_file = tempnam(sys_get_temp_dir(), mt_rand(0,10000));
  25. self::write($temp_file, $rows);
  26. $stream = file_get_contents($temp_file);
  27. unlink($temp_file);
  28. return $stream;
  29. }
  30. public function recursive_sample($dataset,$level = 0,$parent=''){
  31. $stream = array();
  32. $parent = ($parent!=''?$parent.'.':'');
  33. $indentation = str_repeat("\t", $level);
  34. $titleSize = 18-($level*2);
  35. $titleSize = $titleSize < 12 ? $titleSize:12;
  36. foreach($dataset as $macro => $infos){
  37. $infos['type'] = isset($infos['type']) ? $infos['type'] : '';
  38. switch($infos['type']){
  39. case 'list':
  40. $stream[]= array(
  41. 'content'=> $indentation.$parent.$macro.' '.(isset($infos['label'])?': '.$infos['label']:'').' (liste)',
  42. 'style' => array(
  43. 'bold' => true,
  44. 'font-size' => $titleSize
  45. )
  46. );
  47. $stream[]= array('content'=> $indentation.'{{#'.$parent.$macro.'}}');
  48. if(is_array($infos['value']) && isset($infos['value'][0])) $stream= array_merge($stream,self::recursive_sample($infos['value'][0],$level+1));
  49. $stream[]= array('content'=> $indentation.'{{/'.$parent.$macro.'}}');
  50. break;
  51. case 'object':
  52. $stream[]= array(
  53. 'content'=> $indentation.$parent.$macro.' '.(!empty($infos['label'])?': '.$infos['label']:''),
  54. 'style' => array(
  55. 'bold' => true,
  56. 'font-size' => $titleSize
  57. )
  58. );
  59. $stream= array_merge( $stream,self::recursive_sample($infos['value'],$level+1,$parent.$macro));
  60. break;
  61. case 'image' :
  62. $stream[]= array('content'=> $indentation.'{{'.$parent.$macro.'::image}} : '.( !isset($infos['label']) ? '': $infos['label']));
  63. break;
  64. default :
  65. $stream[]= array('content'=> $indentation.'{{'.$parent.$macro.'}} : '.( !isset($infos['label']) ? '': $infos['label']));
  66. break;
  67. }
  68. }
  69. return $stream;
  70. }
  71. /** ROOT **/
  72. //Fichier [Content_Types].xml
  73. public static function content_types(){
  74. return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  75. <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/><Override PartName="/word/webSettings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"/><Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>';
  76. }
  77. /** DOCPROPS **/
  78. //Fichier docProps/app.xml
  79. public static function app(){
  80. return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  81. <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Template>Normal.dotm</Template><TotalTime>0</TotalTime><Pages>1</Pages><Words>0</Words><Characters>0</Characters><Application>Microsoft Office Word</Application><DocSecurity>0</DocSecurity><Lines>1</Lines><Paragraphs>1</Paragraphs><ScaleCrop>false</ScaleCrop><HeadingPairs><vt:vector size="2" baseType="variant"><vt:variant><vt:lpstr>Titre</vt:lpstr></vt:variant><vt:variant><vt:i4>1</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size="1" baseType="lpstr"><vt:lpstr></vt:lpstr></vt:vector></TitlesOfParts><Company></Company><LinksUpToDate>false</LinksUpToDate><CharactersWithSpaces>0</CharactersWithSpaces><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>16.0000</AppVersion></Properties>';
  82. }
  83. //Fichier docProps/core.xml
  84. public static function core(){
  85. global $myUser;
  86. return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  87. <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title></dc:title><dc:subject></dc:subject><dc:creator>'.$myUser->fullName().'</dc:creator><cp:keywords></cp:keywords><cp:lastModifiedBy>'.$myUser->fullName().'</cp:lastModifiedBy><cp:revision>1</cp:revision><dcterms:created xsi:type="dcterms:W3CDTF">'.date('Y-m-d').'T'.date('H:i:s').'Z'.'</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">'.date('Y-m-d').'T'.date('H:i:s').'Z'.'</dcterms:modified></cp:coreProperties>';
  88. }
  89. /** _RELS **/
  90. //Fichier _rels/.rels
  91. public static function single_rels(){
  92. return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  93. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>';
  94. }
  95. /** WORD **/
  96. //Fichier word/_rels/document.xml.rels
  97. public static function word_rels(){
  98. return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  99. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/></Relationships>';
  100. }
  101. //Fichier word/webSettings.xml
  102. public static function web_settings(){
  103. return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  104. <w:webSettings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid"><w:optimizeForBrowser/><w:allowPNG/></w:webSettings>';
  105. }
  106. //Fichier word/settings.xml
  107. public static function settings(){
  108. return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  109. <w:settings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main" mc:Ignorable="w14 w15 w16se w16cid"><w:zoom w:percent="100"/><w:proofState w:spelling="clean" w:grammar="clean"/><w:defaultTabStop w:val="708"/><w:hyphenationZone w:val="425"/><w:characterSpacingControl w:val="doNotCompress"/><w:compat><w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"/><w:compatSetting w:name="overrideTableStyleFontSizeAndJustification" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="enableOpenTypeFeatures" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="doNotFlipMirrorIndents" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="differentiateMultirowTableHeaders" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="useWord2013TrackBottomHyphenation" w:uri="http://schemas.microsoft.com/office/word" w:val="0"/></w:compat><m:mathPr><m:mathFont m:val="Cambria Math"/><m:brkBin m:val="before"/><m:brkBinSub m:val="--"/><m:smallFrac m:val="0"/><m:dispDef/><m:lMargin m:val="0"/><m:rMargin m:val="0"/><m:defJc m:val="centerGroup"/><m:wrapIndent m:val="1440"/><m:intLim m:val="subSup"/><m:naryLim m:val="undOvr"/></m:mathPr><w:themeFontLang w:val="fr-FR"/><w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink"/><w:shapeDefaults><o:shapedefaults v:ext="edit" spidmax="1026"/><o:shapelayout v:ext="edit"><o:idmap v:ext="edit" data="1"/></o:shapelayout></w:shapeDefaults><w:decimalSymbol w:val=","/><w:listSeparator w:val=";"/><w15:chartTrackingRefBased/><w15:docId w15:val="{E4CFD658-C7F9-47A9-A972-9FA0C777DED5}"/></w:settings>';
  110. }
  111. //Fichier word/fontTable.xml
  112. public static function font_table(){
  113. return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  114. <w:fonts xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid"><w:font w:name="Calibri"><w:panose1 w:val="020F0502020204030204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="E0002AFF" w:usb1="4000ACFF" w:usb2="00000001" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font></w:fonts>';
  115. }
  116. //créée le document d'exemple
  117. public static function document($rows){
  118. $stream = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  119. <w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid wp14"><w:body>';
  120. foreach($rows as $row){
  121. $stream .= '<w:p><w:r>';
  122. if(isset($row['style'])) {
  123. $stream .= '<w:rPr>';
  124. foreach ($row['style'] as $property => $value) {
  125. $stream .= self::custom_style_map($property, $value);
  126. }
  127. $stream .= '</w:rPr>';
  128. }
  129. if(isset($row['content'])) $stream .= '<w:t xml:space="preserve">'. htmlspecialchars($row['content'], ENT_COMPAT).'</w:t>';
  130. $stream .= '</w:r></w:p>';
  131. }
  132. $stream .= '<w:sectPr><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="720" w:right="720" w:bottom="720" w:left="720" w:header="708" w:footer="708" w:gutter="0"/><w:cols w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:body></w:document>';
  133. return $stream;
  134. }
  135. //Retourne les différentes balises de style
  136. public static function custom_style_map($property=null, $value=null){
  137. $stdFontSize = 22; //11pt * 2
  138. $stdColor = '000000';
  139. $stdHighlight = 'none';
  140. $stdHighlightColors= array('black','blue','cyan','green','magenta','red','yellow','white','darkBlue','darkCyan','darkGreen','darkMagenta','darkRed','darkYellow','darkGray','lightGray');
  141. $basic = array(
  142. 'bold' => array('tag'=>'<w:b/>'),
  143. 'italic' => array('tag'=>'<w:i/>'),
  144. 'underline' => array('tag'=>'<w:u w:val="single"/>'),
  145. 'strike' => array('tag'=>'<w:strike/>'),
  146. 'caps' => array('tag'=>'<w:caps/>'),
  147. );
  148. if(isset($value)){
  149. $advanced = array(
  150. 'font-size' => array('tag'=>'<w:sz w:val="'.(is_numeric($value)?$value*2:22).'"/>'),
  151. 'color' => array('tag'=>'<w:color w:val="'.(ctype_xdigit($value) && strlen($value)==6)?ltrim($value,'#'):$stdColor.'"/>'),
  152. 'highlight' => array('tag'=>'<w:highlight w:val="'.(in_array($value,$stdHighlightColors))?$value:$stdHighlight.'"/>')
  153. );
  154. }
  155. $styles = array_merge($basic, $advanced);
  156. return isset($property) && isset($styles[$property]) ? $styles[$property]['tag'] : '';
  157. }
  158. //Crée un fichier Word (format .docx) au chemin
  159. //$path indiqué avec les données $rows fournies
  160. //en paramètres
  161. /** Tableau de données attendu :
  162. * $rows = array(
  163. * 0 => array(
  164. * 'content' => 'Macros disponibles :',
  165. * 'style' => array(
  166. * 'bold' => true,
  167. * 'font-size' => 16
  168. * )
  169. * ),
  170. * 1 => array(
  171. * 'content' => '[programme.date]'
  172. * ),
  173. * 2 => array(
  174. * 'content' => '[programme.heure]'
  175. * )
  176. * );
  177. *
  178. * Chaque entrée dans $rows correspond à une ligne (paragraphe) sur le fichier
  179. **/
  180. public static function write($path, $rows){
  181. $docx = new ZipArchive();
  182. if(file_exists($path)) unlink($path);
  183. $docx->open($path, ZipArchive::CREATE);
  184. //root
  185. $docx->addFromString('[Content_Types].xml', self::content_types());
  186. //_rels folder
  187. $docx->addFromString('_rels/.rels', self::single_rels());
  188. //docProps folder
  189. $docx->addFromString('docProps/app.xml', self::app());
  190. $docx->addFromString('docProps/core.xml', self::core());
  191. //word folder
  192. $docx->addFromString('word/_rels/document.xml.rels', self::word_rels());
  193. $docx->addFromString('word/document.xml', self::document($rows));
  194. $docx->addFromString('word/fontTable.xml', self::font_table());
  195. $docx->addFromString('word/settings.xml', self::settings());
  196. $docx->addFromString('word/webSettings.xml', self::web_settings());
  197. $docx->close();
  198. }
  199. public function parse($filePath){
  200. $this->zip = new ZipArchive();
  201. $res = $this->zip->open($filePath);
  202. if($res !== TRUE) throw new Exception('Impossible d\'ouvrir le ZIP, code:' . $res);
  203. $this->body = self::strip($this->zip->getFromName('word/document.xml'));
  204. $this->footer = self::strip($this->zip->getFromName('word/footer1.xml'));
  205. $this->header = self::strip($this->zip->getFromName('word/header1.xml'));
  206. $this->relationships = $this->zip->getFromName('word/_rels/document.xml.rels');
  207. }
  208. public function add_image($img){
  209. //Unité utilisé en OOXML, 1px = 9525 EMU
  210. $emu = 9525;
  211. $finfo = new finfo(FILEINFO_MIME);
  212. $mime = $finfo->buffer($img);
  213. $ext = 'jpg';
  214. switch($mime){
  215. case 'image/jpeg': $ext = 'jpeg'; break;
  216. case 'image/png': $ext = 'png'; break;
  217. case 'image/gif': $ext = 'gif'; break;
  218. }
  219. if($mime == 'image/jpg') $mime = 'image/jpeg';
  220. list($width, $height) = getimagesizefromstring($img);
  221. $mimeTypes = $this->zip->getFromName('[Content_Types].xml', 0, ZipArchive::OVERWRITE);
  222. if(strrpos($mimeTypes, '<Default Extension="'.$ext.'" ContentType="'.$mime.'"/>') === false) {
  223. $mimeTypes = str_replace('</Types>', '<Default Extension="gif" ContentType="image/gif"/><Default Extension="png" ContentType="image/png"/><Default Extension="jpeg" ContentType="image/jpeg"/><Default Extension="jpg" ContentType="image/jpeg"/></Types>', $mimeTypes);
  224. $this->zip->addFromString('[Content_Types].xml', $mimeTypes);
  225. }
  226. $i = 100;
  227. $uid = 'img'.$i;
  228. while(strpos($this->relationships, 'Id="'.$uid.'"') !== false){
  229. $i++;
  230. $uid = 'img'.$i;
  231. }
  232. $this->zip->addFromString( 'word/media/'.$uid.'.'.$ext,$img);
  233. $rel = '<Relationship Id="'.$uid.'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/'.$uid.'.'.$ext.'"/>';
  234. $this->relationships = str_replace('</Relationships>',$rel.'</Relationships>',$this->relationships);
  235. $xmlPic = '<w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0" ><wp:extent cx="'.$width*$emu.'" cy="'.$height*$emu.'"/><wp:effectExtent l="1" t="1" r="1" b="1"/><wp:docPr id="'.rand(100,200).'" name="'.$uid.'" descr=""/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="0" name=""/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="'.$uid.'"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="'.$width*$emu.'" cy="'.$height*$emu.'"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>';
  236. return $xmlPic;
  237. }
  238. public function start(&$modelStream, $data){
  239. $this->destination = File::dir().'tmp'.SLASH.'template.'.time().'-'.rand(0,100).'.docx';
  240. file_put_contents($this->destination, utf8_decode($modelStream) );
  241. $this->parse($this->destination);
  242. $modelStream = $this->body;
  243. }
  244. public function from_template($stream, $datas){
  245. //if / loop
  246. $loopIfRegex = '/\{\{\#([^\/\#}]*)\}\}(.*?)\{\{\/\1\}\}/is';
  247. $stream = preg_replace_callback($loopIfRegex,function($matches) use ($datas) {
  248. $key = $matches[1];
  249. $streamTpl = $matches[2];
  250. $keyInfos = explode('::',$key);
  251. $key = $keyInfos[0];
  252. $type = isset($keyInfos[1]) ? $keyInfos[1] : 'string';
  253. $value = $this->decomposeKey($datas,$key);
  254. //gestion des boucles
  255. if(is_array($value)){
  256. $stream = '';
  257. foreach($value as $line){
  258. $localData = is_array($line) ? array_merge($datas,$line) : $datas;
  259. $stream .= $this->from_template($streamTpl,$localData);
  260. }
  261. return $stream;
  262. //gestion des if
  263. }else{
  264. return !isset($value) || (!is_array($value) && empty($value)) || (is_array($value) && count($value)==0) ? '' : $this->from_template($streamTpl,$datas);
  265. }
  266. },$stream);
  267. //gestion des elses
  268. $elseRegex = '/\{\{\^([^\/\#}]*)\}\}(.*?)\{\{\/\1\}\}/is';
  269. $stream = preg_replace_callback($elseRegex,function($matches) use ($datas) {
  270. $key = $matches[1];
  271. $streamTpl = $matches[2];
  272. $keyInfos = explode('::',$key);
  273. $key = $keyInfos[0];
  274. $type = isset($keyInfos[1]) ? $keyInfos[1] : 'string';
  275. $value = $this->decomposeKey($datas,$key);
  276. if(is_array($value)){
  277. $stream = '';
  278. foreach($value as $line){
  279. $localData = is_array($line) ? array_merge($datas,$line) : $datas;
  280. $stream .= $this->from_template($streamTpl,$localData);
  281. }
  282. return $stream;
  283. //gestion des else
  284. }else{
  285. return !isset($value) || (!is_array($value) && empty($value)) || (is_array($value) && count($value)==0) ? $this->from_template($streamTpl,$datas) : '';
  286. }
  287. },$stream);
  288. //gestion des simples variables
  289. $stream = preg_replace_callback('/{{([^#\/}]*)}}/',function($matches) use ($datas) {
  290. $key = $matches[1];
  291. $keyInfos = explode('::',$key);
  292. $key = $keyInfos[0];
  293. $type = isset($keyInfos[1]) ? $keyInfos[1] : 'string';
  294. $value = $this->decomposeKey($datas,$key);
  295. if(!isset($value)) return $this->formatValue($type,$matches[0]);
  296. if(is_array($value)) return 'Array';
  297. return $this->formatValue($type,$value);
  298. },$stream);
  299. return $stream;
  300. }
  301. public function formatValue($type,$value){
  302. if($type == 'image') $value = $this->add_image($value);
  303. if($type == 'html') $value = self::convert_text_format($value);
  304. if($type == 'string'){
  305. //Remplace les & par des &amp;
  306. $value = str_replace(array('&'), array('&amp;'), $value);
  307. //remplace les balises non word (ne commencant pas par <w: ou </w: )
  308. $value = preg_replace('|<(?!/?w:)([^>]*)>|is', '&lt;$1&gt;', $value);
  309. }
  310. return $value;
  311. }
  312. public function end($stream,$data){
  313. $this->body = $stream;
  314. $this->footer = $this->from_template($this->footer, $data);
  315. $this->header = $this->from_template($this->header, $data);
  316. $this->zip->addFromString('word/document.xml', $this->body);
  317. if(!empty($this->header)) $this->zip->addFromString('word/header1.xml', $this->header);
  318. if(!empty($this->footer)) $this->zip->addFromString('word/footer1.xml', $this->footer);
  319. $this->zip->addFromString('word/_rels/document.xml.rels', $this->relationships);
  320. $this->zip->close();
  321. $stream = file_get_contents($this->destination);
  322. unlink($this->destination);
  323. return $stream;
  324. }
  325. public function decomposeKey($datas,$key){
  326. if(array_key_exists($key, $datas)) return isset($datas[$key]) ? $datas[$key] : '' ;
  327. $attributes = explode('.',$key);
  328. $current = $datas;
  329. $value = null;
  330. foreach ($attributes as $attribute) {
  331. if(!array_key_exists($attribute, $current)) break;
  332. $current = $current[$attribute];
  333. $value = isset($current) ? $current : '';
  334. }
  335. return $value;
  336. }
  337. // Converti les data wysiwyg en wysiwyg word
  338. public static function convert_text_format($value){
  339. //Remplace les <p> et les <br> par les w:br word
  340. $value = str_replace(array('<br>','<p>','</p>','<ul>'),'<w:br/>',$value);
  341. //todo ameliorable
  342. $value = str_replace(array('<li>'),' - ',$value);
  343. $value = str_replace(array('</li>'),'<w:br/>',$value);
  344. $value = str_replace('</ul>','',$value);
  345. //todo - $value = preg_replace('|<strong>([^<]*)</strong>|is', '<w:r w:rsidRPr="00556DA8"><w:rPr><w:b /></w:rPr><w:t>$1</w:t></w:r>', $value);
  346. return $value;
  347. }
  348. public static function strip($content){
  349. $content = preg_replace_callback(
  350. '#\{\{([^\]]+)\}\}#U',
  351. function ($match) {
  352. return strip_tags($match[0]);
  353. },
  354. $content
  355. );
  356. return $content;
  357. }
  358. }
  359. ?>