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