1<?php 2 /** 3 4 */ 5 6 // must be run within Dokuwiki 7 if (!defined('DOKU_INC')) die(); 8 9 if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 10 if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 11 if(!defined('EPUB_DIR')) define('EPUB_DIR',realpath(dirname(__FILE__).'/../').'/'); 12 require_once DOKU_INC.'inc/parser/xhtml.php'; 13 14 class renderer_plugin_epub extends Doku_Renderer_xhtml { 15 private $opf_handle; 16 private $oebps; 17 private $current_page; 18 private $allow_url_fopen; 19 private $isWin; 20 private $audio_link; 21 private $audio_nmsp; 22 private $audio_nmsp_orig; 23 private $video_link; 24 private $video_nmsp; 25 private $video_nmsp_orig; 26 function getInfo() { 27 return array( 28 'author' => 'Myron Turner', 29 'email' => 'turnermm02@shaw.ca', 30 'date' => '2011-07-1', 31 'name' => 'epub', 32 'desc' => 'renderer for ebook creator', 33 'url' => 'http://www.dokuwiki.org/plugin:epub'); 34 } 35 36 function __construct() { 37 $this->allow_url_fopen=ini_get ( 'allow_url_fopen' ) ; 38 $this->isWin=function_exists('epub_isWindows') ? epub_isWindows() : false; 39 $this->mpeg_settings('audio') ; 40 $this->mpeg_settings('video') ; 41 } 42 43 function mpeg_settings($which) { 44 if($which == 'audio') { 45 $this->audio_link = $this->getConf('audio_fn'); 46 $nmsp = 'audio_nmsp'; 47 } 48 else { 49 $this->video_link = $this->getConf('video_fn'); 50 $nmsp = 'video_nmsp'; 51 } 52 $nmsp = $this->getConf($nmsp); 53 $nmsp = str_replace(' ','',$nmsp); 54 $nmsp = str_replace(',','|',$nmsp); 55 $nmsp_orig = $nmsp; 56 $nmsp = str_replace(':','_',$nmsp); 57 if($which == 'audio') { 58 $this->audio_nmsp = $nmsp; 59 $this->audio_nmsp_orig = $nmsp_orig; 60 return; 61 } 62 $this->video_nmsp = $nmsp; 63 $this->video_nmsp_orig = $nmsp_orig; 64 } 65 66 /** 67 * Make available as XHTML replacement renderer 68 */ 69 public function canRender($format){ 70 if($format == 'xhtml') return true; 71 return false; 72 } 73 74 function set_oebps() { 75 $this->oebps = epub_get_oebps(); 76 } 77 function set_current_page($page) { 78 $this->current_page=$page; 79 } 80 81 function opf_handle() { 82 if(!$this->opf_handle) { 83 $this->opf_handle= fopen($this->oebps. 'content.opf', 'a'); 84 } 85 return $this->opf_handle; 86 } 87 88 function is_epubid($id) { 89 return is_epub_pageid($id); 90 } 91 /** 92 * Simplified header printing with PDF bookmarks 93 */ 94 function header($text, $level, $pos, $readonly=false) { 95 if(!$text) return; //skip empty headlines 96 $hid = $this->_headerToLink($text, true); 97 // print header 98 $this->doc .= DOKU_LF."<h$level id='$hid'>"; 99 $this->doc .= $this->_xmlEntities($text); 100 $this->doc .= "</h$level>".DOKU_LF; 101 } 102 103 /** 104 * Wrap centered media in a div to center it 105 */ 106 function _media ($src, $title=NULL, $align=NULL, $width=NULL, 107 $height=NULL, $cache=NULL, $render = true) { 108 $mtype = mimetype($src); 109 if(!$title) $title = $src; 110 $external = false; 111 $src = trim($src); 112 $out = ""; 113 if(strpos($src,'http://') === 0) $external = true; 114 if($external && !$this->allow_url_fopen) { 115 $link = $this->create_external_link($src); 116 return $this->_formatLink($link); 117 } 118 $src = $this->copy_media($src,$external); 119 120 if(strpos($mtype[1],'image') !== false) { 121 $out .= $this->set_image($src,$width,$height,$align); 122 } 123 else if(strpos($mtype[1],'audio') !== false) { 124 if($this->audio_link) $out .= '</p><div style="text-align:center">' ; 125 $out .= $this->set_audio($src,$mtype,$title) ; 126 if($this->audio_link) { // set audio footnote 127 list($title,$rest) = explode('(', $title); 128 $mpfile = str_replace('Audio/',"",$src); 129 $display_name = $title; 130 $title = $mpfile; 131 $out .= $this->_formatLink( array('class'=>'media mediafile mf_mp3','title'=>$title,'name'=>$title, 'display'=>$display_name) ) ."\n</div><p>"; 132 } 133 } 134 else if(strpos($mtype[1],'video') !== false) { 135 if($this->video_link) $out .= '</p><div style="text-align:center">' ;//$out .= '<div style="text-align:center">' ; 136 $out .= $this->set_video($src,$mtype,$title) ; 137 if($this->video_link) { 138 list($title,$rest) = explode('(', $title); 139 $mpfile = str_replace('Video/',"",$src); 140 $display_name = $title; 141 $title = $mpfile; 142 $out .= $this->_formatLink( array('class'=>'media mediafile mf_mp4','title'=>$title,'name'=>$title, 'display'=>$display_name) ) ."\n</div><p>"; 143 } 144 } 145 else { 146 $out .= "<a href='$src'>$title</a>"; 147 } 148 return $out; 149 } 150 151 function create_external_link($name) { 152 return array( 153 "target" => "", 154 "style" => "", 155 "pre" => "", 156 "suf" => "", 157 "type"=>'ext_media', 158 "more" => 'rel="nofollow"', 159 "class" => 'urlextern', 160 "url" => $name, 161 "name" => basename($name), 162 "title" => $name 163 ); 164 } 165 166 /** 167 * hover info makes no sense in PDFs, so drop acronyms 168 */ 169 function acronym($acronym) { 170 $this->doc .= $this->_xmlEntities($acronym); 171 } 172 173 174 function is_image($link,&$type) { 175 176 if(strpos($link['class'],'media') === false) { 177 $type=$link['class']; 178 return false; 179 } 180 181 $mime_type = mimetype($link['title']); 182 if(!$mime_type[1] ) { 183 list($url,$rest) = explode('?', $link['url']); 184 $mime_type = mimetype($url); 185 if(!$mime_type[1]) { 186 $mime_type = mimetype($rest); 187 } 188 } 189 190 if(!$mime_type[1]) { 191 $type = 'other'; 192 return false; 193 } 194 195 list($type,$ext) = explode('/', $mime_type[1] ); 196 197 if($type != 'image') { 198 $type='media'; 199 return false; 200 } 201 202 return true; 203 } 204 /** 205 * reformat links if needed 206 */ 207 208 function _formatLink($link){ 209 $type = ""; 210 211 if($this->is_image($link,$type)) { 212 if(preg_match('#media\s*=\s*http#',$link['url'])) { 213 $name = $this->copy_media($link['title'],true); 214 if($name) { 215 return $this->set_image($name) ; 216 } 217 else return $this->set_image(''); 218 } 219 elseif(strpos($link['name'],'<img') !== false) { 220 $name = $this->copy_media($link['title']); 221 if(strpos($link['name'],'fetch.php') !== false) { 222 $t = $link['title']; 223 $link['name'] = preg_replace("#src=.*?$t\"#", "src=\"$name\"",$link['name']); 224 if(strpos($link['name'],'alt=""') !==false) { 225 $link['name'] = str_replace('alt=""','alt="'. $link['title'] . '"', $link['name']); 226 } 227 return $this->clean_image_link($link['name']); 228 } 229 elseif(strpos($link['url'],'fetch.php') !== false) { 230 if(preg_match('/src=\"(.*?)\"/',$link['name'],$matches)) { 231 $link['name'] = '<a href="' . $matches[1] .'">' . $link['title'] . '</a>'; 232 return $this->clean_image_link($link['name']); 233 } 234 } 235 $link['name'] = $this->clean_image_link($link['name']); 236 237 return $link['name']; 238 } 239 240 $name = $this->copy_media($link['title']); 241 return $this->set_image($name); 242 } 243 elseif($link['class'] == 'media' && strpos($link['name'],'<img') !== false) { 244 $this->doc .= '<a href="' . $link['url'] .'" class="media" title="' . $link['title'] . '" rel="nofollow">' . $link['name'] . '</a>'; 245 return; 246 } 247 248 if((strpos($link['class'],'wikilink') !== false ) && $type!='media') { //internal link 249 $orig = ""; 250 $name = $this->local_name($link,$orig,$frag); 251 if(!$this->is_epubid($orig)) { 252 $doku_base = DOKU_BASE; 253 $doku_base = trim($doku_base,'/'); 254 $fnote = DOKU_URL . "doku.php?id=$orig"; 255 return $this->set_footnote($link,$fnote); 256 } 257 258 $name = epub_clean_name($name); 259 $name .='.html'; 260 if($link['class'] == 'wikilink2') { 261 $wfn = wikiFN($orig); 262 if( file_exists($wfn) ) $link['class'] = 'wikilink1'; 263 } 264 265 } 266 else if($type=='media') { //internal media 267 $orig = ""; 268 $name = $this->local_name($link,$orig); 269 if(!empty($link['display'])) { 270 $link['name'] = $link['display']; 271 if(strpos($link['class'],'mp3') !== false) { 272 $orig = preg_replace('/^(' . $this->audio_nmsp . ')_/', "$1:", $orig); 273 $orig = preg_replace('/^(' . $this->audio_nmsp_orig . ')_/', "$1:", $orig); //two levels deep 274 } 275 else if(strpos($link['class'],'mp4') !== false) { 276 $orig = preg_replace('/^(' . $this->video_nmsp . ')_/', "$1:", $orig); 277 $orig = preg_replace('/^(' . $this->video_nmsp_orig . ')_/', "$1:", $orig); 278 } 279 } 280 $note_url = DOKU_URL . "lib/exe/fetch.php?media=" . $orig; 281 $link['class'] = 'wikilink1'; 282 $out = $this->set_footnote($link,$note_url); 283 $out=preg_replace('/<a\s+href=\'\'>(.*?)<\/a>(?=<a)/',"$1",$out); //remove link markup from link name 284 return $out; 285 } 286 elseif($link['class'] != 'media') { // or urlextern or samba share or . . . 287 $out = $this->set_footnote($link,trim($link['url'])); // creates an entry in output for the link with a live footnote to the link 288 if(isset($link['type']) && $link['type'] == 'ext_media') { 289 $this->doc .= $out; 290 } 291 else return $out; 292 } 293 294 if(!$name) return; 295 $link['url'] = $name; 296 if($frag) $link['url'] .="#$frag"; 297 return parent::_formatLink($link); 298 } 299 300 function clean_image_link($link) { 301 $link = str_replace('Images/Images',"Images",$link); 302 $link = preg_replace('#[\.\/]*Images#', "../Images", $link ); 303 return $link; 304 } 305 function set_footnote($link, $note_url="") { 306 $out = $link['name']; 307 $fn_id = epub_fn(); 308 $link['name'] = "[$fn_id]"; 309 if(preg_match("/media\s*=\s*(http.*)/", $link['url'],$matches)) { //format external urls 310 $note_url = urldecode($matches[1]); 311 } 312 $link['url'] = 'footnotes.html#' .$this->current_page; 313 $link['class'] = 'wikilink1'; 314 $id = 'backto_' . $fn_id; 315 $hash_link="<a id='$id' name='$id'></a>"; 316 $out .= $hash_link . parent::_formatLink($link); // . '</a>'; 317 epub_write_footnote($fn_id,$this->current_page,$note_url); 318 return $out; 319 320 } 321 322 function smiley($smiley) { 323 static $smileys; 324 if(!$smileys) $smileys = getSmileys(); 325 326 if ( array_key_exists($smiley, $this->smileys) ) { 327 $spath = DOKU_INC . 'lib/images/smileys/'.$smileys[$smiley]; 328 $name = $this->copy_media($spath,true); 329 $this->doc .= $this->_media($name); 330 } 331 } 332 333 function local_name($link,&$orig="", &$frag ="") { 334 $base_name= basename($link['url']); 335 $title = $link['title']? ltrim($link['title'],':'): ""; 336 list($starturl,$frag) = explode('#',$link['url']); 337 if ($title) { 338 $name = $title; 339 } 340 elseif($base_name) { 341 list($name,$rest) = explode('?',$base_name); 342 } 343 344 if($name) { 345 $orig = ltrim($name,':'); 346 return epub_clean_name(str_replace(':','_',$name)); 347 } 348 return false; 349 } 350 351 function copy_media($media,$external=false) { 352 $name = epub_clean_name(str_replace(':','_',basename($media))); 353 $ret_name = $name; 354 355 $mime_type = mimetype($name); 356 list($type,$ext) = explode('/', $mime_type[1] ); 357 if($type !== 'image' && $type != 'audio' && $type != 'video') return; 358 if($external) { 359 if(!$this->allow_url_fopen) return; 360 $tmp = str_replace('https://',"",$media); 361 $tmp = str_replace('http://',"",$media); 362 $tmp = str_replace('www.',"",$tmp); 363 if($this->isWin) { 364 $tmp = preg_replace('/^[A-Z]:/',"",$tmp); 365 } 366 $tmp=ltrim($tmp,'/\/'); 367 368 $elems=explode('/',$tmp); 369 if($this->isWin) { 370 $elems=explode('\\',$tmp); 371 } 372 if(!empty($elems) && $elems[0]) { 373 $elems[0] = preg_replace('#/\W#','_',$elems[0]); 374 $name = $elems[0]. "_" . $name; 375 } 376 } 377 if($type == 'audio') { 378 $name = "Audio/$name"; 379 } 380 else if($type == 'video') { 381 $name = "Video/$name"; 382 } 383 else if(!preg_match("/^Images/", $name)) { 384 $name = "Images/$name"; 385 } 386 387 $file = $this->oebps . $name; 388 if(file_exists($file)) return $name; 389 if(!$external) { 390 $media = mediaFN($media); 391 } 392 393 if(copy ($media , $file)) { 394 epub_write_item($name,$mime_type[1]) ; 395 return $name; 396 } 397 else if(!$this->isWin && epub_save_image($media , $file)) { 398 epub_write_item($name,$mime_type[1]) ; 399 return $name; 400 } 401 return false; 402 } 403 404 function set_image($img,$width=null,$height=null,$align=null) { 405 $w=""; 406 $h=""; 407 if($width) $w= ' width="' . $width . '"'; 408 if($height) $h= ' height="' .$height . '"'; 409 $img = $this->clean_image_link($img); 410 $class='media'; 411 if($align) { 412 $class .= $align; 413 } 414 return '<img src="' . $img . '"' . "$h $w " . ' alt="'. $img . '" class="' . $class . '" />'; 415 } 416 function set_audio($src,$mtype,$title) { 417 $src = "../$src"; 418 $type = $mtype[1]; 419 $out = '<audio class="mediacenter" controls="controls">' . "\n"; 420 $out .= "<source src= '$src' type='$type' />" . 421 "\n<a href='$src' title='$title'>$title</a></audio>\n"; 422 return $out; 423 } 424 425 function set_video($src,$mtype,$title) { 426 $src = "../$src"; 427 $type = $mtype[1]; 428 $out = '<video class="mediacenter" controls="controls">' . "\n"; 429 $out .= "<source src= '$src' type='$type' />" . 430 "\n<a href='$src' title='$title'>$title</a></video>\n"; 431 return $out; 432 } 433 434 function plugin($name,$data,$state = '', $match = '') { 435 436 if($name !='mathpublish' && $name !='ditaa' && $name !='graphviz') return parent::plugin($name,$data); 437 438 $mode ='xhtml'; 439 $renderer =p_get_renderer($mode ); 440 $plugin =& plugin_load('syntax',$name); 441 if($plugin != null) { 442 $plugin->render($mode,$renderer,$data); 443 444 if($name == 'ditaa') { 445 epub_check_for_ditaa($renderer->doc,$this); 446 } 447 else if($name =='mathpublish') { 448 449 epub_check_for_math($renderer->doc,$this); 450 } 451 else if($name =='graphviz') { 452 epub_check_for_graphviz($renderer->doc,$this,$data,$plugin); 453 } 454 $this->doc .= $renderer->doc; 455 } 456 } 457 458 /** 459 * no obfuscation for email addresses 460 */ 461 function emaillink($address, $name = NULL,$returnonly = false) { 462 global $conf; 463 $old = $conf['mailguard']; 464 $conf['mailguard'] = 'none'; 465 parent::emaillink($address, $name); 466 $conf['mailguard'] = $old; 467 } 468 469 function write($what,$handle) { 470 fwrite($handle,"$what\n"); 471 fflush($handle); 472 } 473 } 474 475