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 ) { 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 $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() { 200 $this->doc .= '<ul class="level' . $this->currentLevel . '">' . DOKU_LF; 201 } 202 203 function listo_open() { 204 $this->doc .= '<ol class="level' . $this->currentLevel . '">' . DOKU_LF; 205 } 206 207 public function finishSectionEdit($end = null) { 208 return ''; 209 } 210 211 /** 212 * @param string $type 213 */ 214 public function startSectionEdit($start, $type, $title = 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, 239 $height=NULL, $cache=NULL, $linking=NULL) { 240 global $ID; 241 list($src,$hash) = explode('#',$src,2); 242 resolve_mediaid(getNS($ID),$src, $exists); 243 244 $noLink = false; 245 $render = ($linking == 'linkonly') ? false : true; 246 $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 247 248 list($ext,$mime,$dl) = mimetype($src); 249 if(substr($mime,0,5) == 'image' && $render){ 250 $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),($linking=='direct')); 251 if ( substr($mime,0,5) == 'image' && $linking='details' ) { $noLink = true;} 252 } elseif($mime == 'application/x-shockwave-flash' && $render){ 253 // don't link flash movies 254 $noLink = true; 255 } else{ 256 // add file icons 257 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext); 258 $link['class'] .= ' mediafile mf_'.$class; 259 $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),true); 260 } 261 262 if($hash) { 263 $link['url'] .= '#'.$hash; 264 } 265 266 //markup non existing files 267 if (!$exists) { 268 $link['class'] .= ' wikilink2'; 269 } 270 271 //output formatted 272 if ($linking == 'nolink' || $noLink) { 273 $this->doc .= $link['name']; 274 } else { 275 $this->doc .= $this->_formatLink($link); 276 } 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 /** 364 * @param string $string 365 */ 366 function _xmlEntities($string) { 367 368 $string = parent::_xmlEntities($string); 369 $string = htmlentities($string, 8, 'UTF-8'); 370 $string = $this->superentities($string); 371 372 if ($this->info['scriptmode']) { 373 $string = str_replace(array("<%", "%>", "<?", "?>"), 374 array("<%", "%>", "<?", "?>"), 375 $string); 376 } 377 378 return $string; 379 } 380 381 // Unicode-proof htmlentities. 382 // Returns 'normal' chars as chars and weirdos as numeric html entites. 383 384 /** 385 * @param string $str 386 */ 387 function superentities( $str ){ 388 // get rid of existing entities else double-escape 389 $str2 = ''; 390 $str = html_entity_decode(stripslashes($str),ENT_QUOTES,'UTF-8'); 391 $ar = preg_split('/(?<!^)(?!$)(?!\n)/u', $str ); // return array of every multi-byte character 392 foreach ($ar as $c){ 393 $o = ord($c); 394 if ( // (strlen($c) > 1) || /* multi-byte [unicode] */ 395 ($o > 127) // || /* <- control / latin weirdos -> */ 396 // ($o <32 || $o > 126) || /* <- control / latin weirdos -> */ 397 // ($o >33 && $o < 40) ||/* quotes + ambersand */ 398 // ($o >59 && $o < 63) /* html */ 399 400 ) { 401 // convert to numeric entity 402 $c = mb_encode_numericentity($c, array(0x0, 0xffff, 0, 0xffff), 'UTF-8'); 403 } 404 $str2 .= $c; 405 } 406 return $str2; 407 } 408 409 function preformatted($text) { 410 $this->doc .= '<div class="pre">'; 411 parent::preformatted($text); 412 $this->doc .= '</div>'; 413 } 414 415 function _highlight($type, $text, $language = null, $filename = null) { 416 $this->doc .= '<div class="pre">'; 417 parent::_highlight($type, $text, $language, $filename); 418 $this->doc .= '</div>'; 419 } 420 421 /** 422 * API of the imagereference plugin 423 * https://github.com/i-net-software/dokuwiki-plugin-imagereference 424 * 425 * Allows to specify special imagecaption tags that the renderer (mpdf) can use 426 */ 427 public function imageCaptionTags(&$imagereferenceplugin) 428 { 429 if ( !$imagereferenceplugin->accepts('table') ) { 430 return array( '<figure id="%s" class="imgcaption%s">', // $captionStart 431 '</figure>', // $captionEnd 432 '<figcaption class="undercaption">', // $underCaptionStart 433 '</figcaption>' // $underCaptionEnd 434 ); 435 } 436 437 return null; 438 } 439} 440 441//Setup VIM: ex: et ts=4 enc=utf-8 :