1<?php 2/** 3 * Render Plugin for XHTML without details link for internal images. 4 * 5 * @author i-net software <tools@inetsoftware.de> 6 * @author Gerry Weissbach <gweissbach@inetsoftware.de> 7 */ 8 9if(!defined('DOKU_INC')) die(); 10if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 11 12require_once DOKU_INC . 'inc/parser/xhtml.php'; 13 14/** 15 * The Renderer 16 */ 17class renderer_plugin_siteexport_pdf extends Doku_Renderer_xhtml { 18 19 var $acronymsExchanged = null; 20 var $hasSeenHeader = false; 21 var $scriptmode = false; 22 23 var $currentLevel = 0; 24 var $startlevel = 0; // level to start with numbered headings (default = 2) 25 var $levels = array( '======'=>1, 26 '====='=>2, 27 '===='=>3, 28 '==='=>4, 29 '=='=>5); 30 31 var $info = array( 32 'cache' => true, // may the rendered result cached? 33 'toc' => true, // render the TOC? 34 'forceTOC' => false, // shall I force the TOC? 35 'scriptmode' => false, // In scriptmode, some tags will not be encoded => '<%', '%>' 36 ); 37 38 var $headingCount = 39 array( 1=>0, 40 2=>0, 41 3=>0, 42 4=>0, 43 5=>0); 44 45 /** 46 * return some info 47 */ 48 function getInfo(){ 49 if ( method_exists(parent, 'getInfo')) { 50 $info = parent::getInfo(); 51 } 52 return array_merge(is_array($info) ? $info : confToHash(dirname(__FILE__).'/../plugin.info.txt'), array( 53 54 )); 55 } 56 57 function document_start() { 58 global $TOC, $ID, $INFO; 59 60 parent::document_start(); 61 62 // Cheating in again 63 $newMeta = p_get_metadata($ID, 'description tableofcontents', false); // 2010-10-23 This should be save to use 64 if ( !empty( $newMeta ) && count($newMeta) > 1 ) { 65 // $TOC = $this->toc = $newMeta; // 2010-08-23 doubled the TOC 66 $TOC = $newMeta; 67 } 68 } 69 70 function document_end() { 71 72 parent::document_end(); 73 74 // Prepare the TOC 75 global $TOC, $ID; 76 $meta = array(); 77 78 // NOTOC, and no forceTOC 79 if ( $this->info['toc'] === false && !($this->info['forceTOC'] || $this->meta['forceTOC']) ) { 80 $TOC = $this->toc = array(); 81 $meta['internal']['toc'] = false; 82 $meta['description']['tableofcontents'] = array(); 83 $meta['forceTOC'] = false; 84 85 } else if ( $this->info['forceTOC'] || $this->meta['forceTOC'] || (utf8_strlen(strip_tags($this->doc)) >= $this->getConf('documentlengthfortoc') && count($this->toc) > 1 ) ) { 86 $TOC = $this->toc; 87 // This is a little bit like cheating ... but this will force the TOC into the metadata 88 $meta = array(); 89 $meta['internal']['toc'] = true; 90 $meta['forceTOC'] = $this->info['forceTOC'] || $this->meta['forceTOC']; 91 $meta['description']['tableofcontents'] = $TOC; 92 } 93 94 // allways write new metadata 95 p_set_metadata($ID, $meta); 96 $this->doc = preg_replace('#<p( class=".*?")?>\s*</p>#','',$this->doc); 97 } 98 99 function header($text, $level, $pos) { 100 global $conf; 101 global $ID; 102 global $INFO; 103 104 if($text) 105 { 106 $hid = $this->_headerToLink($text,true); 107 108 //only add items within configured levels 109 $this->toc_additem($hid, $text, $level); 110 111 // adjust $node to reflect hierarchy of levels 112 $this->node[$level-1]++; 113 if ($level < $this->lastlevel) { 114 for ($i = 0; $i < $this->lastlevel-$level; $i++) { 115 $this->node[$this->lastlevel-$i-1] = 0; 116 } 117 } 118 $this->lastlevel = $level; 119 120 121 /* There should be no class for "sectioneditX" if there is no edit perm */ 122 if ($INFO['perm'] > AUTH_READ && 123 $level <= $conf['maxseclevel'] && 124 count($this->sectionedits) > 0 && 125 $this->sectionedits[count($this->sectionedits) - 1][2] === 'section') { 126 $this->finishSectionEdit($pos - 1); 127 } 128 129 $headingNumber = ''; 130 $useNumbered = p_get_metadata($ID, 'usenumberedheading', true); // 2011-02-07 This should be save to use 131 if ( $this->getConf('usenumberedheading') || !empty($useNumbered) || !empty($INFO['meta']['usenumberedheading']) || isset($_REQUEST['usenumberedheading'])) { 132 133 // increment the number of the heading 134 $this->headingCount[$level]++; 135 136 // build the actual number 137 for ($i=1;$i<=5;$i++) { 138 139 // reset the number of the subheadings 140 if ($i>$level) { 141 $this->headingCount[$i] = 0; 142 } 143 144 // build the number of the heading 145 $headingNumber .= $this->headingCount[$i] . '.'; 146 } 147 148 $headingNumber = preg_replace("/(\.0)+\.?$/", '', $headingNumber) . ' '; 149 } 150 151 // write the header 152 $this->doc .= DOKU_LF.'<h'.$level; 153 $class = array(); 154 if ($INFO['perm'] > AUTH_READ && 155 $level <= $conf['maxseclevel']) { 156 $class[] = $this->startSectionEdit($pos, 'section', $text); 157 } 158 159 if ( !empty($headingNumber) ) { 160 $class[] = 'level' . trim($headingNumber); 161 if ( intval($headingNumber) > 1 ) { 162 $class[] = 'notfirst'; 163 } else { 164 $class[] = 'first'; 165 } 166 } 167 168 if ( !empty($class) ) { 169 $this->doc .= ' class="' . implode(' ', $class) . '"'; 170 } 171 172 $this->doc .= '><a name="'.$hid.'" id="'.$hid.'">'; 173 $this->doc .= $this->_xmlEntities($headingNumber . $text); 174 $this->doc .= "</a></h$level>".DOKU_LF; 175 176 } else if ( $INFO['perm'] > AUTH_READ ) { 177 178 if ( $hasSeenHeader ) $this->finishSectionEdit($pos); 179 180 // write the header 181 $name = rand() . $level; 182 $this->doc .= DOKU_LF.'<a name="'. $this->startSectionEdit($pos, 'section_empty', $name) .'" class="' . $this->startSectionEdit($pos, 'section_empty', $name) . '" ></a>'.DOKU_LF; 183 } 184 185 $hasSeenHeader = true; 186 } 187 188 function section_open($level) { 189 $this->currentLevel = $level; 190 } 191 192 function section_close() {} 193 194 function p_open() { 195 $this->doc .= DOKU_LF.'<p class="level' . $this->currentLevel . '">'.DOKU_LF; 196 } 197 198 function listu_open() { 199 $this->doc .= '<ul class="level' . $this->currentLevel . '">'.DOKU_LF; 200 } 201 202 function listo_open() { 203 $this->doc .= '<ol class="level' . $this->currentLevel . '">'.DOKU_LF; 204 } 205 206 public function finishSectionEdit($end = null) { 207 global $INFO; 208 if ( $INFO['perm'] > AUTH_READ ) 209 { 210 return parent::finishSectionEdit($end); 211 } 212 } 213 214 public function startSectionEdit($start, $type, $title = null) { 215 global $INFO; 216 if ( $INFO['perm'] > AUTH_READ ) 217 { 218 return parent::startSectionEdit($start, $type, $title); 219 } 220 221 return ""; 222 } 223 224 /** 225 * Wrap centered media in a div to center it 226 */ 227 function _media ($src, $title=NULL, $align=NULL, $width=NULL, 228 $height=NULL, $cache=NULL, $render = true) { 229 230 $out = ''; 231 if($align == 'center'){ 232 $out .= '<div align="center" style="text-align: center">'; 233 } 234 235 $out .= parent::_media ($src, $title, $align, $width, $height, $cache, $render); 236 237 if($align == 'center'){ 238 $out .= '</div>'; 239 } 240 241 return $out; 242 } 243 244 function internalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 245 $height=NULL, $cache=NULL, $linking=NULL) { 246 global $ID; 247 list($src,$hash) = explode('#',$src,2); 248 resolve_mediaid(getNS($ID),$src, $exists); 249 250 $noLink = false; 251 $render = ($linking == 'linkonly') ? false : true; 252 $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 253 254 list($ext,$mime,$dl) = mimetype($src); 255 if(substr($mime,0,5) == 'image' && $render){ 256 $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),($linking=='direct')); 257 if ( substr($mime,0,5) == 'image' && $linking='details' ) { $noLink = true;} 258 }elseif($mime == 'application/x-shockwave-flash' && $render){ 259 // don't link flash movies 260 $noLink = true; 261 }else{ 262 // add file icons 263 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext); 264 $link['class'] .= ' mediafile mf_'.$class; 265 $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),true); 266 } 267 268 if($hash) $link['url'] .= '#'.$hash; 269 270 //markup non existing files 271 if (!$exists) 272 $link['class'] .= ' wikilink2'; 273 274 //output formatted 275 if ($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 276 else $this->doc .= $this->_formatLink($link); 277 } 278 279 /** 280 * Render an internal Wiki Link 281 * 282 * $search,$returnonly & $linktype are not for the renderer but are used 283 * elsewhere - no need to implement them in other renderers 284 * 285 * @author Andreas Gohr <andi@splitbrain.org> 286 */ 287 function internallink($id, $name = NULL, $search=NULL,$returnonly=false,$linktype='content') { 288 global $conf; 289 global $ID; 290 // default name is based on $id as given 291 $default = $this->_simpleTitle($id); 292 293 // now first resolve and clean up the $id 294 resolve_pageid(getNS($ID),$id,$exists); 295 $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 296 if ( !$isImage ) { 297 if ( $exists ) { 298 $class='wikilink1'; 299 } else { 300 $class='wikilink2'; 301 $link['rel']='nofollow'; 302 } 303 } else { 304 $class='media'; 305 } 306 307 //keep hash anchor 308 list($id,$hash) = explode('#',$id,2); 309 if(!empty($hash)) $hash = $this->_headerToLink($hash); 310 311 //prepare for formating 312 $link['target'] = $conf['target']['wiki']; 313 $link['style'] = ''; 314 $link['pre'] = ''; 315 $link['suf'] = ''; 316 // highlight link to current page 317 if ($id == $ID) { 318 $link['pre'] = '<span class="curid">'; 319 $link['suf'] = '</span>'; 320 } 321 $link['more'] = ''; 322 $link['class'] = $class; 323 $link['url'] = wl($id); 324 $link['name'] = $name; 325 $link['title'] = $this->_getLinkTitle(null, $default, $isImage, $id, $linktype); 326 327 //add search string 328 if($search){ 329 ($conf['userewrite']) ? $link['url'].='?' : $link['url'].='&'; 330 if(is_array($search)){ 331 $search = array_map('rawurlencode',$search); 332 $link['url'] .= 's[]='.join('&s[]=',$search); 333 }else{ 334 $link['url'] .= 's='.rawurlencode($search); 335 } 336 } 337 338 //keep hash 339 if($hash) $link['url'].='#'.$hash; 340 341 //output formatted 342 if($returnonly){ 343 return $this->_formatLink($link); 344 }else{ 345 $this->doc .= $this->_formatLink($link); 346 } 347 } 348 349 function acronym($acronym) { 350 351 if ( empty($this->acronymsExchanged) ) { 352 $this->acronymsExchanged = $this->acronyms; 353 $this->acronyms = array(); 354 355 foreach( $this->acronymsExchanged as $key => $value ) { 356 $this->acronyms[str_replace('_', ' ', $key)] = $value; 357 } 358 } 359 360 parent::acronym($acronym); 361 } 362 363 function _xmlEntities($string) { 364 365 $string = parent::_xmlEntities($string); 366 $string = htmlentities($string, 8, 'UTF-8'); 367 $string = $this->superentities($string); 368 369 if ( $this->info['scriptmode'] ) { 370 $string = str_replace( array( "<%", "%>", "<?", "?>"), 371 array( "<%", "%>", "<?", "?>"), 372 $string); 373 } 374 375 return $string; 376 } 377 378 // Unicode-proof htmlentities. 379 // Returns 'normal' chars as chars and weirdos as numeric html entites. 380 function superentities( $str ){ 381 // get rid of existing entities else double-escape 382 $str2 = ''; 383 $str = html_entity_decode(stripslashes($str),ENT_QUOTES,'UTF-8'); 384 $ar = preg_split('/(?<!^)(?!$)(?!\n)/u', $str ); // return array of every multi-byte character 385 foreach ($ar as $c){ 386 $o = ord($c); 387 if ( // (strlen($c) > 1) || /* multi-byte [unicode] */ 388 ($o > 127) // || /* <- control / latin weirdos -> */ 389 // ($o <32 || $o > 126) || /* <- control / latin weirdos -> */ 390 // ($o >33 && $o < 40) ||/* quotes + ambersand */ 391 // ($o >59 && $o < 63) /* html */ 392 393 ) { 394 // convert to numeric entity 395 $c = mb_encode_numericentity($c,array (0x0, 0xffff, 0, 0xffff), 'UTF-8'); 396 } 397 $str2 .= $c; 398 } 399 return $str2; 400 } 401 402 function preformatted($text) { 403 $this->doc .= '<div class="pre">'; 404 parent::preformatted($text); 405 $this->doc .= '</div>'; 406 } 407 408 function _highlight($type, $text, $language = null, $filename = null) { 409 $this->doc .= '<div class="pre">'; 410 parent::_highlight($type, $text, $language, $filename); 411 $this->doc .= '</div>'; 412 } 413 414 /** 415 * API of the imagereference plugin 416 * https://github.com/i-net-software/dokuwiki-plugin-imagereference 417 * 418 * Allows to specify special imagecaption tags that the renderer (mpdf) can use 419 */ 420 public function imageCaptionTags(&$imagereferenceplugin) 421 { 422 if ( !$imagereferenceplugin->accepts('table') ) { 423 return array( '<figure id="%s" class="imgcaption%s">', // $captionStart 424 '</figure>', // $captionEnd 425 '<figcaption class="undercaption">', // $underCaptionStart 426 '</figcaption>' // $underCaptionEnd 427 ); 428 } 429 430 return null; 431 } 432} 433 434//Setup VIM: ex: et ts=4 enc=utf-8 :