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