1<?php 2/** 3 * ODP Plugin: Exports to ODP 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author damien clochard <damien@taadeem.net> 7 */ 8// must be run within Dokuwiki 9if(!defined('DOKU_INC')) die(); 10 11require_once DOKU_INC.'inc/parser/renderer.php'; 12 13// ZipLib.class.php 14$dw_version = preg_replace('/[^\d]/', '', getversion()); 15if (version_compare($dw_version, "20070626") and 16 version_compare(PHP_VERSION,'5.0.0','>')) { 17 // If strictly newer than 2007-06-26 and use PHP5, fixes to ZipLib are 18 // included in Dokuwiki's ZipLib 19 require_once DOKU_INC.'inc/ZipLib.class.php'; 20} else { // for DW up to 2007-06-26, we need the patched version 21 require_once 'ZipLib.class.php'; 22} 23 24/** 25 * The Renderer 26 */ 27class renderer_plugin_odp extends Doku_Renderer { 28 var $ZIP = null; 29 var $meta; 30 var $store = ''; 31 var $footnotes = array(); 32 var $manifest = array(); 33 var $headers = array(); 34 var $template = ""; 35 var $fields = array(); 36 var $in_list_item = false; 37 var $in_paragraph = false; 38 var $highlight_style_num = 1; 39 var $slideopen = false; 40 // Automatic styles. Will always be added to content.xml and styles.xml 41 var $autostyles = array( 42 "pm1"=>' 43 <style:page-layout style:name="pm1"> 44 <style:page-layout-properties fo:page-width="21cm" fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:footnote-max-height="0cm"> 45 <style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.1cm" style:distance-after-sep="0.1cm" style:adjustment="left" style:rel-width="25%" style:color="#000000"/> 46 </style:page-layout-properties> 47 <style:header-style/> 48 <style:footer-style/> 49 </style:page-layout>', 50 "sub"=>' 51 <style:style style:name="sub" style:family="text"> 52 <style:text-properties style:text-position="-33% 80%"/> 53 </style:style>', 54 "sup"=>' 55 <style:style style:name="sup" style:family="text"> 56 <style:text-properties style:text-position="33% 80%"/> 57 </style:style>', 58 "del"=>' 59 <style:style style:name="del" style:family="text"> 60 <style:text-properties style:text-line-through-style="solid"/> 61 </style:style>', 62 "underline"=>' 63 <style:style style:name="underline" style:family="text"> 64 <style:text-properties style:text-underline-style="solid" 65 style:text-underline-width="auto" style:text-underline-color="font-color"/> 66 </style:style>', 67 "media"=>' 68 <style:style style:name="media" style:family="graphic" style:parent-style-name="Graphics"> 69 <style:graphic-properties style:run-through="foreground" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" 70 style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="baseline" style:horizontal-pos="left" 71 style:horizontal-rel="paragraph"/> 72 </style:style>', 73 "medialeft"=>' 74 <style:style style:name="medialeft" style:family="graphic" style:parent-style-name="Graphics"> 75 <style:graphic-properties style:run-through="foreground" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" 76 style:wrap-contour="false" style:horizontal-pos="left" style:horizontal-rel="paragraph"/> 77 </style:style>', 78 "mediaright"=>' 79 <style:style style:name="mediaright" style:family="graphic" style:parent-style-name="Graphics"> 80 <style:graphic-properties style:run-through="foreground" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" 81 style:wrap-contour="false" style:horizontal-pos="right" style:horizontal-rel="paragraph"/> 82 </style:style>', 83 "mediacenter"=>' 84 <style:style style:name="mediacenter" style:family="graphic" style:parent-style-name="Graphics"> 85 <style:graphic-properties style:run-through="foreground" style:wrap="none" style:horizontal-pos="center" 86 style:horizontal-rel="paragraph"/> 87 </style:style>', 88 "tablealigncenter"=>' 89 <style:style style:name="tablealigncenter" style:family="paragraph" style:parent-style-name="Table_20_Contents"> 90 <style:paragraph-properties fo:text-align="center"/> 91 </style:style>', 92 "tablealignright"=>' 93 <style:style style:name="tablealignright" style:family="paragraph" style:parent-style-name="Table_20_Contents"> 94 <style:paragraph-properties fo:text-align="end"/> 95 </style:style>', 96 "tablealignleft"=>' 97 <style:style style:name="tablealignleft" style:family="paragraph" style:parent-style-name="Table_20_Contents"> 98 <style:paragraph-properties fo:text-align="left"/> 99 </style:style>', 100 "tableheader"=>' 101 <style:style style:name="tableheader" style:family="table-cell"> 102 <style:table-cell-properties fo:padding="0.05cm" fo:border-left="0.002cm solid #000000" fo:border-right="0.002cm solid #000000" fo:border-top="0.002cm solid #000000" fo:border-bottom="0.002cm solid #000000"/> 103 </style:style>', 104 "tablecell"=>' 105 <style:style style:name="tablecell" style:family="table-cell"> 106 <style:table-cell-properties fo:padding="0.05cm" fo:border-left="0.002cm solid #000000" fo:border-right="0.002cm solid #000000" fo:border-top="0.002cm solid #000000" fo:border-bottom="0.002cm solid #000000"/> 107 </style:style>', 108 "legendcenter"=>' 109 <style:style style:name="legendcenter" style:family="paragraph" style:parent-style-name="Illustration"> 110 <style:paragraph-properties fo:text-align="center"/> 111 </style:style>', 112 ); 113 // Regular styles. May not be present if in template mode, in which case they will be added to styles.xml 114 var $styles = array( 115 "Source_20_Text"=>' 116 <style:style style:name="Source_20_Text" style:display-name="Source Text" style:family="text"> 117 <style:text-properties style:font-name="Bitstream Vera Sans Mono" style:font-name-asian="Bitstream Vera Sans Mono" style:font-name-complex="Bitstream Vera Sans Mono"/> 118 </style:style>', 119 "Preformatted_20_Text"=>' 120 <style:style style:name="Preformatted_20_Text" style:display-name="Preformatted Text" style:family="paragraph" style:parent-style-name="Standard" style:class="html"> 121 <style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.2cm"/> 122 <style:text-properties style:font-name="Bitstream Vera Sans Mono" style:font-name-asian="Bitstream Vera Sans Mono" style:font-name-complex="Bitstream Vera Sans Mono"/> 123 </style:style>', 124 "Source_20_Code"=>' 125 <style:style style:name="Source_20_Code" style:display-name="Source Code" style:family="paragraph" style:parent-style-name="Preformatted_20_Text"> 126 <style:paragraph-properties fo:padding="0.05cm" style:shadow="none" fo:border="0.002cm solid #8cacbb" fo:background-color="#f7f9fa"/> 127 </style:style>', 128 "Source_20_File"=>' 129 <style:style style:name="Source_20_File" style:display-name="Source File" style:family="paragraph" style:parent-style-name="Preformatted_20_Text"> 130 <style:paragraph-properties fo:padding="0.05cm" style:shadow="none" fo:border="0.002cm solid #8cacbb" fo:background-color="#f1f4f5"/> 131 </style:style>', 132 "Horizontal_20_Line"=>' 133 <style:style style:name="Horizontal_20_Line" style:display-name="Horizontal Line" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="html"> 134 <style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.5cm" style:border-line-width-bottom="0.002cm 0.035cm 0.002cm" fo:padding="0cm" fo:border-left="none" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.04cm double #808080" text:number-lines="false" text:line-number="0" style:join-border="false"/> 135 <style:text-properties fo:font-size="6pt" style:font-size-asian="6pt" style:font-size-complex="6pt"/> 136 </style:style>', 137 "Footnote"=>' 138 <style:style style:name="Footnote" style:family="paragraph" style:parent-style-name="Standard" style:class="extra"> 139 <style:paragraph-properties fo:margin-left="0.5cm" fo:margin-right="0cm" fo:text-indent="-0.5cm" style:auto-text-indent="false" text:number-lines="false" text:line-number="0"/> 140 <style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/> 141 </style:style>', 142 "Emphasis"=>' 143 <style:style style:name="Emphasis" style:family="text"> 144 <style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"/> 145 </style:style>', 146 "Strong_20_Emphasis"=>' 147 <style:style style:name="Strong_20_Emphasis" style:display-name="Strong Emphasis" style:family="text"> 148 <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/> 149 </style:style>', 150 ); 151 // Font definitions. May not be present if in template mode, in which case they will be added to styles.xml 152 var $fonts = array( 153 "StarSymbol"=>'<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>', // for bullets 154 "Bitstream Vera Sans Mono"=>'<style:font-face style:name="Bitstream Vera Sans Mono" svg:font-family="\'Bitstream Vera Sans Mono\'" style:font-family-generic="modern" style:font-pitch="fixed"/>', // for source code 155 ); 156 157 /** 158 * Return version info 159 */ 160 function getInfo(){ 161 return confToHash(dirname(__FILE__).'/info.txt'); 162 } 163 164 /** 165 * Returns the format produced by this renderer. 166 */ 167 function getFormat(){ 168 return "odp"; 169 } 170 171 /** 172 * Do not make multiple instances of this class 173 */ 174 function isSingleton(){ 175 return true; 176 } 177 178 179 /** 180 * Initialize the rendering 181 */ 182 function document_start() { 183 global $ID; 184 185 // If older or equal to 2007-06-26, we need to disable caching 186 $dw_version = preg_replace('/[^\d]/', '', getversion()); 187 if (version_compare($dw_version, "20070626", "<=")) { 188 $this->info["cache"] = false; 189 } 190 191 //DEBUG 192 $this->info["cache"] = false; 193 194 // prepare the zipper 195 $this->ZIP = new ZipLib(); 196 197 // prepare meta data 198 $this->meta = array( 199 'meta:generator' => 'DokuWiki '.getversion(), 200 'meta:initial-creator' => 'Generated', 201 'meta:creation-date' => date('Y-m-d\\TH::i:s', null), //FIXME 202 'dc:creator' => 'Generated', 203 'dc:date' => date('Y-m-d\\TH::i:s', null), 204 'dc:language' => 'en-US', 205 'meta:editing-cycles' => '1', 206 'meta:editing-duration' => 'PT0S', 207 ); 208 209 // prepare the table of content 210 $this->toc = ''; 211 $this->toc_counter = 0; 212 $this->toc_source = '<text:table-of-content-source text:outline-level="4">'; 213 $this->toc_source .= '<text:index-title-template text:style-name="Contents_20_Heading">Table des matières</text:index-title-template>'; 214 215 $this->toc_index = '<text:index-title text:style-name="Sect2" text:name="Table des matières1_Head">'; 216 $this->toc_index .= '<text:p text:style-name="Contents_20_Heading">Table des matières</text:p>'; 217 $this->toc_index .= '</text:index-title>'; 218 219 //$headers = array('Content-Type'=>'text/plain'); p_set_metadata($ID,array('format' => array('odp' => $headers) )); return ; // DEBUG 220 // send the content type header, new method after 2007-06-26 (handles caching) 221 $output_filename = str_replace(':','-',$ID).".odp"; 222 if (version_compare($dw_version, "20070626")) { 223 // store the content type headers in metadata 224 $headers = array( 225 'Content-Type' => 'application/vnd.oasis.opendocument.presentation', 226 'Content-Disposition' => 'attachment; filename="'.$output_filename.'";', 227 ); 228 p_set_metadata($ID,array('format' => array('odp' => $headers) )); 229 } else { // older method 230 header('Content-Type: application/vnd.oasis.opendocument.presentation'); 231 header('Content-Disposition: attachment; filename="'.$output_filename.'";'); 232 } 233 } 234 235 /** 236 * Prepare meta.xml 237 */ 238 function _odpMeta(){ 239 $value = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n"; 240 $value .= '<office:document-meta '; 241 $value .= 'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '; 242 $value .= 'xmlns:xlink="http://www.w3.org/1999/xlink" '; 243 $value .= 'xmlns:dc="http://purl.org/dc/elements/1.1/" '; 244 $value .= 'xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" '; 245 $value .= 'xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" '; 246 $value .= 'xmlns:ooo="http://openoffice.org/2004/office" '; 247 $value .= 'xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" '; 248 $value .= 'xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" '; 249 $value .= 'xmlns:grddl="http://www.w3.org/2003/g/data-view#" '; 250 $value .= 'xmlns:officeooo="http://openoffice.org/2009/office" '; 251 $value .= 'office:version="1.2" '; 252 $value .= 'grddl:transformation="http://docs.oasis-open.org/office/1.2/xslt/odf2rdf.xsl" >'; 253 $value .= '<office:meta>'; 254 # FIXME 255 # foreach($meta as $meta_key => $meta_value) 256 # $value .= '<' . $meta_key . '>' . ODUtils::encode($meta_value) . '</' . $meta_key . '>'; 257 $value .= '</office:meta>'; 258 $value .= '</office:document-meta>'; 259 $this->ZIP->add_File($value,'meta.xml'); 260 } 261 262 /** 263 * Prepare manifest.xml 264 */ 265 function _odpManifest(){ 266 $value = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n"; 267 $value .= '<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">'; 268 $value .= '<manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.presentation" manifest:version="1.2" manifest:full-path="/"/>'; 269 $value .= '<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="settings.xml"/>'; 270 $value .= '<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/>'; 271 $value .= '<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>'; 272 $value .= '<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>'; 273 274 foreach($this->manifest as $path => $type){ 275 $value .= '<manifest:file-entry manifest:media-type="'.$this->_xmlEntities($type). 276 '" manifest:full-path="'.$this->_xmlEntities($path).'"/>'; 277 } 278 279 $value .= '</manifest:manifest>'; 280 $this->ZIP->add_File($value,'META-INF/manifest.xml'); 281 } 282 283 /** 284 * Prepare settings.xml 285 */ 286 function _odpSettings(){ 287 $value = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n"; 288 $value .= '<office:document-settings '; 289 $value .= 'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '; 290 $value .= 'xmlns:xlink="http://www.w3.org/1999/xlink" '; 291 $value .= 'xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" '; 292 $value .= 'xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" '; 293 $value .= 'xmlns:ooo="http://openoffice.org/2004/office" '; 294 $value .= 'xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" '; 295 $value .= 'xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" '; 296 $value .= 'xmlns:officeooo="http://openoffice.org/2009/office" '; 297 $value .= 'office:version="1.2">'; 298 $value .= '<office:settings>'; 299 $value .= '<config:config-item-set config:name="dummy-settings">'; 300 $value .= '<config:config-item config:name="MakeValidatorHappy" config:type="boolean">'; 301 $value .= 'true'; 302 $value .= '</config:config-item>'; 303 $value .= '</config:config-item-set>'; 304 $value .= '</office:settings>'; 305 $value .= '</office:document-settings>'; 306 $this->ZIP->add_File($value,'settings.xml'); 307 } 308 309 310 311 312 /** 313 * Closes the document 314 */ 315 function document_end(){ 316 global $conf; 317 //$this->doc .= $this->_odpAutoStyles(); return; // DEBUG 318 if ($this->template) { // template chosen 319 if (file_exists($conf['mediadir'].'/'.$this->getConf("tpl_dir")."/".$this->template)) { //template found 320 $this->document_end_template(); 321 } else { // template chosen but not found : warn the user and use the default template 322 $this->doc = '<text:p text:style-name="Text_20_body"><text:span text:style-name="Strong_20_Emphasis">' 323 .$this->_xmlEntities( sprintf($this->getLang('tpl_not_found'),$this->template,$this->getConf("tpl_dir")) ) 324 .'</text:span></text:p>'.$this->doc; 325 $this->document_end_scratch(); 326 } 327 } else { 328 $this->document_end_scratch(); 329 } 330 $this->doc = $this->ZIP->get_file(); 331 } 332 333 334 /** 335 * Closes the document when not using a template 336 */ 337 function document_end_scratch(){ 338 $autostyles = $this->_odpAutoStyles(); 339 $userfields = $this->_odpUserFields(); 340 341 // add defaults 342 $this->ZIP->add_File('application/vnd.oasis.opendocument.presentation', 'mimetype', 0); 343 344 $this->_odpMeta(); 345 $this->_odpSettings(); 346 347 $value = '<' . '?xml version="1.0" encoding="UTF-8"?' . ">\n"; 348 $value .= '<office:document-content '; 349 $value .= 'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '; 350 $value .= 'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" '; 351 $value .= 'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" '; 352 $value .= 'xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" '; 353 $value .= 'xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" '; 354 $value .= 'xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" '; 355 $value .= 'xmlns:xlink="http://www.w3.org/1999/xlink" '; 356 $value .= 'xmlns:dc="http://purl.org/dc/elements/1.1/" '; 357 $value .= 'xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" '; 358 $value .= 'xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" '; 359 $value .= 'xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" '; 360 $value .= 'xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" '; 361 $value .= 'xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" '; 362 $value .= 'xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" '; 363 $value .= 'xmlns:math="http://www.w3.org/1998/Math/MathML" '; 364 $value .= 'xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" '; 365 $value .= 'xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" '; 366 $value .= 'xmlns:ooo="http://openoffice.org/2004/office" '; 367 $value .= 'xmlns:ooow="http://openoffice.org/2004/writer" '; 368 $value .= 'xmlns:oooc="http://openoffice.org/2004/calc" '; 369 $value .= 'xmlns:dom="http://www.w3.org/2001/xml-events" '; 370 $value .= 'xmlns:xforms="http://www.w3.org/2002/xforms" '; 371 $value .= 'xmlns:xsd="http://www.w3.org/2001/XMLSchema" '; 372 $value .= 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '; 373 $value .= 'xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" '; 374 $value .= 'xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" '; 375 $value .= 'xmlns:rpt="http://openoffice.org/2005/report" '; 376 $value .= 'xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" '; 377 $value .= 'xmlns:xhtml="http://www.w3.org/1999/xhtml" '; 378 $value .= 'xmlns:grddl="http://www.w3.org/2003/g/data-view#" '; 379 $value .= 'xmlns:officeooo="http://openoffice.org/2009/office" '; 380 $value .= 'xmlns:tableooo="http://openoffice.org/2009/table" '; 381 $value .= 'xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" '; 382 $value .= 'xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" '; 383 $value .= 'office:version="1.2" '; 384 $value .= 'grddl:transformation="http://docs.oasis-open.org/office/1.2/xslt/odf2rdf.xsl" '; 385 $value .= '>'; 386 $value .= '<office:scripts/>'; 387 $value .= $autostyles; 388 $value .= '<office:body>'; 389 $value .= '<office:presentation>'; 390 391 $value .= $userfields; 392 393 $value .= $this->doc; 394 $value .= '</draw:page>'; 395 $value .= '</office:presentation>'; 396 $value .= '</office:body>'; 397 $value .= '</office:document-content>'; 398 399 $this->ZIP->add_File($value,'content.xml'); 400 401 $value = io_readFile(DOKU_PLUGIN.'odp/styles.xml'); 402 $value = str_replace('<office:automatic-styles/>', $autostyles, $value); 403 $this->ZIP->add_File($value,'styles.xml'); 404 405 // build final manifest 406 $this->_odpManifest(); 407 } 408 409 /** 410 * Closes the document using a template 411 */ 412 function document_end_template(){ 413 global $conf, $ID; // for the temp dir 414 415 // Temp dir 416 if (is_dir($conf['tmpdir'])) { 417 $temp_dir = $conf['tmpdir']; // version > 20070626 418 } else { 419 $temp_dir = $conf['savedir'].'/cache/tmp'; // version <= 20070626 420 } 421 $this->temp_dir = $temp_dir."/odp/".str_replace(':','-',$ID); 422 if (is_dir($this->temp_dir)) { $this->io_rm_rf($this->temp_dir); } 423 io_mkdir_p($this->temp_dir); 424 425 // Extract template 426 $template_path = $conf['mediadir'].'/'.$this->getConf("tpl_dir")."/".$this->template; 427 $this->ZIP->Extract($template_path, $this->temp_dir); 428 429 // Prepare content 430 $autostyles = $this->_odpAutoStyles(); 431 $missingstyles = $this->_odpStyles(); 432 $missingfonts = $this->_odpFonts(); 433 $userfields = $this->_odpUserFields(); 434 435 // Insert content 436 $old_content = io_readFile($this->temp_dir.'/content.xml'); 437 if (strpos($old_content, 'DOKUWIKI-ODP-INSERT') !== FALSE) { // Replace the mark 438 $this->_odpReplaceInFile('/<text:p[^>]*>DOKUWIKI-ODP-INSERT<\/text:p>/', 439 $this->doc, $this->temp_dir.'/content.xml', true); 440 } else { // Append to the template 441 $this->_odpReplaceInFile('</office:presentation>', $this->doc.'</office:presentation>', $this->temp_dir.'/content.xml'); 442 } 443 444 $this->render_TOC(); 445 446 // Cut off unwanted content 447 if (strpos($old_content, 'DOKUWIKI-ODP-CUT-START') !== FALSE 448 && strpos($old_content, 'DOKUWIKI-ODP-CUT-STOP') !== FALSE) { 449 $this->_odpReplaceInFile('/DOKUWIKI-ODP-CUT-START.*DOKUWIKI-ODP-CUT-STOP/', 450 '', $this->temp_dir.'/content.xml', true); 451 } 452 453 // Insert userfields 454 if (strpos($old_content, "text:user-field-decls") === FALSE) { // no existing userfields 455 $this->_odpReplaceInFile('/<office:text([^>]*)>/U', '<office:text\1>'.$userfields, $this->temp_dir.'/content.xml', TRUE); 456 } else { 457 $this->_odpReplaceInFile('</text:user-field-decls>', substr($userfields,23), $this->temp_dir.'/content.xml'); 458 } 459 460 // Insert styles & fonts 461 $this->_odpReplaceInFile('</office:automatic-styles>', substr($autostyles, 25), $this->temp_dir.'/content.xml'); 462 $this->_odpReplaceInFile('</office:automatic-styles>', substr($autostyles, 25), $this->temp_dir.'/styles.xml'); 463 $this->_odpReplaceInFile('</office:styles>', $missingstyles.'</office:styles>', $this->temp_dir.'/styles.xml'); 464 $this->_odpReplaceInFile('</office:font-face-decls>', $missingfonts.'</office:font-face-decls>', $this->temp_dir.'/styles.xml'); 465 466 // Build the Zip 467 $this->ZIP->Compress(null, $this->temp_dir, null); 468 $this->io_rm_rf($this->temp_dir); 469 } 470 471 function _odpReplaceInFile($from, $to, $file, $regexp=FALSE) { 472 $value = io_readFile($file); 473 if ($regexp) { 474 $value = preg_replace($from, $to, $value); 475 } else { 476 $value = str_replace($from, $to, $value); 477 } 478 $file_f = fopen($file, 'w'); 479 fwrite($file_f, $value); 480 fclose($file_f); 481 } 482 483 /** 484 * Recursively deletes a directory (equivalent to the "rm -rf" command) 485 * Found in comments on http://www.php.net/rmdir 486 */ 487 function io_rm_rf($f) { 488 if (is_dir($f)) { 489 foreach(glob($f.'/*') as $sf) { 490 if (is_dir($sf) && !is_link($sf)) { 491 $this->io_rm_rf($sf); 492 } else { 493 unlink($sf); 494 } 495 } 496 } else { // avoid nasty consequenses if something wrong is given 497 die("Error: not a directory - $f"); 498 } 499 rmdir($f); 500 } 501 502 // Build the table of content 503 function render_TOC() { 504 } 505 506 function toc_additem($id, $text, $level) { 507 } 508 509 function _odpAutoStyles() { 510 $value = '<office:automatic-styles>'; 511 foreach ($this->autostyles as $stylename=>$stylexml) { 512 $value .= $stylexml; 513 } 514 $value .= '</office:automatic-styles>'; 515 return $value; 516 } 517 518 function _odpUserFields() { 519 $value = '<text:user-field-decls>'; 520 foreach ($this->fields as $fname=>$fvalue) { 521 $value .= '<text:user-field-decl office:value-type="string" text:name="'.$fname.'" office:string-value="'.$fvalue.'"/>'; 522 } 523 $value .= '</text:user-field-decls>'; 524 return $value; 525 } 526 527 /* Add missing styles in the template */ 528 function _odpStyles() { 529 $value = ''; 530 $existing_styles = io_readFile($this->temp_dir.'/styles.xml'); 531 foreach ($this->styles as $stylename=>$stylexml) { 532 if (strpos($existing_styles, 'style:name="'.$stylename.'"') === FALSE) { 533 $value .= $stylexml; 534 } 535 } 536 // Loop on bullet/numerotation styles 537 if (strpos($existing_styles, 'style:name="List_20_1"') === FALSE) { 538 $value .= '<text:list-style style:name="List_20_1" style:display-name="List 1">'; 539 for ($i=1;$i<=10;$i++) { 540 $value .= '<text:list-level-style-bullet text:level="'.$i.'" text:style-name="Numbering_20_Symbols" text:bullet-char="•"> 541 <style:list-level-properties text:space-before="'.(0.4*($i-1)).'cm" text:min-label-width="0.4cm"/> 542 <style:text-properties style:font-name="StarSymbol"/> 543 </text:list-level-style-bullet>'; 544 } 545 $value .= '</text:list-style>'; 546 } 547 if (strpos($existing_styles, 'style:name="Numbering_20_1"') === FALSE) { 548 $value .= '<text:list-style style:name="Numbering_20_1" style:display-name="Numbering 1">'; 549 for ($i=1;$i<=10;$i++) { 550 $value .= '<text:list-level-style-number text:level="'.$i.'" text:style-name="Numbering_20_Symbols" style:num-suffix="." style:num-format="1"> 551 <style:list-level-properties text:space-before="'.(0.5*($i-1)).'cm" text:min-label-width="0.5cm"/> 552 </text:list-level-style-number>'; 553 } 554 $value .= '</text:list-style>'; 555 } 556 return $value; 557 } 558 559 /* Add missing fonts in the template */ 560 function _odpFonts() { 561 $value = ''; 562 $existing_styles = io_readFile($this->temp_dir.'/styles.xml'); 563 foreach ($this->fonts as $name=>$xml) { 564 if (strpos($existing_styles, 'style:name="'.$name.'"') === FALSE) { 565 $value .= $xml; 566 } 567 } 568 return $value; 569 } 570 571 function cdata($text) { 572 $this->doc .= $this->_xmlEntities($text); 573 } 574 575 function p_open($style='Text_20_body'){ 576 if (!$this->in_paragraph) { // opening a paragraph inside another paragraph is illegal 577 $this->in_paragraph = true; 578 $this->doc .= '<text:p text:style-name="'.$style.'">'; 579 } 580 } 581 582 function p_close(){ 583 if ($this->in_paragraph) { 584 $this->in_paragraph = false; 585 $this->doc .= '</text:p>'; 586 } 587 } 588 589 function header($text, $level, $pos){ 590 // H1 and H2 create new slides 591 if($level <3){ 592 if($this->slideopen){ 593 //close previous slide 594 $this->doc .= '</draw:page>'; 595 } 596 //open the new slide 597 $this->doc .= '<draw:page draw:name="page1" draw:style-name="dp1" draw:master-page-name="Default">'; 598 $this->slideopen = true; 599 } 600 // write the header 601 $this->doc.='<draw:frame presentation:style-name="pr4" draw:layer="layout" svg:width="25.199cm" svg:height="3.256cm" svg:x="1.4cm" svg:y="0.962cm" presentation:class="title">'; 602 $this->doc.='<draw:text-box>'; 603 $this->doc.='<text:p>'; 604 $this->doc.= $this->_xmlEntities($text); 605 $this->doc.='</text:p>'; 606 $this->doc.='</draw:text-box>'; 607 $this->doc.='</draw:frame>'; 608 609/* 610 $this->doc .= '<presentation:notes draw:style-name="dp2">'; 611 $this->doc .= '<draw:page-thumbnail draw:style-name="gr1" draw:layer="layout" svg:width="14.848cm" svg:height="11.136cm" svg:x="3.075cm" svg:y="2.257cm" draw:page-number="1" presentation:class="page"/>'; 612 $this->doc .= '<draw:frame presentation:style-name="pr1" draw:layer="layout" svg:width="16.799cm" svg:height="13.365cm" svg:x="2.1cm" svg:y="14.107cm" presentation:class="notes" presentation:placeholder="true">'; 613 $this->doc .= '<draw:text-box/>'; 614 $this->doc .= '</draw:frame>'; 615 $this->doc .= '</presentation:notes>'; 616*/ 617 } 618 619 /** 620 * A line stops the slide and start the handout section 621 */ 622 function hr() { 623 //$this->doc .= '</div>'; 624 //$this->doc .= '<div class="handout">'.DOKU_LF; 625 } 626 627 function linebreak() { 628 $this->doc .= '<text:line-break/>'; 629 } 630 631 function strong_open() { 632 $this->doc .= '<text:span text:style-name="Strong_20_Emphasis">'; 633 } 634 635 function strong_close() { 636 $this->doc .= '</text:span>'; 637 } 638 639 function emphasis_open() { 640 $this->doc .= '<text:span text:style-name="Emphasis">'; 641 } 642 643 function emphasis_close() { 644 $this->doc .= '</text:span>'; 645 } 646 647 function underline_open() { 648 $this->doc .= '<text:span text:style-name="underline">'; 649 } 650 651 function underline_close() { 652 $this->doc .= '</text:span>'; 653 } 654 655 function monospace_open() { 656 $this->doc .= '<text:span text:style-name="Source_20_Text">'; 657 } 658 659 function monospace_close() { 660 $this->doc .= '</text:span>'; 661 } 662 663 function subscript_open() { 664 $this->doc .= '<text:span text:style-name="sub">'; 665 } 666 667 function subscript_close() { 668 $this->doc .= '</text:span>'; 669 } 670 671 function superscript_open() { 672 $this->doc .= '<text:span text:style-name="sup">'; 673 } 674 675 function superscript_close() { 676 $this->doc .= '</text:span>'; 677 } 678 679 function deleted_open() { 680 $this->doc .= '<text:span text:style-name="del">'; 681 } 682 683 function deleted_close() { 684 $this->doc .= '</text:span>'; 685 } 686 687 /* 688 * Tables 689 */ 690 function table_open($maxcols = NULL, $numrows = NULL){ 691 $this->doc .= '<table:table>'; 692 for($i=0; $i<$maxcols; $i++){ 693 $this->doc .= '<table:table-column />'; 694 } 695 } 696 697 function table_close(){ 698 $this->doc .= '</table:table>'; 699 } 700 701 function tablerow_open(){ 702 $this->doc .= '<table:table-row>'; 703 } 704 705 function tablerow_close(){ 706 $this->doc .= '</table:table-row>'; 707 } 708 709 function tableheader_open($colspan = 1, $align = "left"){ 710 $this->doc .= '<table:table-cell office:value-type="string" table:style-name="tableheader" '; 711 //$this->doc .= ' table:style-name="tablealign'.$align.'"'; 712 if ( $colspan > 1 ) { 713 $this->doc .= ' table:number-columns-spanned="'.$colspan.'"'; 714 } 715 $this->doc .= '>'; 716 $this->p_open('Table_20_Heading'); 717 } 718 719 function tableheader_close(){ 720 $this->p_close(); 721 $this->doc .= '</table:table-cell>'; 722 } 723 724 function tablecell_open($colspan = 1, $align = "left"){ 725 $this->doc .= '<table:table-cell office:value-type="string" table:style-name="tablecell" '; 726 if ( $colspan > 1 ) { 727 $this->doc .= ' table:number-columns-spanned="'.$colspan.'"'; 728 } 729 $this->doc .= '>'; 730 if (!$align) $align = "left"; 731 $style = "tablealign".$align; 732 $this->p_open($style); 733 } 734 735 function tablecell_close(){ 736 $this->p_close(); 737 $this->doc .= '</table:table-cell>'; 738 } 739 740 /** 741 * Callback for footnote start syntax 742 * 743 * All following content will go to the footnote instead of 744 * the document. To achieve this the previous rendered content 745 * is moved to $store and $doc is cleared 746 * 747 * @author Andreas Gohr <andi@splitbrain.org> 748 */ 749 function footnote_open() { 750 751 // move current content to store and record footnote 752 $this->store = $this->doc; 753 $this->doc = ''; 754 } 755 756 /** 757 * Callback for footnote end syntax 758 * 759 * All rendered content is moved to the $footnotes array and the old 760 * content is restored from $store again 761 * 762 * @author Andreas Gohr 763 */ 764 function footnote_close() { 765 // recover footnote into the stack and restore old content 766 $footnote = $this->doc; 767 $this->doc = $this->store; 768 $this->store = ''; 769 770 // check to see if this footnote has been seen before 771 $i = array_search($footnote, $this->footnotes); 772 773 if ($i === false) { 774 $i = count($this->footnotes); 775 // its a new footnote, add it to the $footnotes array 776 $this->footnotes[$i] = $footnote; 777 778 $this->doc .= '<text:note text:id="ftn'.$i.'" text:note-class="footnote">'; 779 $this->doc .= '<text:note-citation>'.($i+1).'</text:note-citation>'; 780 $this->doc .= '<text:note-body>'; 781 $this->doc .= '<text:p text:style-name="Footnote">'; 782 $this->doc .= $footnote; 783 $this->doc .= '</text:p>'; 784 $this->doc .= '</text:note-body>'; 785 $this->doc .= '</text:note>'; 786 787 } else { 788 // seen this one before - just reference it FIXME: style isn't correct yet 789 $this->doc .= '<text:note-ref text:note-class="footnote" text:ref-name="ftn'.$i.'">'.($i+1).'</text:note-ref>'; 790 } 791 } 792 793 function listu_open() { 794 $this->p_close(); 795 $this->doc .= '<text:list text:style-name="List_20_1">'; 796 } 797 798 function listu_close() { 799 $this->doc .= '</text:list>'; 800 } 801 802 function listo_open() { 803 $this->p_close(); 804 $this->doc .= '<text:list text:style-name="Numbering_20_1">'; 805 } 806 807 function listo_close() { 808 $this->doc .= '</text:list>'; 809 } 810 811 function listitem_open($level) { 812 $this->in_list_item = true; 813 $this->doc .= '<text:list-item>'; 814 } 815 816 function listitem_close() { 817 $this->in_list_item = false; 818 $this->doc .= '</text:list-item>'; 819 } 820 821 function listcontent_open() { 822 $this->doc .= '<text:p text:style-name="Text_20_body">'; 823 } 824 825 function listcontent_close() { 826 $this->doc .= '</text:p>'; 827 } 828 829 function unformatted($text) { 830 $this->doc .= $this->_xmlEntities($text); 831 } 832 833 function acronym($acronym) { 834 $this->doc .= $this->_xmlEntities($acronym); 835 } 836 837 function smiley($smiley) { 838 if ( array_key_exists($smiley, $this->smileys) ) { 839 $src = DOKU_INC."lib/images/smileys/".$this->smileys[$smiley]; 840 $this->_odpAddImage($src); 841 } else { 842 $this->doc .= $this->_xmlEntities($smiley); 843 } 844 } 845 846 function entity($entity) { 847 # UTF-8 entity decoding is broken in PHP <5 848 if (version_compare(phpversion(), "5.0.0") and array_key_exists($entity, $this->entities) ) { 849 # decoding may fail for missing Multibyte-Support in entity_decode 850 $dec = @html_entity_decode($this->entities[$entity],ENT_NOQUOTES,'UTF-8'); 851 if($dec){ 852 $this->doc .= $this->_xmlEntities($dec); 853 }else{ 854 $this->doc .= $this->_xmlEntities($entity); 855 } 856 } else { 857 $this->doc .= $this->_xmlEntities($entity); 858 } 859 } 860 861 function multiplyentity($x, $y) { 862 $this->doc .= $x.'×'.$y; 863 } 864 865 function singlequoteopening() { 866 global $lang; 867 $this->doc .= $lang['singlequoteopening']; 868 } 869 870 function singlequoteclosing() { 871 global $lang; 872 $this->doc .= $lang['singlequoteclosing']; 873 } 874 875 function apostrophe() { 876 global $lang; 877 $this->doc .= $lang['apostrophe']; 878 } 879 880 function doublequoteopening() { 881 global $lang; 882 $this->doc .= $lang['doublequoteopening']; 883 } 884 885 function doublequoteclosing() { 886 global $lang; 887 $this->doc .= $lang['doublequoteclosing']; 888 } 889 890 function php($text, $wrapper='dummy') { 891 $this->monospace_open(); 892 $this->doc .= $this->_xmlEntities($text); 893 $this->monospace_close(); 894 } 895 function phpblock($text) { 896 $this->file($text); 897 } 898 899 function html($text, $wrapper='dummy') { 900 $this->monospace_open(); 901 $this->doc .= $this->_xmlEntities($text); 902 $this->monospace_close(); 903 } 904 function htmlblock($text) { 905 $this->file($text); 906 } 907 908 /** 909 * static call back to replace spaces 910 */ 911 function _preserveSpace($matches){ 912 $spaces = $matches[1]; 913 $len = strlen($spaces); 914 return '<text:s text:c="'.$len.'"/>'; 915 } 916 917 function preformatted($text) { 918 $this->_preformatted($text); 919 } 920 921 function file($text, $language=null, $filename=null) { 922 $this->_highlight('file', $text, $language); 923 } 924 925 function quote_open() { 926 if (!$this->in_paragraph) { // only start a new par if we're not already in one 927 $this->p_open(); 928 } 929 $this->doc .= ">"; 930 } 931 932 function quote_close() { 933 if ($this->in_paragraph) { // only close the paragraph if we're actually in one 934 $this->p_close(); 935 } 936 } 937 938 function code($text, $language=null, $filename=null) { 939 $this->_highlight('code', $text, $language); 940 } 941 942 function _preformatted($text, $style="Preformatted_20_Text", $notescaped=true) { 943 if ($notescaped) { 944 $text = $this->_xmlEntities($text); 945 } 946 if (strpos($text, "\n") !== FALSE and strpos($text, "\n") == 0) { 947 // text starts with a newline, remove it 948 $text = substr($text,1); 949 } 950 $text = str_replace("\n",'<text:line-break/>',$text); 951 $text = preg_replace_callback('/( +)/',array('renderer_plugin_odp','_preserveSpace'),$text); 952 953 if ($this->in_list_item) { // if we're in a list item, we must close the <text:p> tag 954 $this->doc .= '</text:p>'; 955 $this->doc .= '<text:p text:style-name="'.$style.'">'; 956 $this->doc .= $text; 957 $this->doc .= '</text:p>'; 958 $this->doc .= '<text:p>'; 959 } else { 960 $this->doc .= '<text:p text:style-name="'.$style.'">'; 961 $this->doc .= $text; 962 $this->doc .= '</text:p>'; 963 } 964 } 965 966 function _highlight($type, $text, $language=null) { 967 global $conf; 968 $style_name = "Source_20_Code"; 969 if ($type == "file") $style_name = "Source_20_File"; 970 971 if (is_null($language)) { 972 $this->_preformatted($text, $style_name); 973 return; 974 } 975 976 // from inc/parserutils.php:p_xhtml_cached_geshi() 977 require_once(DOKU_INC . 'inc/geshi.php'); 978 $geshi = new GeSHi($text, $language, DOKU_INC . 'inc/geshi'); 979 $geshi->set_encoding('utf-8'); 980 // $geshi->enable_classes(); DO NOT WANT ! 981 $geshi->set_header_type(GESHI_HEADER_PRE); 982 $geshi->enable_keyword_links(false); 983 984 // remove GeSHi's wrapper element (we'll replace it with our own later) 985 // we need to use a GeSHi wrapper to avoid <BR> throughout the highlighted text 986 $highlighted_code = trim(preg_replace('!^<pre[^>]*>|</pre>$!','',$geshi->parse_code()),"\n\r"); 987 // remove useless leading and trailing whitespace-newlines 988 $highlighted_code = preg_replace('/^ \n/','',$highlighted_code); 989 $highlighted_code = preg_replace('/\n $/','',$highlighted_code); 990 // replace styles 991 $highlighted_code = str_replace("</span>", "</text:span>", $highlighted_code); 992 $highlighted_code = preg_replace_callback('/<span style="([^"]+)">/', array('renderer_plugin_odp','_convert_css_styles'), $highlighted_code); 993 // cleanup leftover span tags 994 $highlighted_code = preg_replace('/<span[^>]*>/', "<text:span>", $highlighted_code); 995 $highlighted_code = str_replace(" ", " ", $highlighted_code); 996 $this->_preformatted($highlighted_code, $style_name, false); 997 } 998 999 function _convert_css_styles($matches) { 1000 $all_css_styles = $matches[1]; 1001 // parse the CSS attribute 1002 $css_styles = array(); 1003 foreach(explode(";", $all_css_styles) as $css_style) { 1004 $css_style_array = explode(":", $css_style); 1005 if (!trim($css_style_array[0]) or !trim($css_style_array[1])) { 1006 continue; 1007 } 1008 $css_styles[trim($css_style_array[0])] = trim($css_style_array[1]); 1009 } 1010 // create the ODP xml style 1011 $style_name = "highlight." . $this->highlight_style_num; 1012 $this->highlight_style_num += 1; 1013 $style_content = ' 1014 <style:style style:name="'.$style_name.'" style:family="text"> 1015 <style:text-properties '; 1016 foreach($css_styles as $style_key=>$style_value) { 1017 // Hats off to those who thought out the OpenDocument spec: styling syntax is similar to CSS ! 1018 $style_content .= 'fo:'.$style_key.'="'.$style_value.'" '; 1019 } 1020 $style_content .= '/> 1021 </style:style>'; 1022 // add the style to the library 1023 $this->autostyles[$style_name] = $style_content; 1024 // now make use of the new style 1025 return '<text:span text:style-name="'.$style_name.'">'; 1026 } 1027 1028 function internalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 1029 $height=NULL, $cache=NULL, $linking=NULL) { 1030 global $conf; 1031 global $ID; 1032 resolve_mediaid(getNS($ID),$src, $exists); 1033 list($ext,$mime) = mimetype($src); 1034 1035 if(substr($mime,0,5) == 'image'){ 1036 $file = mediaFN($src); 1037 $this->_odpAddImage($file, $width, $height, $align, $title); 1038 }else{ 1039 // FIXME build absolute medialink and call externallink() 1040 $this->code('FIXME internalmedia: '.$src); 1041 } 1042 } 1043 1044 function externalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 1045 $height=NULL, $cache=NULL, $linking=NULL) { 1046 global $conf; 1047 global $ID; 1048 list($ext,$mime) = mimetype($src); 1049 1050 if(substr($mime,0,5) == 'image'){ 1051 if($width) { 1052 $width = 'svg:width="'.(($width/96.0)*2.54).'cm"'; 1053 } else { 1054 $width = 'svg:rel-width="100%"'; 1055 } 1056 if($height) { 1057 $height = 'svg:height="'.(($height/96.0)*2.54).'cm"'; 1058 } else { 1059 $height = 'svg:rel-height="100%"'; 1060 } 1061 1062 $style = 'media'.$align; 1063 if($align){ 1064 $anchor = 'paragraph'; 1065 }else{ 1066 $anchor = 'as-char'; 1067 } 1068 1069 $this->doc .= '<draw:frame draw:style-name="'.$style.'" draw:name="'.$this->_xmlEntities($title).'" 1070 text:anchor-type="'.$anchor.'" draw:z-index="0" 1071 '.$width.' '.$height.' >'; 1072 $this->doc .= '<draw:image xlink:href="'.$this->_xmlEntities($src).'" 1073 xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>'; 1074 $this->doc .= '</draw:frame>'; 1075 }else{ 1076 $this->externallink($src,$title); 1077 } 1078 } 1079 1080 function camelcaselink($link) { 1081 $this->internallink($link,$link); 1082 } 1083 1084 function reference($id, $name = NULL) { 1085 $this->doc .= '<text:a xlink:type="simple" xlink:href="#'.$id.'"'; 1086 if ($name) { 1087 $this->doc .= '>'.$this->_xmlEntities($name).'</text:a>'; 1088 } else { 1089 $this->doc .= '/>'; 1090 } 1091 } 1092 1093 /** 1094 * Render an internal Wiki Link 1095 * 1096 * @author Andreas Gohr <andi@splitbrain.org> 1097 */ 1098 function internallink($id, $name = NULL) { 1099 global $conf; 1100 global $ID; 1101 // default name is based on $id as given 1102 $default = $this->_simpleTitle($id); 1103 // now first resolve and clean up the $id 1104 resolve_pageid(getNS($ID),$id,$exists); 1105 $name = $this->_getLinkTitle($name, $default, $isImage, $id); 1106 1107 // build the absolute URL (keeping a hash if any) 1108 list($id,$hash) = explode('#',$id,2); 1109 $url = wl($id,'',true); 1110 if($hash) $url .='#'.$hash; 1111 1112 if ($ID == $id) { 1113 $this->reference($hash, $name); 1114 } else { 1115 $this->_doLink($url,$name); 1116 } 1117 } 1118 1119 /** 1120 * Add external link 1121 */ 1122 function externallink($url, $name = NULL) { 1123 global $conf; 1124 1125 $name = $this->_getLinkTitle($name, $url, $isImage); 1126 1127 $this->_doLink($url,$name); 1128 } 1129 1130 /** 1131 * Just print local links 1132 * 1133 * @fixme add image handling 1134 */ 1135 function locallink($hash, $name = NULL){ 1136 $name = $this->_getLinkTitle($name, $hash, $isImage); 1137 $this->doc .= $name; 1138 } 1139 1140 /** 1141 * InterWiki links 1142 */ 1143 function interwikilink($match, $name = NULL, $wikiName, $wikiUri) { 1144 $name = $this->_getLinkTitle($name, $wikiUri, $isImage); 1145 $url = $this-> _resolveInterWiki($wikiName,$wikiUri); 1146 $this->_doLink($url,$name); 1147 } 1148 1149 /** 1150 * Just print WindowsShare links 1151 * 1152 * @fixme add image handling 1153 */ 1154 function windowssharelink($url, $name = NULL) { 1155 $name = $this->_getLinkTitle($name, $url, $isImage); 1156 $this->doc .= $name; 1157 } 1158 1159 /** 1160 * Just print email links 1161 * 1162 * @fixme add image handling 1163 */ 1164 function emaillink($address, $name = NULL) { 1165 $name = $this->_getLinkTitle($name, $address, $isImage); 1166 $this->_doLink("mailto:".$address,$name); 1167 } 1168 1169 /** 1170 * Add a hyperlink, handling Images correctly 1171 * 1172 * @author Andreas Gohr <andi@splitbrain.org> 1173 */ 1174 function _doLink($url,$name){ 1175 $url = $this->_xmlEntities($url); 1176 if(is_array($name)){ 1177 // Images 1178 if($url) $this->doc .= '<draw:a xlink:type="simple" xlink:href="'.$url.'">'; 1179 1180 if($name['type'] == 'internalmedia'){ 1181 $this->internalmedia($name['src'], 1182 $name['title'], 1183 $name['align'], 1184 $name['width'], 1185 $name['height'], 1186 $name['cache'], 1187 $name['linking']); 1188 } 1189 1190 if($url) $this->doc .= '</draw:a>'; 1191 }else{ 1192 // Text 1193 if($url) $this->doc .= '<text:a xlink:type="simple" xlink:href="'.$url.'">'; 1194 $this->doc .= $name; // we get the name already XML encoded 1195 if($url) $this->doc .= '</text:a>'; 1196 } 1197 } 1198 1199 /** 1200 * Construct a title and handle images in titles 1201 * 1202 * @author Harry Fuecks <hfuecks@gmail.com> 1203 */ 1204 function _getLinkTitle($title, $default, & $isImage, $id=null) { 1205 global $conf; 1206 1207 $isImage = false; 1208 if ( is_null($title) ) { 1209 if ($conf['useheading'] && $id) { 1210 $heading = p_get_first_heading($id); 1211 if ($heading) { 1212 return $this->_xmlEntities($heading); 1213 } 1214 } 1215 return $this->_xmlEntities($default); 1216 } else if ( is_string($title) ) { 1217 return $this->_xmlEntities($title); 1218 } else if ( is_array($title) ) { 1219 $isImage = true; 1220 return $title; 1221 } 1222 } 1223 1224 /** 1225 * Creates a linkid from a headline 1226 * 1227 * @param string $title The headline title 1228 * @param boolean $create Create a new unique ID? 1229 * @author Andreas Gohr <andi@splitbrain.org> 1230 */ 1231 function _headerToLink($title,$create=false) { 1232 $title = str_replace(':','',cleanID($title)); 1233 $title = ltrim($title,'0123456789._-'); 1234 if(empty($title)) $title='section'; 1235 1236 if($create){ 1237 // make sure tiles are unique 1238 $num = ''; 1239 while(in_array($title.$num,$this->headers)){ 1240 ($num) ? $num++ : $num = 1; 1241 } 1242 $title = $title.$num; 1243 $this->headers[] = $title; 1244 } 1245 1246 return $title; 1247 } 1248 1249 1250 function _xmlEntities($value) { 1251 return str_replace( array('&','"',"'",'<','>'), array('&','"',''','<','>'), $value); 1252 } 1253 1254 function rss ($url,$params){ 1255 global $lang; 1256 global $conf; 1257 1258 require_once(DOKU_INC.'inc/FeedParser.php'); 1259 $feed = new FeedParser(); 1260 $feed->feed_url($url); 1261 1262 //disable warning while fetching 1263 if (!defined('DOKU_E_LEVEL')) { $elvl = error_reporting(E_ERROR); } 1264 $rc = $feed->init(); 1265 if (!defined('DOKU_E_LEVEL')) { error_reporting($elvl); } 1266 1267 //decide on start and end 1268 if($params['reverse']){ 1269 $mod = -1; 1270 $start = $feed->get_item_quantity()-1; 1271 $end = $start - ($params['max']); 1272 $end = ($end < -1) ? -1 : $end; 1273 }else{ 1274 $mod = 1; 1275 $start = 0; 1276 $end = $feed->get_item_quantity(); 1277 $end = ($end > $params['max']) ? $params['max'] : $end;; 1278 } 1279 1280 $this->listu_open(); 1281 if($rc){ 1282 for ($x = $start; $x != $end; $x += $mod) { 1283 $item = $feed->get_item($x); 1284 $this->listitem_open(0); 1285 $this->listcontent_open(); 1286 $this->externallink($item->get_permalink(), 1287 $item->get_title()); 1288 if($params['author']){ 1289 $author = $item->get_author(0); 1290 if($author){ 1291 $name = $author->get_name(); 1292 if(!$name) $name = $author->get_email(); 1293 if($name) $this->cdata(' '.$lang['by'].' '.$name); 1294 } 1295 } 1296 if($params['date']){ 1297 $this->cdata(' ('.$item->get_date($conf['dformat']).')'); 1298 } 1299 if($params['details']){ 1300 $this->cdata(strip_tags($item->get_description())); 1301 } 1302 $this->listcontent_close(); 1303 $this->listitem_close(); 1304 } 1305 }else{ 1306 $this->listitem_open(0); 1307 $this->listcontent_open(); 1308 $this->emphasis_open(); 1309 $this->cdata($lang['rssfailed']); 1310 $this->emphasis_close(); 1311 $this->externallink($url); 1312 $this->listcontent_close(); 1313 $this->listitem_close(); 1314 } 1315 $this->listu_close(); 1316 } 1317 1318 1319 function _odpAddImage($src, $width = NULL, $height = NULL, $align = NULL, $title = NULL, $style = NULL){ 1320 list($ext,$mime) = mimetype($src); 1321 $name = 'Pictures/'.md5($src).'.'.$ext; 1322 if(!$this->manifest[$name]){ 1323 $this->manifest[$name] = $mime; 1324 $this->ZIP->add_File(io_readfile($src,false),$name,0); 1325 } 1326 // make sure width and height is available 1327 // FIXME we don't have the dimension of an external file 1328 // (except it's cached, but this is not the default) there seems 1329 // to be no way to specify "use original image" in ODF - thus 1330 // a hardcoded default size of 200 pixel here 1331 if(!$width || !$height){ 1332 $info = getimagesize($src); 1333 if(!$width){ 1334 $width = $info[0]; 1335 $height = $info[1]; 1336 }else{ 1337 $height = round(($width * $info[1]) / $info[0]); 1338 } 1339 } 1340 1341 // convert from pixel to centimeters 1342 $width = (($width/96.0)*2.54); 1343 $height = (($height/96.0)*2.54); 1344 // Don't be wider than the page 1345 if ($width >= 17){ // FIXME : this assumes A4 page format with 2cm margins 1346 $width = $width.'cm" style:rel-width="100%'; 1347 $height = $height.'cm" style:rel-height="scale'; 1348 } else { 1349 $width = $width.'cm'; 1350 $height = $height.'cm'; 1351 } 1352 1353 if($align){ 1354 $anchor = 'paragraph'; 1355 }else{ 1356 $anchor = 'as-char'; 1357 } 1358 1359 if (!$style or !array_key_exists($style, $this->autostyles)) { 1360 $style = 'media'.$align; 1361 } 1362 1363 if ($title) { 1364 $this->doc .= '<draw:frame draw:style-name="'.$style.'" draw:name="'.$this->_xmlEntities($title).' Legend" 1365 text:anchor-type="'.$anchor.'" draw:z-index="0" svg:width="'.$width.'">'; 1366 $this->doc .= '<draw:text-box>'; 1367 $this->doc .= '<text:p text:style-name="legendcenter">'; 1368 } 1369 $this->doc .= '<draw:frame draw:style-name="'.$style.'" draw:name="'.$this->_xmlEntities($title).'" 1370 text:anchor-type="'.$anchor.'" draw:z-index="0" 1371 svg:width="'.$width.'" svg:height="'.$height.'" >'; 1372 $this->doc .= '<draw:image xlink:href="'.$this->_xmlEntities($name).'" 1373 xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>'; 1374 $this->doc .= '</draw:frame>'; 1375 if ($title) { 1376 $this->doc .= $this->_xmlEntities($title).'</text:p></draw:text-box></draw:frame>'; 1377 } 1378 } 1379 1380} 1381 1382//Setup VIM: ex: et ts=4 enc=utf-8 : 1383