1<?php 2 3/** 4 * DokuWiki Plugin latexit (Renderer Component) 5 * 6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 7 * @author Adam Kučera <adam.kucera@wrent.cz> 8 */ 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) 11 die(); 12 13/** 14 * Latexit plugin extends default renderer class in this file 15 */ 16require_once DOKU_INC . 'inc/parser/renderer.php'; 17 18/** 19 * includes additional plugin classes 20 */ 21require_once DOKU_INC . 'lib/plugins/latexit/classes/Package.php'; 22require_once DOKU_INC . 'lib/plugins/latexit/classes/RowspanHandler.php'; 23require_once DOKU_INC . 'lib/plugins/latexit/classes/BibHandler.php'; 24require_once DOKU_INC . 'lib/plugins/latexit/classes/LabelHandler.php'; 25require_once DOKU_INC . 'lib/plugins/latexit/classes/RecursionHandler.php'; 26 27/** 28 * includes default DokuWiki files containing functions used by latexit plugin 29 */ 30require_once DOKU_INC . 'inc/parserutils.php'; 31require_once DOKU_INC . 'inc/pageutils.php'; 32require_once DOKU_INC . 'inc/pluginutils.php'; 33require_once DOKU_INC . 'inc/confutils.php'; 34 35/** 36 * Main latexit class, specifies how will be latex rendered 37 */ 38class renderer_plugin_latexit extends Doku_Renderer { 39 40 /** 41 * Singleton helper plugin to store data for multiple renderer instances 42 * 43 * @var helper_plugin_latexit 44 */ 45 protected $store; 46 47 /** 48 * Stores the information about last list level 49 * @var int 50 */ 51 protected $last_level; 52 53 /** 54 * Is true when the renderer is in a list 55 * @var boolean 56 */ 57 protected $list_opened; 58 59 /** 60 * Stores the information about the level of recursion. 61 * It stores the depth of current recusively added file. 62 * @var int 63 */ 64 protected $recursion_level; 65 66 /** 67 * Used in recursively inserted files, stores information about headers level. 68 * @var int 69 */ 70 protected $headers_level; 71 72 /** 73 * Is TRUE when recursive inserting should be used. 74 * @var bool 75 */ 76 protected $recursive; 77 78 /** 79 * Stores the information about the headers level increase in last recursive insertion. 80 * @var int 81 */ 82 protected $last_level_increase; 83 84 /** 85 * Stores the information about the number of cells found in a table row. 86 * @var int 87 */ 88 protected $cells_count; 89 90 /** 91 * Stores the information about the number a table cols. 92 * @var int 93 */ 94 protected $table_cols; 95 96 /** 97 * Stores the last colspan in a table. 98 * @var int 99 */ 100 protected $last_colspan; 101 102 /** 103 * Stores the last rowspan in a table. 104 * @var int 105 */ 106 protected $last_rowspan; 107 108 /** 109 * Stores the last align of a cell in a table. 110 * @var int 111 */ 112 protected $last_align; 113 114 /** 115 * Is TRUE when renderer is inside a table. 116 * @var bool 117 */ 118 protected $in_table; 119 120 /** 121 * An instance of a RowspanHandler class. 122 * @var RowspanHandler 123 */ 124 protected $rowspan_handler; 125 126 /** 127 * Is set on true if the document contains media. 128 * @var boolean 129 */ 130 protected $media; 131 132 /** 133 * Stores the instance of BibHandler 134 * @var BibHandler 135 */ 136 protected $bib_handler; 137 138 /** 139 * This handler makes all the header labels unique 140 * @var LabelHandler 141 */ 142 protected $label_handler; 143 144 /** 145 * This handler prevents recursive inserting of subpages to be an unending loop. 146 * @var RecursionHandler 147 */ 148 protected $recursion_handler; 149 150 /** 151 * @var bool 152 */ 153 protected $bibliography; 154 155 /** 156 * Constructor 157 * 158 * Initializes the storage helper 159 */ 160 public function __construct() { 161 $this->_initializeStore(); 162 } 163 164 /** 165 * Make available as LaTeX renderer 166 */ 167 public function canRender($format) { 168 if ($format == 'latex') { 169 return true; 170 } 171 return false; 172 } 173 174 /** 175 * Return the rendering format of the renderer - latex 176 */ 177 public function getFormat() { 178 return 'latex'; 179 } 180 181 /** 182 * Renderer is always created as a new instance. 183 * It is required for recursive export. 184 */ 185 public function isSingleton() { 186 return false; 187 } 188 189 /** 190 * Allow overwriting options from within the document 191 * 192 * @param string $setting 193 * @param bool $notset 194 * @return mixed 195 */ 196 function getConf($setting, $notset = false) { 197 global $ID; 198 $opts = p_get_metadata($ID, 'plugin_latexit'); 199 if($opts && isset($opts[$setting])) return $opts[$setting]; 200 201 return parent::getConf($setting, $notset); 202 } 203 204 /** 205 * function is called, when a document is started to being rendered. 206 * It inicializes variables, adds headers to the LaTeX document and 207 * sets the browser headers of the exported file. 208 */ 209 function document_start() { 210 //register global variables used for recursive rendering 211 global $latexit_level; 212 global $latexit_headers; 213 global $zip; 214 //ID stores the current page id with namespaces, required for recursion prevention 215 global $ID; 216 217 if(is_null($this->store)) { 218 $this->_initializeStore(); 219 echo "aaa"; 220 } 221 222 //initialize variables 223 $this->list_opened = FALSE; 224 $this->recursive = FALSE; 225 $this->in_table = FALSE; 226 $this->last_level_increase = 0; 227 $this->rowspan_handler = new RowspanHandler(); 228 $this->media = FALSE; 229 $this->bibliography = FALSE; 230 if (!plugin_isdisabled('zotero')) { 231 $this->bib_handler = BibHandler::getInstance(); 232 } else { 233 $this->bib_handler = NULL; 234 } 235 $this->label_handler = LabelHandler::getInstance(); 236 $this->recursion_handler = RecursionHandler::getInstance(); 237 238 //is this recursive export calling on a subpage? 239 if (!isset($latexit_level) || is_null($latexit_level)) { 240 $this->recursion_level = 0; 241 } else { 242 $this->recursion_level = $latexit_level; 243 } 244 if (!isset($latexit_headers) || is_null($latexit_headers)) { 245 $this->headers_level = 0; 246 } else { 247 $this->headers_level = $latexit_headers; 248 } 249 250 //export of the main document 251 if (!$this->_immersed()) { 252 //the parent documented cannot be recursively inserted somewhere 253 $this->recursion_handler->insert(wikifn($ID)); 254 255 //prepare ZIP archive (will not be created, if it isn't necessary) 256 $zip = new ZipArchive(); 257 $this->_prepareZIP(); 258 259 // configure language 260 $document_lang = $this->getConf('document_lang'); 261 $pckg = new Package('babel'); 262 $pckg->addParameter($document_lang); 263 $this->store->addPackage($pckg); 264 265 // encoding is always UTF-8 266 $pckg = new Package('inputenc'); 267 $pckg->addParameter('utf8x'); 268 $this->store->addPackage($pckg); 269 270 // add metadata to preamble 271 $this->store->addPreamble(array('date', '\today')); // FIXME use the document's date instead 272 $this->store->addPreamble(array('title', $this->getConf('title'))); 273 $this->store->addPreamble(array('author', $this->getConf('author'))); 274 275 276 // start document 277 $this->_c('begin', 'document', 2); 278 279 //if title or author or date is set, it prints it 280 if ($this->getConf('date') || $this->getConf('title') != "" || $this->getConf('author') != "") { 281 $this->_c('maketitle'); 282 } 283 //if table of contents should be displayed, it prints it 284 if ($this->getConf('table_of_content')) { 285 $this->_c('tableofcontents', NULL, 2); 286 } 287 } 288 } 289 290 /** 291 * Prefix the created document with the pramble and packages 292 */ 293 protected function document_prefix() { 294 // copy current doc and reset 295 $doc = $this->doc; 296 $this->doc = ''; 297 298 //get document settings 299 $params = array( 300 $this->getConf('paper_size'), 301 $this->getConf('output_format'), 302 $this->getConf('font_size') . 'pt',); 303 if ($this->getConf('landscape')) { 304 $params[] = 'landscape'; 305 } 306 if ($this->getConf('draft')) { 307 $params[] = 'draft'; 308 } 309 310 // print document settings 311 $this->_c('documentclass', $this->getConf('document_class'), 1, $params); 312 313 // print the packages 314 $packages = $this->store->getPackages(); 315 foreach ($packages as $package) { 316 /** @var Package $package */ 317 $this->doc .= $package->printUsePackage(); 318 } 319 320 // print the preamble 321 $preamble = $this->store->getPreamble(); 322 foreach($preamble as $command) { 323 if(is_array($command)) { 324 $this->_c($command[0], $command[1], $command[2], $command[3]); 325 }else { 326 $this->doc .= $command; 327 } 328 } 329 330 // add custom document header 331 $this->doc .= $this->getConf('document_header'); 332 333 // finally readd the previously created document 334 $this->doc .= $doc; 335 } 336 337 /** 338 * function is called, when a document ends its rendering to finish the document 339 * It finalizes the document. 340 * 341 */ 342 function document_end() { 343 /** @var ZipArchive $zip */ 344 global $zip; 345 346 //if a media were inserted in a recursively added file, we have to push this information up 347 $this->_checkMedia(); 348 349 //this is MAIN PAGE of exported file, we can finalize document 350 if (!$this->_immersed()) { 351 $this->_n(2); 352 353 if ($this->_useBibliography() && !$this->bib_handler->isEmpty()) { 354 $this->_c('bibliographystyle', $this->getConf('bibliography_style')); 355 $this->_c('bibliography', $this->getConf('bibliography_name'), 2); 356 } 357 358 $this->doc .= $this->getConf('document_footer'); 359 $this->_c('end', 'document'); 360 361 // the document is done, add the prefix 362 $this->document_prefix(); 363 364 $this->_deleteMediaSyntax(); 365 //finalize rendering of few entities 366 $this->_highlightFixme(); 367 $this->_removeEntities(); 368 $this->_fixImageRef(); 369 370 371 $output = "output" . time() . ".latex"; 372 373 //file to download will be ZIP archive 374 if ($this->media || ($this->_useBibliography() && !$this->bib_handler->isEmpty())) { 375 $filename = $zip->filename; 376 if ($this->_useBibliography() && !$this->bib_handler->isEmpty()) { 377 $zip->addFromString($this->getConf('bibliography_name') . '.bib', $this->bib_handler->getBibtex()); 378 } 379 $zip->addFromString($output, $this->doc); 380 //zip archive is created when this function is called, 381 //so if no ZIP is needed, nothing is created 382 $zip->close(); 383 384 header("Content-type: application/zip"); 385 header("Content-Disposition: attachment; filename=output" . time() . ".zip"); 386 header("Content-length: " . filesize($filename)); 387 header("Pragma: no-cache"); 388 header("Expires: 0"); 389 readfile($filename); 390 //delete temporary zip file 391 unlink($filename); 392 } 393 //file to download will be ordinary LaTeX file 394 else { 395 //set the headers, so the browsers knows, this is not the HTML file 396 header('Content-Type: application/x-latex'); 397 header("Content-Disposition: attachment; filename=$output;"); 398 } 399 } 400 //this is RECURSIVELY added file 401 else { 402 //signal to the upper document, that we inserted media to ZIP archive 403 if ($this->media) { 404 $this->doc .= '%///MEDIA///'; 405 } 406 } 407 } 408 409 /** 410 * Function is called, when renderer finds a new header. 411 * It calls the LaTeX command for an appropriate level. 412 * @param string $text Text of the header 413 * @param int $level Level of the header. 414 * @param int $pos Not used in LaTeX 415 */ 416 function header($text, $level, $pos) { 417 //package hyperref will enable PDF bookmarks 418 $package = new Package('hyperref'); 419 $package->addParameter('unicode'); 420 $this->store->addPackage($package); 421 422 //set the types of headers to be used depending on configuration 423 $levels = array(); 424 if ($this->getConf('header_title')) { 425 $levels[] = 'title'; 426 } 427 if ($this->getConf('header_part')) { 428 $levels[] = 'part'; 429 } 430 if ($this->getConf('header_chapter') && $this->getConf('document_class') != 'article') { 431 $levels[] = 'chapter'; 432 } 433 array_push($levels, 'section', 'subsection', 'subsubsection', 'paragraph', 'subparagraph'); 434 435 if ($this->_immersed()) { 436 //when document is recursively inserted, it will continue from previous headers level 437 $level += $this->headers_level; 438 } 439 $this->_n(2); 440 441 //the array of levels is indexed from 0 442 $level--; 443 444 //such a level exists in the array 445 if (isset($levels[$level])) { 446 $this->_header($levels[$level], $text); 447 } 448 //level not in array, use default 449 else { 450 //to force a newline in latex, there has to be some empty char before, e.g. ~ 451 $this->doc .= '~'; 452 $this->_c('newline'); 453 $this->_c('textbf', $this->_latexSpecialChars($text)); 454 } 455 //add a label, so each section can be referenced 456 $label = $this->label_handler->newLabel($this->_createLabel($text)); 457 $this->_c('label', 'sec:' . $label); 458 } 459 460 /** 461 * Basic funcion called, when a text not from DokuWiki syntax is read 462 * It adds the data to the document, potentionally dangerous characters for 463 * LaTeX are escaped or removed. 464 * @param string $text Text to be inserted. 465 */ 466 function cdata($text) { 467 $this->doc .= $this->_latexSpecialChars($text); 468 } 469 470 /** 471 * Function is called, when renderer finds a new paragraph. 472 * It makes new paragraph in LaTeX Document. 473 */ 474 function p_open() { 475 $this->_n(2); 476 } 477 478 /** 479 * Function is called, when renderer finds a linebreak. 480 * It adds new line in LaTeX Document. 481 */ 482 function linebreak() { 483 if ($this->in_table) { 484 //in tables in LaTeX there is different syntax 485 $this->doc .= "\\newline "; 486 } else { 487 $this->doc .= "\\\\"; 488 } 489 } 490 491 /** 492 * Function is called, when renderer finds a horizontal line. 493 * It adds centered horizontal line in LaTeX Document. 494 */ 495 function hr() { 496 $this->_n(2); 497 $this->_c('begin', 'center'); 498 $this->doc .= "\line(1,0){250}\n"; 499 $this->_c('end', 'center', 2); 500 } 501 502 /** 503 * function is called, when renderer finds a strong text 504 * It calls command for strong text in LaTeX Document. 505 */ 506 function strong_open() { 507 $this->_open('textbf'); 508 } 509 510 /** 511 * function is called, when renderer finds the end of a strong text 512 */ 513 function strong_close() { 514 $this->_close(); 515 } 516 517 /** 518 * function is called, when renderer finds an emphasised text 519 * It calls command for emphasised text in LaTeX Document. 520 */ 521 function emphasis_open() { 522 $this->_open('emph'); 523 } 524 525 /** 526 * function is called, when renderer finds the end of an emphasised text 527 */ 528 function emphasis_close() { 529 $this->_close(); 530 } 531 532 /** 533 * function is called, when renderer finds an underlined text 534 * It calls command for underlined text in LaTeX Document. 535 */ 536 function underline_open() { 537 $this->_open('underline'); 538 } 539 540 /** 541 * function is called, when renderer finds the end of an underlined text 542 */ 543 function underline_close() { 544 $this->_close(); 545 } 546 547 /** 548 * function is called, when renderer finds a monospace text 549 * (all letters have same width) 550 * It calls command for monospace text in LaTeX Document. 551 */ 552 function monospace_open() { 553 $this->_open('texttt'); 554 } 555 556 /** 557 * function is called, when renderer finds the end of a monospace text 558 * (all letters have same width) 559 */ 560 function monospace_close() { 561 $this->_close(); 562 } 563 564 /** 565 * function is called, when renderer finds a subscript 566 * It adds needed package and calls command for subscript in LaTeX Document. 567 */ 568 function subscript_open() { 569 $package = new Package('fixltx2e'); 570 $this->store->addPackage($package); 571 $this->_open('textsubscript'); 572 } 573 574 /** 575 * function is called, when renderer finds the end of a subscript 576 */ 577 function subscript_close() { 578 $this->_close(); 579 } 580 581 /** 582 * function is called, when renderer finds a superscript 583 * It adds needed package and calls command for superscript in LaTeX Document. 584 */ 585 function superscript_open() { 586 $package = new Package('fixltx2e'); 587 $this->store->addPackage($package); 588 $this->_open('textsuperscript'); 589 } 590 591 /** 592 * function is called, when renderer finds the end of a superscript 593 */ 594 function superscript_close() { 595 $this->_close(); 596 } 597 598 /** 599 * function is called, when renderer finds a deleted text 600 * It adds needed package and calls command for deleted text in LaTeX Document. 601 */ 602 function deleted_open() { 603 $package = new Package('ulem'); 604 $package->addParameter('normalem'); 605 $this->store->addPackage($package); 606 $this->_open('sout'); 607 } 608 609 /** 610 * function is called, when renderer finds the end of a deleted text 611 */ 612 function deleted_close() { 613 $this->_close(); 614 } 615 616 /** 617 * function is called, when renderer finds a footnote 618 * It calls footnote command in LaTeX Document. 619 */ 620 function footnote_open() { 621 $this->_open('footnote'); 622 } 623 624 /** 625 * function is called, when renderer finds the end of a footnote 626 */ 627 function footnote_close() { 628 $this->_close(); 629 } 630 631 /** 632 * function is called, when renderer finds start of an unordered list 633 * It calls command for an unordered list in latex, even with right indention 634 */ 635 function listu_open() { 636 $this->_list_open("itemize"); 637 } 638 639 /** 640 * function is called, when renderer finds the end of an unordered list 641 * It calls command for the end of an unordered list in latex, even with right indention 642 */ 643 function listu_close() { 644 $this->_list_close("itemize"); 645 } 646 647 /** 648 * function is called, when renderer finds start of an ordered list 649 * It calls command for an ordered list in latex, even with right indention 650 */ 651 function listo_open() { 652 $this->_list_open("enumerate"); 653 } 654 655 /** 656 * function is called, when renderer finds the end of an ordered list 657 * It calls command for the end of an ordered list in latex, even with right indention 658 */ 659 function listo_close() { 660 $this->_list_close("enumerate"); 661 } 662 663 /** 664 * function is called, when renderer finds start of a list item 665 * It calls command for a list item in latex, even with right indention 666 * @param int $level Level of indention. 667 */ 668 function listitem_open($level) { 669 $this->last_level = $level; 670 $this->_indent_list(); 671 $this->doc .= " "; 672 $this->_c('item', NULL, 0); 673 } 674 675 /** 676 * function is called, when renderer finds the end of a list item content 677 * It adds newline to the latex file. 678 */ 679 function listcontent_close() { 680 $this->_n(); 681 } 682 683 /** 684 * Original text is not formatted by DW, so this function just inserts the text as it is. 685 * It just escapes special characters. 686 * @param string $text Unformatted text. 687 */ 688 function unformatted($text) { 689 $this->doc .= $this->_latexSpecialChars($text); 690 } 691 692 /** 693 * Inserts PHP code to the document. 694 * @param string $text PHP code. 695 */ 696 function php($text) { 697 $this->code($text, "PHP"); 698 } 699 700 /** 701 * Inserts block of PHP code to the document. 702 * @param string $text PHP code. 703 */ 704 function phpblock($text) { 705 $this->code($text, "PHP"); 706 } 707 708 /** 709 * Inserts HTML code to the document. 710 * @param string $text HTML code. 711 */ 712 function html($text) { 713 $this->code($text, "HTML"); 714 } 715 716 /** 717 * Inserts block of HTML code to the document. 718 * @param string $text HTML code. 719 */ 720 function htmlblock($text) { 721 $this->code($text, "HTML"); 722 } 723 724 /** 725 * Inserts preformatted text (with all whitespaces) 726 * @param string $text Preformatted text. 727 */ 728 function preformatted($text) { 729 $this->_n(); 730 $this->_c('begin', 'verbatim'); 731 $this->doc .= $text; 732 $this->_n(); 733 $this->_c('end', 'verbatim'); 734 } 735 736 /** 737 * Opens the quote environment. 738 */ 739 function quote_open() { 740 $this->_n(); 741 $this->_c('begin', 'quote'); 742 } 743 744 /** 745 * Closes the quote environment. 746 */ 747 function quote_close() { 748 $this->_n(); 749 $this->_c('end', 'quote'); 750 } 751 752 /** 753 * File tag is almost the same like the code tag, but it enables to download 754 * the code directly from DW. 755 * Therefore we just add the filename to the top of code. 756 * @param string $text The code itself. 757 * @param string $lang Programming language. 758 * @param string $file The code will be exported from DW as a file. 759 */ 760 function file($text, $lang = null, $file = null) { 761 $this->code($text, $lang, $file); 762 } 763 764 /** 765 * Function adds a block of programming language code to LaTeX file 766 * using the listings package. 767 * @param string $text The code itself. 768 * @param string $lang Programming language. 769 * @param string $file The code can be inserted to DokuWiki as a file. 770 */ 771 function code($text, $lang = null, $file = null) { 772 $pckg = new Package('listings'); 773 $this->store->addPackage($pckg); 774 775 //start code block 776 $this->_open('lstset'); 777 $this->doc .= 'frame=single'; 778 if (!is_null($lang)) { 779 //if language name is specified, insert it to LaTeX 780 $this->doc .= ', language='; 781 $this->doc .= $this->_latexSpecialChars($lang); 782 } 783 //insert filename 784 if (!is_null($file)) { 785 $this->doc .= ', title='; 786 $this->doc .= $this->_latexSpecialChars($file); 787 } 788 $this->_close(); 789 $this->_n(); 790 //open the code block 791 $this->_c('begin', 'lstlisting'); 792 793 //get rid of some non-standard characters 794 $text = str_replace('”', '"', $text); 795 $text = str_replace('–', '-', $text); 796 $this->doc .= $text; 797 //close the code block 798 $this->_c('end', 'lstlisting', 2); 799 } 800 801 /** 802 * This function is called when an acronym is found. It just inserts it as a classic text. 803 * I decided not to implement the mouse over text, although it is possible, but 804 * it does not work in all PDF browsers. 805 * http://tex.stackexchange.com/questions/32314/is-there-an-easy-way-to-add-hover-text-to-all-incidents-of-math-mode-where-the-h 806 * @param string $acronym The Acronym. 807 */ 808 function acronym($acronym) { 809 $this->doc .= $this->_latexSpecialChars($acronym); 810 } 811 812 /** 813 * This function is called when a smiley is found. 814 * LaTeX does not support smileys, so they are inserted as a normal text. 815 * FIXME and DELETEME are exceptions, they are highlited (in the end of exporting). 816 * @param string $smiley Smiley chars. 817 */ 818 function smiley($smiley) { 819 if ($smiley == 'FIXME' || $smiley == 'DELETEME') { 820 $pckg = new Package('soul'); 821 $this->store->addPackage($pckg); 822 $this->doc .= $smiley; 823 } else { 824 $this->doc .= $this->_latexSpecialChars($smiley); 825 } 826 } 827 828 /** 829 * DocuWiki can represent some characters as they typograficaly correct entities. 830 * Most of them exist in LaTeX as well, but some only in math mode. 831 * @param string $entity An entity. 832 */ 833 function entity($entity) { 834 //this text is removed after exporting 835 //it is here to disallow double escaping of some math characters 836 $this->doc .= '///ENTITYSTART///'; 837 switch ($entity) { 838 case '->': 839 $this->doc .= '$\rightarrow$'; 840 break; 841 case '<-': 842 $this->doc .= '$\leftarrow$'; 843 break; 844 case '<->': 845 $this->doc .= '$\leftrightarrow$'; 846 break; 847 case '=>': 848 $this->doc .= '$\Rightarrow$'; 849 break; 850 case '<=': 851 $this->doc .= '$\Leftarrow$'; 852 break; 853 case '<=>': 854 $this->doc .= '$\Leftrightarrow$'; 855 break; 856 case '(c)': 857 $this->doc .= '\copyright '; 858 break; 859 case '(tm)': 860 $this->doc .= '\texttrademark '; 861 break; 862 case '(r)': 863 $this->doc .= '\textregistered '; 864 break; 865 default: 866 $this->doc .= $this->_latexSpecialChars($entity); 867 break; 868 } 869 $this->doc .= '///ENTITYEND///'; 870 } 871 872 /** 873 * Inserts multiply entity (eg. 640x480) to LaTeX file. 874 * @param int $x First number 875 * @param int $y Second number 876 */ 877 function multiplyentity($x, $y) { 878 $this->doc .= '///ENTITYSTART///'; 879 $this->doc .= '$'; 880 $this->doc .= $this->_latexSpecialChars($x); 881 $this->doc .= ' \times '; 882 $this->doc .= $this->_latexSpecialChars($y); 883 $this->doc .= '$'; 884 $this->doc .= '///ENTITYEND///'; 885 } 886 887 /** 888 * Inserts single quote opening to LaTeX depending on set language. 889 */ 890 function singlequoteopening() { 891 $this->doc .= '`'; 892 } 893 894 /** 895 * Inserts single quote closing to LaTeX depending on set language. 896 */ 897 function singlequoteclosing() { 898 $this->doc .= '\''; 899 } 900 901 /** 902 * Inserts apostrophe to LaTeX depending on set language. 903 */ 904 function apostrophe() { 905 $this->doc .= '\''; 906 } 907 908 /** 909 * Inserts double quote opening to LaTeX depending on set language. 910 * Support for only English and Czech is implemented. 911 */ 912 function doublequoteopening() { 913 switch ($this->getConf('document_lang')) { 914 /* This is bugging, DW parses it strangely... FIXME consultation 915 * case 'czech': 916 $this->_open('uv'); 917 break; */ 918 default : 919 $this->doc .= ',,'; 920 break; 921 } 922 } 923 924 /** 925 * Inserts double quote closing to LaTeX depending on set language. 926 * Support for only English and Czech is implemented. 927 */ 928 function doublequoteclosing() { 929 switch ($this->getConf('document_lang')) { 930 /* This is bugging, DW parses it strangely... FIXME consultation 931 case 'czech': 932 $this->_close(); 933 break; */ 934 default : 935 $this->doc .= '"'; 936 break; 937 } 938 } 939 940 /** 941 * Function is called, when renderer finds a link written in text like CamelCase. 942 * It just calls the common link function. 943 * @param string $link Internal link to a wiki page. 944 */ 945 function camelcaselink($link) { 946 $this->internallink($link, $link); 947 } 948 949 /** 950 * This function handles the links on the page itself (#something at the end of URL) 951 * It inserts reference to LaTeX document 952 * @param string $hash Label of a section 953 * @param string $name Text of the original link 954 */ 955 function locallink($hash, $name = NULL) { 956 $this->_insertLinkPackages(); 957 if (!is_null($name)) { 958 $this->doc .= $this->_latexSpecialChars($name); 959 } else { 960 $this->doc .= $this->_latexSpecialChars($hash); 961 } 962 $this->doc .= ' ('; 963 $this->_c('autoref', "sec:" . $hash, 0); 964 $this->doc .= ')'; 965 } 966 967 /** 968 * function is called, when renderer finds an internal link 969 * It resolves the internal link (namespaces, URL) 970 * Depending on the configuration (inside the document): 971 * It handles link as an external and calls proper function in LaTeX depending on the title 972 * It recursively adds the linked page to the exported LaTeX file 973 * This feature is not in classic plugin configuration. 974 * If you want to have a link recursively inserted, add ~~RECURSIVE~~ just before it. 975 * The count of ~ means the same as = for headers. It will determine the 976 * level of first header used in recursively inserted text. 977 * @param string $link Internal link (can be without proper namespace) 978 * @param string/array $title Title, can be null or array (if it is media) 979 */ 980 function internallink($link, $title = NULL) { 981 //register globals 982 global $ID; //in this global var DokuWiki stores the current page id with namespaces 983 global $latexit_level; 984 global $latexit_headers; 985 986 //escape link title 987 if (!is_array($title)) { 988 $title = $this->_latexSpecialChars($title); 989 } 990 $link_original = $link; 991 992 //get current namespace from current page 993 $current_namespace = getNS($ID); 994 //get the page ID with right namespaces 995 //$exists stores information, if the page exists. 996 resolve_pageid($current_namespace, $link, $exists); 997 998 //if the page does not exist, just insert it as common text 999 if (!$exists) { 1000 $this->doc .= $title; 1001 return; 1002 } 1003 1004 $params = ''; 1005 $absoluteURL = true; 1006 //get the whole URL 1007 $url = wl($link, $params, $absoluteURL); 1008 $url = $this->_secureLink($url); 1009 if ($this->recursive) { 1010 //check if it can continue with recursive inserting of this page 1011 if ($this->recursion_handler->disallow(wikifn($link))) { 1012 $this->_n(2); 1013 //warn the user about unending recursion 1014 $this->doc .= "%!!! RECURSION LOOP HAS BEEN PREVENTED !!!"; 1015 $this->_n(2); 1016 } else { 1017 //insert this page to RecursionHandler 1018 $this->recursion_handler->insert(wikifn($link)); 1019 //the level of recursion is increasing 1020 $latexit_level = $this->recursion_level + 1; 1021 $latexit_headers = $this->headers_level; 1022 1023 //start parsing linked page - call the latexit plugin again 1024 $data = p_cached_output(wikifn($link), 'latexit'); 1025 1026 $this->_n(2); 1027 //insert comment to LaTeX 1028 $this->doc .= "%RECURSIVELY INSERTED FILE START"; 1029 $this->_n(2); 1030 //insert parsed data 1031 $this->doc .= $data; 1032 $this->_n(2); 1033 //insert comment to LaTeX 1034 $this->doc .= "%RECURSIVELY INSERTED FILE END"; 1035 $this->_n(2); 1036 //get headers level to previous level 1037 $this->headers_level -= $this->last_level_increase; 1038 //remove this page from RecursionHandler 1039 $this->recursion_handler->remove(wikifn($link)); 1040 } 1041 } 1042 //handle internal links as they were external 1043 else { 1044 $this->_insertLink($url, $title, "internal", $link_original); 1045 } 1046 $this->recursive = FALSE; 1047 } 1048 1049 /** 1050 * function is called, when renderer finds an external link 1051 * It calls proper function in LaTeX depending on the title 1052 * @param string $link External link 1053 * @param string/array $title Title, can be null or array (if it is media) 1054 */ 1055 function externallink($link, $title = NULL) { 1056 if (!is_array($title)) { 1057 $title = $this->_latexSpecialChars($title); 1058 } 1059 $link = $this->_secureLink($link); 1060 $this->_insertLink($link, $title, "external"); 1061 } 1062 1063 /** 1064 * InterWiki links lead to another wikis and they can be written in special syntax. 1065 * This resolves the link and inserts it as normal external link. 1066 * @param string $link Original link in DW syntax 1067 * @param string $title Title of link, can also be image 1068 * @param string $wikiName Name of wiki (according to configuration) 1069 * @param string $wikiUri Text in link after wiki address 1070 */ 1071 function interwikilink($link, $title = NULL, $wikiName, $wikiUri) { 1072 $url = $this->_resolveInterWiki($wikiName, $wikiUri); 1073 if (is_null($title)) { 1074 $name = $wikiUri; 1075 } else { 1076 $name = $title; 1077 } 1078 $this->externallink($url, $name); 1079 } 1080 1081 /** 1082 * Inserts a link to a file on local filesystem. 1083 * It just handles the link as an external link. 1084 * @param string $link Link to a file. 1085 * @param string $title Title of the link, can be image. 1086 */ 1087 function filelink($link, $title = NULL) { 1088 $this->externallink($link, $title); 1089 } 1090 1091 /** 1092 * Inserts a link to a Windows share intranet server. 1093 * It just handles the link as an external link. 1094 * @param string $link Link to a file. 1095 * @param string $title Title of the link, can be image. 1096 */ 1097 function windowssharelink($link, $title = NULL) { 1098 $this->externallink($link, $title); 1099 } 1100 1101 /** 1102 * function is called, when renderer finds an email link 1103 * It calls proper function in LaTeX depending on the name and sets mailto 1104 * @param string $address Email address 1105 * @param string/array $name Name, can be null or array (if it is media) 1106 */ 1107 function emaillink($address, $name = NULL) { 1108 if (!is_array($name)) { 1109 $name = $this->_latexSpecialChars($name); 1110 } 1111 $this->_insertLink($address, $name, "email"); 1112 } 1113 1114 /** 1115 * This function is called when an image is uploaded to DokuWiki and inserted to a page. 1116 * It adds desired commands to the LaTeX file and also downloads the image with LaTeX 1117 * file in the ZIP archive. 1118 * 1119 * @param string $src DokuWiki source of the media. 1120 * @param string|null $title Mouseover title of image, we dont use this param (use imagareference plugin for correct labeling) 1121 * @param string|null $align Align of the media. 1122 * @param int|null $width Width of the media. But DW uses pixels, LaTeX does not. Therefore we dont use it. 1123 * @param int|null $height Height of the media. But DW uses pixels, LaTeX does not. Therefore we dont use it. 1124 * @param string $cache We delete cache, so we don't use this param. 1125 * @param bool $linking Not used. 1126 */ 1127 function internalmedia($src, $title = NULL, $align = NULL, $width = NULL, $height = NULL, $cache = NULL, $linking = NULL) { 1128 /** @var ZipArchive $zip */ 1129 global $zip; 1130 1131 $media_folder = $this->getConf('media_folder'); 1132 1133 if (strpos($src,':') !== false){ 1134 //the namespace structure is kept in folder structure in ZIP archive 1135 $namespaces = explode(':', $src); 1136 $path = ''; 1137 for ($i = 1; $i < count($namespaces); $i++) { 1138 if ($i != 1) { 1139 $path .= "/"; 1140 } 1141 $path .= $namespaces[$i]; 1142 } 1143 }else{ 1144 $path = $src; 1145 } 1146 1147 //find media on FS 1148 $location = mediaFN($src); 1149 $exists = file_exists($location); 1150 if ($exists) { 1151 //exported file will be ZIP archive 1152 $this->media = TRUE; 1153 //add media to ZIP archive 1154 $zip->addFile($location, $media_folder . "/" . $path); 1155 } 1156 1157 $mime = mimetype($src); 1158 if (substr($mime[1], 0, 5) == "image") { 1159 $this->_insertImage($path, $align, $media_folder); 1160 } else { 1161 $this->_insertFile($path, $title, $media_folder); 1162 } 1163 } 1164 1165 /** 1166 * This function is called when an image from the internet is inserted to a page. 1167 * It adds desired commands to the LaTeX file and also downloads the image with LaTeX 1168 * file in the ZIP archive. 1169 * 1170 * @param string $src URL source of the media. 1171 * @param string|null $title Mouseover title of image, we dont use this param (use imagareference plugin for correct labeling) 1172 * @param string $align Align of the media. 1173 * @param int|null $width Width of the media. But DW uses pixels, LaTeX does not. Therefore we dont use it. 1174 * @param int|null $height Height of the media. But DW uses pixels, LaTeX does not. Therefore we dont use it. 1175 * @param string|null $cache We delete cache, so we don't use this param. 1176 * @param bool|null $linking Not used. 1177 */ 1178 function externalmedia($src, $title = NULL, $align = NULL, $width = NULL, $height = NULL, $cache = NULL, $linking = NULL) { 1179 global $conf; 1180 /** @var ZipArchive $zip */ 1181 global $zip; 1182 1183 $this->media = TRUE; 1184 $media_folder = $this->getConf('media_folder'); 1185 1186 //get just the name of file without path 1187 $filename = basename($src); 1188 //download the file to the DokuWiki TEMP folder 1189 $location = $conf["tmpdir"] . "/" . $filename; 1190 file_put_contents($location, file_get_contents($src)); 1191 //add file to the ZIP archive 1192 $path = $media_folder . "/" . $filename; 1193 $zip->addFile($location, $path); 1194 1195 $mime = mimetype($filename); 1196 1197 if (substr($mime[1], 0, 5) == "image") { 1198 $this->_insertImage($filename, $align, $media_folder); 1199 } else { 1200 $this->_insertFile($filename, $title, $media_folder); 1201 } 1202 } 1203 1204 /** 1205 * Function is called, when a renderer finds a start of an table. 1206 * It inserts needed packages and the header of the table. 1207 * @param int $maxcols Maximum of collumns in the table 1208 * @param int $numrows Number of rows in table (not required in LaTeX) 1209 * @param int $pos This parameter is not required by LaTeX. 1210 */ 1211 function table_open($maxcols = null, $numrows = null, $pos = null) { 1212 $this->table_cols = $maxcols; 1213 1214 //set environment to tables 1215 $this->in_table = true; 1216 $pckg = new Package('longtable'); 1217 $this->store->addPackage($pckg); 1218 1219 //print the header 1220 $this->_c('begin', 'longtable', 0); 1221 $this->doc .= "{|"; 1222 for ($i = 0; $i < $maxcols; $i++) { 1223 $this->doc .= $this->getConf('default_table_align') . "|"; 1224 } 1225 $this->_close(); 1226 $this->_n(); 1227 $this->_c('hline'); 1228 } 1229 1230 /** 1231 * Function is called in the end of every table. 1232 * It prints the footer of the table. 1233 * @param int $pos Not required in LaTeX. 1234 */ 1235 function table_close($pos = null) { 1236 //close the table environment 1237 $this->in_table = false; 1238 //print the footer 1239 $this->_c('end', 'longtable', 2); 1240 } 1241 1242 /** 1243 * Function is called at start of every row in a table. 1244 */ 1245 function tablerow_open() { 1246 //set the number of cells printed 1247 $this->cells_count = 0; 1248 } 1249 1250 /** 1251 * Function is called at the end of every row in a table 1252 */ 1253 function tablerow_close() { 1254 //add syntax for end of a row 1255 $this->doc .= " \\\\ "; 1256 $this->_n(); 1257 //add line 1258 $this->_c('hline'); 1259 $this->doc .= " "; 1260 $this->_n(); 1261 } 1262 1263 /** 1264 * Function is called when the header row is reached. 1265 * It just prints regular row in bold. 1266 * 1267 * @param int $colspan 1268 * @param string|null $align 1269 * @param int $rowspan 1270 */ 1271 function tableheader_open($colspan = 1, $align = NULL, $rowspan = 1) { 1272 $this->tablecell_open($colspan, $align, $rowspan); 1273 $this->_open('textbf'); 1274 } 1275 1276 /** 1277 * Function is called at the end of the header row. 1278 */ 1279 function tableheader_close() { 1280 $this->_close(); 1281 $this->tablecell_close(); 1282 } 1283 1284 /** 1285 * Function handling exporting of each cell in a table. 1286 * @param int $colspan Sets collspan of the cell. 1287 * @param string $align Sets align of the cell. 1288 * @param int $rowspan Sets rows[am of the cell. 1289 */ 1290 function tablecell_open($colspan = 1, $align = NULL, $rowspan = 1) { 1291 if (is_null($align)) { 1292 $align = $this->getConf('default_table_align'); 1293 } else { 1294 //in DW align is left, right, center, in LaTeX just first letter 1295 $align = substr($align, 0, 1); 1296 } 1297 //if anything is not standard, we will have to use different closing of a cell 1298 $this->last_colspan = $colspan; 1299 $this->last_rowspan = $rowspan; 1300 $this->last_align = $align; 1301 1302 //RowspanHandler stores information about the number of cells to be rowspanned 1303 if ($this->rowspan_handler->getRowspan($this->cells_count) != 0) { 1304 $this->doc .= ' & '; 1305 $this->rowspan_handler->decreaseRowspan($this->cells_count); 1306 $this->cells_count++; 1307 } 1308 1309 //colspan or not default align 1310 if ($colspan != 1 || $align != $this->getConf('default_table_align')) { 1311 $this->doc .= "\\multicolumn{" . $colspan . "}{|$align|}{"; 1312 } 1313 //start a new rowspan using RowspanHandler 1314 if ($rowspan != 1) { 1315 $pckg = new Package('multirow'); 1316 $this->store->addPackage($pckg); 1317 $this->rowspan_handler->insertRowspan($rowspan - 1, $this->cells_count); 1318 $this->doc .= "\\multirow{" . $rowspan . "}{*}{"; 1319 } 1320 } 1321 1322 /** 1323 * Function is called at the end of every cell. 1324 */ 1325 function tablecell_close() { 1326 //colspan or align different from default has been set in this cell 1327 if ($this->last_colspan != 1 || $this->last_align != $this->getConf('default_table_align')) { 1328 $this->doc .= "}"; 1329 } 1330 //rowspan has been set in this cell 1331 if ($this->last_rowspan != 1) { 1332 $this->doc .= "}"; 1333 } 1334 1335 //are there any cells left in this row? 1336 $this->cells_count += $this->last_colspan; 1337 if ($this->table_cols != $this->cells_count) { 1338 $this->doc .= " & "; 1339 } 1340 } 1341 1342 /** 1343 * Syntax of almost every basic LaTeX command is always the same. 1344 * @param string $command The name of a LaTeX command. 1345 * @param array $params Array of parameters of the command 1346 * @param bool $brackets Tells if the brackets should be used. 1347 */ 1348 protected function _open($command, $params = NULL, $brackets = true) { 1349 $this->doc .= "\\" . $command; 1350 //if params are set, print them all 1351 if (!is_null($params)) { 1352 $this->doc .= '['; 1353 $i = 0; 1354 foreach ($params as $p) { 1355 if ($i++ > 0) { 1356 $this->doc .= ', '; 1357 } 1358 $this->doc .= $p; 1359 } 1360 $this->doc .= ']'; 1361 } 1362 if ($brackets) { 1363 $this->doc .= "{"; 1364 } 1365 } 1366 1367 /** 1368 * Closing tag of a lot of LaTeX commands is always same and will be called 1369 * in almost every close function. 1370 */ 1371 protected function _close() { 1372 $this->doc .= '}'; 1373 } 1374 1375 /** 1376 * Helper function for printing almost all regular commands in LaTeX. 1377 * It can also print newlines after command and it supports parameters. 1378 * @param string $command Name of the command. 1379 * @param string $text Text to insert into the brackets. 1380 * @param int $newlines How many newlines after the command to insert. 1381 * @param array $params Array of parameters to be inserted. 1382 */ 1383 protected function _c($command, $text = NULL, $newlines = 1, $params = NULL) { 1384 //if there is no text, there will be no brackets 1385 if (is_null($text)) { 1386 $brackets = false; 1387 } else { 1388 $brackets = true; 1389 } 1390 $this->_open($command, $params, $brackets); 1391 //if there is no text, there is nothing to be closed 1392 if (!is_null($text)) { 1393 $this->doc .= $text; 1394 $this->_close(); 1395 } 1396 $this->_n($newlines); 1397 } 1398 1399 /** 1400 * Function inserting new lines in the LaTeX file. 1401 * @param int $cnt How many new lines to insert. 1402 */ 1403 protected function _n($cnt = 1) { 1404 for ($i = 0; $i < $cnt; $i++) { 1405 $this->doc .= "\n"; 1406 } 1407 } 1408 1409 /** 1410 * Function checks, if there were media added in a subfile. 1411 */ 1412 protected function _checkMedia() { 1413 //check 1414 if (preg_match('#%///MEDIA///#si', $this->doc)) { 1415 $this->media = TRUE; 1416 } 1417 //and delete any traces 1418 $this->_deleteMediaSyntax(); 1419 } 1420 1421 /** 1422 * Function removes %///MEDIA/// from document 1423 */ 1424 protected function _deleteMediaSyntax() { 1425 str_replace('%///MEDIA///', '', $this->doc); 1426 } 1427 1428 /** 1429 * Function inserts package used for hyperlinks. 1430 */ 1431 protected function _insertLinkPackages() { 1432 $package = new Package('hyperref'); 1433 //fixes the encoding warning 1434 $package->addParameter('unicode'); 1435 $this->store->addPackage($package); 1436 } 1437 1438 /** 1439 * Function used for exporting lists, they differ only by command. 1440 * @param string $command Proper LaTeX list command 1441 */ 1442 protected function _list_open($command) { 1443 $this->_n(); 1444 if ($this->list_opened) { 1445 for ($i = 1; $i < $this->last_level + 1; $i++) { 1446 //indention 1447 $this->doc .= ' '; 1448 } 1449 } else { 1450 $this->list_opened = TRUE; 1451 } 1452 $this->_indent_list(); 1453 $this->_c('begin', $command); 1454 } 1455 1456 /** 1457 * Function used for exporting the end of lists, they differ only by command. 1458 * @param string $command Proper LaTeX list command 1459 */ 1460 protected function _list_close($command) { 1461 if ($this->last_level == 1) { 1462 $this->list_opened = FALSE; 1463 } 1464 $this->_indent_list(); 1465 $this->_c('end', $command); 1466 } 1467 1468 /** 1469 * Indents the list according to the last seen level. 1470 */ 1471 protected function _indent_list() { 1472 for ($i = 1; $i < $this->last_level; $i++) { 1473 $this->doc .= ' '; 1474 } 1475 } 1476 1477 /** 1478 * This function highlights fixme DW command. 1479 * This format is used in some DokuWiki instances. 1480 * FIXME insert into documentation 1481 * format is: FIXME[author](description of a thing to fix) 1482 * (this feature comes from CCM at FIT CVUT, for whom I write the plugin) 1483 */ 1484 protected function _highlightFixme() { 1485 $this->doc = str_replace('FIXME', '\hl{FIXME}', $this->doc); 1486 $this->doc = str_replace('DELETEME', '\hl{DELETEME}', $this->doc); 1487 $this->doc = preg_replace_callback('#{FIXME}\[(.*?)\]\((.*?)\)#si', array(&$this, '_highlightFixmeHandler'), $this->doc); 1488 } 1489 1490 /** 1491 * Function handling parsing of the fix me DW command. 1492 * 1493 * @param array $matches of strings $matches strings from the regex 1494 * @return string regex result replacement 1495 */ 1496 protected function _highlightFixmeHandler($matches) { 1497 $matches[1] = $this->_stripDiacritics($matches[1]); 1498 $matches[2] = $this->_stripDiacritics($matches[2]); 1499 return '{FIXME[' . $matches[1] . '](' . $matches[2] . ')}'; 1500 } 1501 1502 /** 1503 * Insert header to the LaTeX document with right level command. 1504 * @param string $level LaTeX command for header on right level. 1505 * @param string $text Text of the Header. 1506 */ 1507 protected function _header($level, $text) { 1508 $this->_open($level); 1509 //pdflatex can have problems with special chars while making bookmarks 1510 //this is the fix 1511 $this->_open('texorpdfstring'); 1512 $text = str_replace("\"", "", $text); 1513 $this->doc .= $this->_latexSpecialChars($text); 1514 $this->_close(); 1515 $this->doc .= '{'; 1516 $this->doc .= $this->_pdfString($text); 1517 $this->_close(); 1518 $this->_close(); 1519 $this->_n(); 1520 } 1521 1522 /** 1523 * This function finds out, if the current renderer is immersed in recursion. 1524 * @return boolean Is immersed in recursion? 1525 */ 1526 protected function _immersed() { 1527 if ($this->recursion_level > 0) { 1528 return true; 1529 } 1530 return false; 1531 } 1532 1533 /** 1534 * Escapes LaTeX special chars. 1535 * Entities are in the middle of special tags so eg. MathJax texts are not escaped, but entities are. 1536 * @param string $text Text to be escaped. 1537 * @return string Escaped text. 1538 */ 1539 public function _latexSpecialChars($text) { 1540 return helper_plugin_latexit::escape($text); 1541 } 1542 1543 /** 1544 * Function replaces entities, which have not been replaced using _latexSpecialChars function 1545 */ 1546 protected function _removeEntities() { 1547 $this->doc = preg_replace('#///ENTITYSTART///(.*?)///ENTITYEND///#si', '$1', $this->doc); 1548 } 1549 1550 /** 1551 * Functions fixes few problems which come from imagereference plugin. 1552 */ 1553 protected function _fixImageRef() { 1554 $this->doc = str_replace('[h!]{\centering}', '[!ht]{\centering}', $this->doc); 1555 $this->doc = str_replace('\\ref{', '\autoref{', $this->doc); 1556 } 1557 1558 /** 1559 * Function sets, if the next link will be inserted to the file recursively. 1560 * @param bool $recursive Will next link be added recursively? 1561 */ 1562 public function _setRecursive($recursive) { 1563 $this->recursive = $recursive; 1564 } 1565 1566 /** 1567 * Function increases header level of a given number. 1568 * @param int $level Size of the increase. 1569 */ 1570 public function _increaseLevel($level) { 1571 $this->last_level_increase = $level; 1572 $this->headers_level += $level; 1573 } 1574 1575 /** 1576 * function replacing some characters in MathJax mode 1577 * @param string $data Parsed text. 1578 */ 1579 public function _mathMode($data) { 1580 $data = str_replace('<=>', '\Leftrightarrow', $data); 1581 $data = str_replace('<->', '\leftrightarrow', $data); 1582 $data = str_replace('->', '\rightarrow', $data); 1583 $data = str_replace('<-', '\leftarrow', $data); 1584 $data = str_replace('=>', '\Rightarrow', $data); 1585 $data = str_replace('<=', '\Leftarrow', $data); 1586 $data = str_replace('...', '\ldots', $data); 1587 $data = str_replace('−', '-', $data); 1588 1589 $this->doc .= $data; 1590 } 1591 1592 /** 1593 * Function creates label from a header name. 1594 * @param string $text A header name. 1595 * @return string Label 1596 */ 1597 protected function _createLabel($text) { 1598 $text = preg_replace('#///ENTITYSTART///(.*?)///ENTITYEND///#si', '$1', $text); 1599 $text = $this->_stripDiacritics($text); 1600 $text = strtolower($text); 1601 $text = str_replace(" ", "_", $text); 1602 $text = $this->_removeMathAndSymbols($text); 1603 return $text; 1604 } 1605 1606 /** 1607 * Escapes some characters in the URL. 1608 * @param string $link The URL. 1609 * @return string Escaped URL. 1610 */ 1611 protected function _secureLink($link) { 1612 $link = str_replace("\\", "\\\\", $link); 1613 $link = str_replace("#", "\#", $link); 1614 $link = str_replace("%", "\%", $link); 1615 $link = str_replace("&", "\&", $link); 1616 return $link; 1617 } 1618 1619 /** 1620 * Prepares the ZIP archive. 1621 * @global string $conf global dokuwiki configuration 1622 * @global ZipArchive $zip pointer to our zip archive 1623 */ 1624 protected function _prepareZIP() { 1625 global $conf; 1626 /** @var ZipArchive $zip */ 1627 global $zip; 1628 1629 //generate filename 1630 $filename = $conf["tmpdir"] . "/output" . time() . ".zip"; 1631 //create ZIP archive 1632 if ($zip->open($filename, ZipArchive::CREATE) !== TRUE) { 1633 exit("LaTeXit was not able to open <$filename>, check access rights.\n"); 1634 } 1635 } 1636 1637 /** 1638 * Function prints the image command into the LaTeX file. 1639 * @param string $path relative path of the image. 1640 * @param string $align image align 1641 * @param string $media_folder path to the media folder. 1642 */ 1643 protected function _insertImage($path, $align, $media_folder) { 1644 $pckg = new Package('graphicx'); 1645 $pckg->addCommand('\\graphicspath{{' . $media_folder . '/}}'); 1646 $this->store->addPackage($pckg); 1647 1648 1649 //http://stackoverflow.com/questions/2395882/how-to-remove-extension-from-string-only-real-extension 1650 $path = preg_replace("/\\.[^.\\s]{3,4}$/", "", $path); 1651 1652 //print align command 1653 if (!is_null($align)) { 1654 switch ($align) { 1655 case "center": 1656 $this->_c('centering', NULL, 0); 1657 break; 1658 case "left": 1659 $this->_c('raggedleft', NULL, 0); 1660 break; 1661 case "right": 1662 $this->_c('raggedright', NULL, 0); 1663 break; 1664 default : 1665 break; 1666 } 1667 } 1668 //insert image with params from config. 1669 $this->_c('includegraphics', $path, 1, array($this->getConf('image_params'))); 1670 } 1671 1672 /** 1673 * Inserts a link to media file other from an image. 1674 * @param string $path Relative path to the file. 1675 * @param string $title Title of the link. 1676 * @param string $media_folder Location of media folder. 1677 */ 1678 protected function _insertFile($path, $title, $media_folder) { 1679 $path = $media_folder . "/" . $path; 1680 $this->filelink($path, $title); 1681 } 1682 1683 /** 1684 * General function for inserting links 1685 * @param string $url Link URL. 1686 * @param string $title Link title. 1687 * @param string $type Link type (internal/external/email) 1688 * @param string $link_original Original link (for internal links it is used as a title) 1689 */ 1690 protected function _insertLink($url, $title, $type, $link_original = NULL) { 1691 $this->_insertLinkPackages(); 1692 1693 if ($type == "email") { 1694 $mailto = "mailto:"; 1695 } else { 1696 $mailto = ""; 1697 } 1698 1699 //no title was specified 1700 if (is_null($title) || (!is_array($title) && trim($title) == '')) { 1701 //for internal links, original DW link is inserted as a title 1702 if ($type == "internal") { 1703 $this->doc .= '\\href{' . $mailto . $url . '}{' . $link_original . '}'; 1704 } 1705 //email links have to contain mailto and address is used as text 1706 elseif ($type == "email") { 1707 $this->doc .= '\\href{' . $mailto . $url . '}{' . $url . '}'; 1708 } 1709 //reqular external link inserts the whole URL 1710 else { 1711 $this->doc .= '\\url{' . $mailto . $url . '}'; 1712 } 1713 } else { 1714 //is title an image? 1715 if (is_array($title)) { 1716 $this->doc .= '\\href{' . $mailto . $url . '}{'; 1717 if ($title["type"] == "internalmedia") { 1718 $this->internalmedia($title["src"], $title["title"], $title["align"]); 1719 } else { 1720 $this->externalmedia($title["src"], $title["title"], $title["align"]); 1721 } 1722 $this->doc .= '}'; 1723 } else { 1724 $this->doc .= '\\href{' . $mailto . $url . '}{' . $title . '}'; 1725 } 1726 } 1727 } 1728 1729 /** 1730 * Handle a new BibEntry 1731 * @param string $entry 1732 */ 1733 public function _bibEntry($entry) { 1734 if ($this->_useBibliography()) { 1735 $this->bib_handler->insert($entry); 1736 } 1737 } 1738 1739 /** 1740 * Escape the text, so it can be used as an pdf string for headers 1741 * @param string $text 1742 * @return string 1743 */ 1744 protected function _pdfString($text) { 1745 $text = $this->_stripDiacritics($this->_latexSpecialChars($text)); 1746 $text = $this->_removeMathAndSymbols($text); 1747 return $text; 1748 } 1749 1750 /** 1751 * Removes all math and symbols from the text. 1752 * @param string $text 1753 * @return string 1754 */ 1755 protected function _removeMathAndSymbols($text) { 1756 $text = preg_replace("#\$(.*)\$#", "", $text); 1757 //next regex comes from this site: 1758 //http://stackoverflow.com/questions/5199133/function-to-return-only-alpha-numeric-characters-from-string 1759 $text = preg_replace("/[^a-zA-Z0-9_ ]+/", "", $text); 1760 return $text; 1761 } 1762 1763 public function _useBibliography() { 1764 if (is_null($this->bib_handler)) { 1765 return false; 1766 } else { 1767 return true; 1768 } 1769 } 1770 1771 /** 1772 * Initializes store variable. 1773 */ 1774 protected function _initializeStore() { 1775 $this->store = $this->loadHelper('latexit', true); 1776 } 1777 1778 /** 1779 * Function removing diacritcs from a text. 1780 * From http://cs.wikibooks.org/wiki/PHP_prakticky/Odstran%C4%9Bn%C3%AD_diakritiky 1781 * @param string $data Text with diacritics 1782 * @return string Text withou diacritics 1783 */ 1784 protected function _stripDiacritics($data) { 1785 $table = Array( 1786 'ä' => 'a', 1787 'Ä' => 'A', 1788 'á' => 'a', 1789 'Á' => 'A', 1790 'à' => 'a', 1791 'À' => 'A', 1792 'ã' => 'a', 1793 'Ã' => 'A', 1794 'â' => 'a', 1795 'Â' => 'A', 1796 'č' => 'c', 1797 'Č' => 'C', 1798 'ć' => 'c', 1799 'Ć' => 'C', 1800 'ď' => 'd', 1801 'Ď' => 'D', 1802 'ě' => 'e', 1803 'Ě' => 'E', 1804 'é' => 'e', 1805 'É' => 'E', 1806 'ë' => 'e', 1807 'Ë' => 'E', 1808 'è' => 'e', 1809 'È' => 'E', 1810 'ê' => 'e', 1811 'Ê' => 'E', 1812 'í' => 'i', 1813 'Í' => 'I', 1814 'ï' => 'i', 1815 'Ï' => 'I', 1816 'ì' => 'i', 1817 'Ì' => 'I', 1818 'î' => 'i', 1819 'Î' => 'I', 1820 'ľ' => 'l', 1821 'Ľ' => 'L', 1822 'ĺ' => 'l', 1823 'Ĺ' => 'L', 1824 'ń' => 'n', 1825 'Ń' => 'N', 1826 'ň' => 'n', 1827 'Ň' => 'N', 1828 'ñ' => 'n', 1829 'Ñ' => 'N', 1830 'ó' => 'o', 1831 'Ó' => 'O', 1832 'ö' => 'o', 1833 'Ö' => 'O', 1834 'ô' => 'o', 1835 'Ô' => 'O', 1836 'ò' => 'o', 1837 'Ò' => 'O', 1838 'õ' => 'o', 1839 'Õ' => 'O', 1840 'ő' => 'o', 1841 'Ő' => 'O', 1842 'ř' => 'r', 1843 'Ř' => 'R', 1844 'ŕ' => 'r', 1845 'Ŕ' => 'R', 1846 'š' => 's', 1847 'Š' => 'S', 1848 'ś' => 's', 1849 'Ś' => 'S', 1850 'ť' => 't', 1851 'Ť' => 'T', 1852 'ú' => 'u', 1853 'Ú' => 'U', 1854 'ů' => 'u', 1855 'Ů' => 'U', 1856 'ü' => 'u', 1857 'Ü' => 'U', 1858 'ù' => 'u', 1859 'Ù' => 'U', 1860 'ũ' => 'u', 1861 'Ũ' => 'U', 1862 'û' => 'u', 1863 'Û' => 'U', 1864 'ý' => 'y', 1865 'Ý' => 'Y', 1866 'ž' => 'z', 1867 'Ž' => 'Z', 1868 'ź' => 'z', 1869 'Ź' => 'Z' 1870 ); 1871 1872 return strtr($data, $table); 1873 } 1874 1875} 1876