1<?php 2 3// must be run within Dokuwiki 4if(!defined('DOKU_INC')) die(); 5 6require_once DOKU_PLUGIN . 'latexport/implementation/decorator.php'; 7 8/** 9 * Final tex decorator, takes care of all formatting that does not 10 * require state machines, and stores content to the archive. 11 * Can add more layers of decorators over it, but this decorator has always to 12 * be at the bottom layer. 13 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 14 * @author Jean-Michel Gonet <jmgonet@yahoo.com> 15 */ 16class DecoratorPersister extends Decorator { 17 18 /** Where to save images. */ 19 const GRAPHICSPATH = 'images/'; 20 21 /** Content of the document is saved in the ZIP archive. */ 22 private $archive; 23 24 /** Counts the number of matters (frontmatter = 0, mainmatter = 1, etc.) */ 25 private $matterNumber; 26 27 private $pageId; 28 29 private $firstHeader; 30 31 /** 32 * Class constructor. 33 * @param archive Will receive the content of the document. 34 */ 35 function __construct($archive) { 36 $this->archive = $archive; 37 $this->matterNumber = 0; 38 } 39 40 ////////////////////////////////////////////////////////////////////////////////// 41 ////////////////////////////////////////////////////////////////////////////////// 42 // // 43 // Handle latexport syntax. // 44 // // 45 ////////////////////////////////////////////////////////////////////////////////// 46 ////////////////////////////////////////////////////////////////////////////////// 47 48 /** 49 * Receives a local file to include. 50 * @param $link string Local file to include. 51 */ 52 function input($link) { 53 $this->appendCommand("input", $link); 54 } 55 56 /** 57 * Adds a latex command to the document. 58 * @param command The command 59 * @param scope The name of the scope, or the mandatory argument, 60 * to be included inside the curly brackets. 61 * @param argument If specified, to be included in square brackets. Depending 62 * on the command, square brackets are placed before or after 63 * the curly brackets. 64 */ 65 function appendCommand($command, $scope = null, $argument = '') { 66 $this->appendInlineCommand($command, $scope, $argument); 67 $this->archive->appendContent("\r\n"); 68 } 69 70 /** 71 * Adds a latex command to the document. 72 * @param command The command 73 * @param scope The name of the scope, or the mandatory argument, 74 * to be included inside the curly brackets. 75 * @param argument If specified, to be included in square brackets. Depending 76 * on the command, square brackets are placed before or after 77 * the curly brackets. 78 */ 79 function appendInlineCommand($command, $scope = null, $argument = '') { 80 if ($argument) { 81 switch($command) { 82 // Some commands have the optional arguments after the curly brackets: 83 case 'begin': 84 case 'end': 85 switch($scope) { 86 case 'minipage': 87 $text = '\\'.$command.'{'.$scope.'}{'.$argument.'}'; 88 break; 89 90 default: 91 $text = '\\'.$command.'{'.$scope.'}['.$argument.']'; 92 break; 93 } 94 break; 95 96 // Most commands have the optional arguments before the curly brackets: 97 default: 98 $text = '\\'.$command.'['.$argument.']{'.$scope.'}'; 99 break; 100 } 101 } 102 // If there is no argument, then there is only one way to express a command... 103 else { 104 if ($scope) { 105 $text = '\\'.$command.'{'.$scope.'}'; 106 } 107 // ... unless there is no scope: 108 else { 109 $text = '\\'.$command; 110 } 111 } 112 113 // Let's render the command: 114 $this->archive->appendContent("$text"); 115 } 116 117 /** 118 * Adds simple content to the document. 119 * @param c The content. 120 */ 121 function appendContent($c) { 122 $this->archive->appendContent($c); 123 } 124 125 /** 126 * Renders a label (crossreference) so it can be referenced from elsewhere in the document. 127 * Places a line break after the label. 128 * @param link String The label identifier. The method precedes it with the page id. If null, 129 * then the label contains only the page id. 130 */ 131 function appendLabel($link = null) { 132 $this->appendLabelInline($link); 133 $this->appendContent("\r\n"); 134 } 135 136 /** 137 * Renders a label (crossreference) so it can be referenced from elsewhere in the document. 138 * Does not place a linebreak after the label. 139 * @param link String The label identifier. The method precedes it with the page id. If null, 140 * then the label contains only the page id. 141 */ 142 function appendLabelInline($link = null) { 143 if ($link) { 144 $label = $this->pageId.':'.$this->texifyReference($link); 145 } else { 146 $label = $this->pageId; 147 } 148 $this->appendInlineCommand('label', $label); 149 } 150 151 ////////////////////////////////////////////////////////////////////////////////// 152 ////////////////////////////////////////////////////////////////////////////////// 153 // // 154 // Handle plugin syntax like mathjax, anchor... // 155 // // 156 ////////////////////////////////////////////////////////////////////////////////// 157 ////////////////////////////////////////////////////////////////////////////////// 158 159 /** 160 * Receives mathematic formula from Mathjax plugin. 161 */ 162 function mathjax_content($formula) { 163 // The '%' is a comment in latex, you can't use it in Mathjax: 164 $formula = str_replace('%', '\\%', $formula); 165 166 // As Mathjax already uses latex separators, there is no need to reprocess: 167 $this->appendContent("$formula"); 168 } 169 170 /** 171 * Receives the anchors from the 'anchor' plugin. 172 * @param string $link The anchor name. 173 * @param string $title The associated text. 174 */ 175 function anchor($link, $title = null) { 176 $this->appendLabelInline($link); 177 $this->appendContent($this->texifyText($title)); 178 } 179 180 ////////////////////////////////////////////////////////////////////////////////// 181 ////////////////////////////////////////////////////////////////////////////////// 182 // // 183 // Handle standard dokuwiki syntax // 184 // // 185 ////////////////////////////////////////////////////////////////////////////////// 186 ////////////////////////////////////////////////////////////////////////////////// 187 188 /** 189 * Starts rendering a new page. 190 * @param string $pageId The identifier of the opening page. 191 * @param int $recursionLevel The level of recursion. When a page includes a page, that's one level of recursion. 192 */ 193 function document_start($pageId = NULL, $recursionLevel = 0) { 194 $this->pageId = $pageId; 195 $this->firstHeader = true; 196 } 197 198 /** 199 * Ends the document 200 */ 201 function document_end($recursionLevel = 0){ 202 if ($recursionLevel == 0) { 203 $this->appendCommand('end', 'document'); 204 } 205 } 206 207 /** 208 * Table of content is not rendered in latex. 209 */ 210 function render_TOC() { 211 // Do nothing. 212 } 213 214 /** 215 * TOC items are not rendered in latex. 216 */ 217 function toc_additem($id, $text, $level) { 218 // Do nothing. 219 } 220 221 /** 222 * Headers are transformed in part, chapter, section, subsection and subsubsection. 223 */ 224 function header($text, $level, $pos) { 225 switch($level) { 226 case 1: 227 switch($this->matterNumber) { 228 case 0: 229 $this->appendContent("\\mainmatter\r\n"); 230 $this->matterNumber = 1; 231 break; 232 case 1: 233 $this->appendContent("\\appendix\r\n"); 234 $this->matterNumber = 2; 235 break; 236 default: 237 $this->appendCommand('chapter', $this->texifyText($text)); 238 $this->appendLabel($text); 239 break; 240 } 241 break; 242 243 case 2: 244 $this->appendCommand('part', $this->texifyText($text)); 245 $this->appendLabel($text); 246 break; 247 case 3: 248 $this->appendCommand('chapter', $this->texifyText($text)); 249 $this->appendLabel($text); 250 break; 251 case 4: 252 $this->appendCommand('section', $this->texifyText($text)); 253 $this->appendLabel($text); 254 break; 255 default: 256 $this->appendCommand('subsection', $this->texifyText($text)); 257 $this->appendLabel($text); 258 break; 259 } 260 261 if ($this->firstHeader) { 262 $this->appendLabel(); 263 $this->firstHeader = false; 264 } 265 266 } 267 268 /** 269 * Sections are rendered as title-less headers. 270 * @param int $level section level (as determined by the previous header) 271 */ 272 function section_open($level) { 273 // Nothing to do. 274 } 275 276 /** 277 * Close the current section 278 */ 279 function section_close() { 280 // Nothing to do. 281 } 282 283 /** 284 * Renders plain text. 285 */ 286 function cdata($text) { 287 $this->appendContent($this->texifyText($text)); 288 } 289 290 /** 291 * Open a paragraph. 292 */ 293 function p_open() { 294 // Nothing to do. 295 } 296 297 /** 298 * Close a paragraph. 299 */ 300 function p_close() { 301 $this->appendContent("\r\n\r\n"); 302 } 303 304 /** 305 * Create a line break 306 */ 307 function linebreak() { 308 $this->appendContent(" \\\\ "); 309 } 310 311 /** 312 * Create a horizontal line 313 */ 314 function hr() { 315 $this->appendContent("\r\n\r\n\\noindent\\makebox[\\linewidth]{\\rule{\\paperwidth}{0.4pt}}\r\n"); 316 } 317 318 /** 319 * Start strong (bold) formatting 320 */ 321 function strong_open() { 322 $this->appendContent("\\textbf{"); 323 } 324 325 /** 326 * Stop strong (bold) formatting 327 */ 328 function strong_close() { 329 $this->appendContent("}"); 330 } 331 332 /** 333 * Start emphasis (italics) formatting 334 */ 335 function emphasis_open() { 336 $this->appendContent("\\emph{"); 337 } 338 339 /** 340 * Stop emphasis (italics) formatting 341 */ 342 function emphasis_close() { 343 $this->appendContent("}"); 344 } 345 346 /** 347 * Start underline formatting 348 */ 349 function underline_open() { 350 $this->appendContent("\\underline{"); 351 } 352 353 /** 354 * Stop underline formatting 355 */ 356 function underline_close() { 357 $this->appendContent("}"); 358 } 359 360 /** 361 * Start monospace formatting 362 */ 363 function monospace_open() { 364 $this->appendContent("\\texttt{"); 365 } 366 367 /** 368 * Stop monospace formatting 369 */ 370 function monospace_close() { 371 $this->appendContent("}"); 372 } 373 374 /** 375 * Start a subscript 376 */ 377 function subscript_open() { 378 $this->appendContent("\\textsubscript{"); 379 } 380 381 /** 382 * Stop a subscript 383 */ 384 function subscript_close() { 385 $this->appendContent("}"); 386 } 387 388 /** 389 * Start a superscript 390 */ 391 function superscript_open() { 392 $this->appendContent("\\textsuperscript{"); 393 } 394 395 /** 396 * Stop a superscript 397 */ 398 function superscript_close() { 399 $this->appendContent("}"); 400 } 401 402 /** 403 * Start deleted (strike-through) formatting 404 */ 405 function deleted_open() { 406 $this->appendContent("\\st{"); 407 } 408 409 /** 410 * Stop deleted (strike-through) formatting 411 */ 412 function deleted_close() { 413 $this->appendContent("}"); 414 } 415 416 /** 417 * Start a footnote 418 */ 419 function footnote_open() { 420 $this->appendContent("\\footnote{"); 421 } 422 423 /** 424 * Stop a footnote 425 */ 426 function footnote_close() { 427 $this->appendContent("}"); 428 } 429 430 /** 431 * Open an unordered list 432 */ 433 function listu_open() { 434 $this->appendCommand('begin', 'itemize'); 435 } 436 437 /** 438 * Close an unordered list 439 */ 440 function listu_close() { 441 $this->appendCommand('end', 'itemize'); 442 } 443 444 /** 445 * Open an ordered list 446 */ 447 function listo_open() { 448 $this->appendCommand('begin', 'enumerate'); 449 } 450 451 /** 452 * Close an ordered list 453 */ 454 function listo_close() { 455 $this->appendCommand('end', 'enumerate'); 456 } 457 458 /** 459 * Open a list item 460 * 461 * @param int $level the nesting level 462 * @param bool $node true when a node; false when a leaf 463 */ 464 function listitem_open($level,$node=false) { 465 $this->appendContent(str_repeat(' ', $level).'\\item '); 466 } 467 468 /** 469 * Start the content of a list item 470 */ 471 function listcontent_open() { 472 // Nothing to do. 473 } 474 475 /** 476 * Stop the content of a list item 477 */ 478 function listcontent_close() { 479 $this->appendContent("\r\n"); 480 } 481 482 /** 483 * Close a list item 484 */ 485 function listitem_close() { 486 // Nothing to do. 487 } 488 489 /** 490 * Output unformatted $text 491 * 492 * @param string $text 493 */ 494 function unformatted($text) { 495 $this->appendCommand("begin", "verbatim"); 496 $this->appendContent($text); 497 $this->appendCommand("end", "verbatim"); 498 } 499 /** 500 * Output inline PHP code 501 * 502 * @param string $text The PHP code 503 */ 504 function php($text) { 505 $this->monospace_open(); 506 $this->cdata($text); 507 $this->monospace_close(); 508 } 509 510 /** 511 * Output block level PHP code 512 * 513 * @param string $text The PHP code 514 */ 515 function phpblock($text) { 516 $this->appendCommand("begin", "lstlisting", "language=php, style=php-style"); 517 $this->appendContent($text); 518 $this->appendCommand("end", "lstlisting"); 519 } 520 521 /** 522 * Output raw inline HTML 523 * 524 * If $conf['htmlok'] is true this should add the code as is to $doc 525 * 526 * @param string $text The HTML 527 */ 528 function html($text) { 529 $this->monospace_open(); 530 $this->cdata($text); 531 $this->monospace_close(); 532 } 533 534 /** 535 * Output raw block-level HTML 536 * 537 * If $conf['htmlok'] is true this should add the code as is to $doc 538 * 539 * @param string $text The HTML 540 */ 541 function htmlblock($text) { 542 $this->appendCommand("begin", "lstlisting", "language=html, style=html-style"); 543 $this->appendContent($text); 544 $this->appendCommand("end", "lstlisting"); 545 } 546 547 /** 548 * Output preformatted text 549 * 550 * @param string $text 551 */ 552 function preformatted($text) { 553 $this->unformatted($text); 554 } 555 556 /** 557 * Start a block quote 558 */ 559 function quote_open() { 560 $this->appendCommand("begin", "displayquote"); 561 } 562 563 /** 564 * Stop a block quote 565 */ 566 function quote_close() { 567 $this->appendCommand("end", "displayquote"); 568 } 569 570 /** 571 * Display text as file content, optionally syntax highlighted 572 * 573 * @param string $text text to show 574 * @param string $lang programming language to use for syntax highlighting 575 * @param string $file file path label 576 */ 577 function file($text, $lang = null, $file = null) { 578 if ($file) { 579 $this->unformatted("--> $file"); 580 } 581 if ($lang) { 582 $this->appendCommand("begin", "lstlisting", "language=$lang, style=$lang-style"); 583 } else { 584 $this->appendCommand("begin", "lstlisting"); 585 } 586 $this->appendContent($text); 587 $this->appendCommand("end", "lstlisting"); 588 } 589 590 /** 591 * Display text as code content, optionally syntax highlighted 592 * 593 * @param string $text text to show 594 * @param string $lang programming language to use for syntax highlighting 595 * @param string $file file path label 596 */ 597 function code($text, $lang = null, $file = null) { 598 $this->file($text, $lang, $file); 599 } 600 601 /** 602 * Format an acronym 603 * Uses $this->acronyms 604 * @param string $acronym 605 */ 606 function acronym($acronym) { 607 $this->cdata($acronym); 608 } 609 610 /** 611 * Format a smiley 612 * Uses $this->smiley 613 * @param string $smiley 614 */ 615 function smiley($smiley) { 616 $this->cdata($smiley); 617 } 618 619 /** 620 * Format an entity 621 * Entities are basically small text replacements 622 * @param string $entity 623 */ 624 function entity($entity) { 625 $this->cdata($entity); 626 } 627 628 /** 629 * Typographically format a multiply sign 630 * 631 * Example: ($x=640, $y=480) should result in "640×480" 632 * 633 * @param string|int $x first value 634 * @param string|int $y second value 635 */ 636 function multiplyentity($x, $y) { 637 $this->mathjax_content("\\( $x \\times $y \\)"); 638 } 639 640 /** 641 * Render an opening single quote char (language specific) 642 */ 643 function singlequoteopening() { 644 $this->cdata("`"); 645 } 646 647 /** 648 * Render a closing single quote char (language specific) 649 */ 650 function singlequoteclosing() { 651 $this->cdata("´"); 652 } 653 654 /** 655 * Render an apostrophe char (language specific) 656 */ 657 function apostrophe() { 658 $this->cdata("’"); 659 } 660 661 /** 662 * Render an opening double quote char (language specific) 663 */ 664 function doublequoteopening() { 665 $this->cdata("“"); 666 } 667 668 /** 669 * Render an closinging double quote char (language specific) 670 */ 671 function doublequoteclosing() { 672 $this->cdata("”"); 673 } 674 675 /** 676 * Render a CamelCase link 677 * 678 * @param string $link The link name 679 * @see http://en.wikipedia.org/wiki/CamelCase 680 */ 681 function camelcaselink($link) { 682 $this->externallink($link); 683 } 684 685 /** 686 * Render a page local link 687 * 688 * @param string $hash hash link identifier 689 * @param string $name name for the link 690 */ 691 function locallink($hash, $name = null) { 692 $this->internallink($this->pageId.":".$hash, $name); 693 } 694 695 /** 696 * Render a wiki internal link. 697 * Internal links at the very beginning of an unordered item include 698 * the destination page. 699 * @param string $link page ID to link to. eg. 'wiki:syntax' 700 * @param string|array $title name for the link, array for media file 701 */ 702 function internallink($link, $title = null) { 703 $this->appendContent($this->texifyText($text)); 704 $this->appendContent(" "); 705 $this->appendInlineCommand("ref", $this->texifyReference($link)); 706 } 707 708 /** 709 * Render an external link 710 * 711 * @param string $link full URL with scheme 712 * @param string|array $title name for the link, array for media file 713 */ 714 function externallink($link, $title = null) { 715 $this->appendContent($this->texifyText($text).' \\url{'.$link.'}'); 716 } 717 718 /** 719 * Render the output of an RSS feed 720 * 721 * @param string $url URL of the feed 722 * @param array $params Finetuning of the output 723 */ 724 function rss($url, $params) { 725 // Nothing to do. 726 } 727 728 /** 729 * Render an interwiki link 730 * 731 * You may want to use $this->_resolveInterWiki() here 732 * 733 * @param string $link original link - probably not much use 734 * @param string|array $title name for the link, array for media file 735 * @param string $wikiName indentifier (shortcut) for the remote wiki 736 * @param string $wikiUri the fragment parsed from the original link 737 */ 738 function interwikilink($link, $title = null, $wikiName, $wikiUri) { 739 $this->externalLink($link, trim($title.' '.$wikiName)); 740 } 741 742 /** 743 * Link to file on users OS 744 * 745 * @param string $link the link 746 * @param string|array $title name for the link, array for media file 747 */ 748 function filelink($link, $title = null) { 749 // Nothing to do. 750 } 751 752 /** 753 * Link to windows share 754 * 755 * @param string $link the link 756 * @param string|array $title name for the link, array for media file 757 */ 758 function windowssharelink($link, $title = null) { 759 // Nothing to do. 760 } 761 762 /** 763 * Render a linked E-Mail Address 764 * 765 * Should honor $conf['mailguard'] setting 766 * 767 * @param string $address Email-Address 768 * @param string|array $name name for the link, array for media file 769 */ 770 function emaillink($address, $name = null) { 771 $this->appendContent("$name \\href{mailto:$address}{$address} "); 772 } 773 774 /** 775 * Render an internal media file 776 * 777 * @param string $src media ID 778 * @param string $title descriptive text 779 * @param string $align left|center|right 780 * @param int $width width of media in pixel 781 * @param int $height height of media in pixel 782 * @param string $cache cache|recache|nocache 783 * @param string $linking linkonly|detail|nolink 784 * @param int $positionInGroup Position of the media in the group. 785 * @param int $totalInGroup Size of the group of media. 786 */ 787 function internalmedia($src, $title = null, $align = null, $width = null, 788 $height = null, $cache = null, $linking = null, 789 $positionInGroup = 0, $totalInGroup = 1) { 790 791 // Find the image and estimate its real size: 792 $filename = $this->obtainFilename($src); 793 if (!$this->isPrintable($filename)) { 794 $this->cdata($title); 795 return; 796 } 797 list($width, $height) = getimagesize($filename); 798 799 // Opens the group of images: 800 if ($positionInGroup == 0) { 801 $this->appendCommand('begin', 'figure', '!htb'); 802 } 803 804 // Places the image: 805 $availableSpace = round(1 / $totalInGroup, 1); 806 $sizeInCmAt240ppi = round(2.54 * $width / 240, 1); 807 $angle = 0; 808 809 $this->appendCommand('begin', 'minipage', "$availableSpace\\textwidth"); 810 $this->appendCommand('centering'); 811 812 $this->appendCommand('includegraphics', $this->insertImage($filename), 813 "width=".$sizeInCmAt240ppi."cm, max width=\\textwidth, angle=$angle"); 814 815 if (!empty($title)) { 816 $this->appendCommand('caption', $this->texifyText($title)); 817 } 818 819 $this->appendCommand('end', 'minipage'); 820 821 // Closes the group of images: 822 if ($positionInGroup == $totalInGroup - 1) { 823 $this->appendCommand('end', 'figure'); 824 } else { 825 $this->appendCommand('hfill'); 826 } 827 } 828 829 /** 830 * Returns true if provided filename's extension is of a printable media. 831 * @param filename String the file name. 832 * @return boolean true if file is printable. 833 */ 834 private function isPrintable($filename) { 835 $ext = pathinfo($filename, PATHINFO_EXTENSION); 836 837 switch($ext) { 838 case "jpg": 839 case "jpeg": 840 case "gif": 841 case "png": 842 return true; 843 844 default: 845 return false; 846 } 847 } 848 849 /** 850 * Obtains the filesystem path to the specified resource. 851 * @param $src String The resource. 852 * @return String The file name. 853 */ 854 private function obtainFilename($src) { 855 global $ID; 856 list($src, $hash) = explode('#', $src, 2); 857 resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true); 858 return mediaFN($src); 859 } 860 861 /** 862 * Inserts the specified file. 863 * @param The physical path to the file. 864 * @return The TeX-ified name of the file. 865 */ 866 private function insertImage($filename) { 867 $baseFilename = $this->texifyFilename(basename($filename)); 868 $this->archive->insertContent(self::GRAPHICSPATH.$baseFilename, file_get_contents($filename)); 869 return $baseFilename; 870 } 871 872 /** 873 * Does not render external media files 874 */ 875 function externalmedia($src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null) { 876 // Nothing to do 877 } 878 879 /** 880 * Render a link to an internal media file. 881 * There is no correct way to render this on a printed media, so we just display the title. 882 */ 883 function internalmedialink($src, $title = null, $align = null, $width = null, $height = null, $cache = null) { 884 $this->cdata($title); 885 } 886 887 /** 888 * Render a link to an external media file as a url. 889 */ 890 function externalmedialink($src, $title = null, $align = null, $width = null, $height = null, $cache = null) { 891 $this->externallink($src, $title); 892 } 893 894 /** 895 * Start a table 896 * 897 * @param int $maxcols maximum number of columns 898 * @param int $numrows NOT IMPLEMENTED 899 * @param int $pos byte position in the original source 900 */ 901 function table_open($maxcols = null, $numrows = null, $pos = null) { 902 $this->appendCommand("begin", "table", "h"); 903 $this->appendCommand("begin", "center"); 904 $this->appendContent("\\begin{tabular}{|".str_repeat("c|", $maxcols)."}\\hline\r\n"); 905 } 906 907 /** 908 * Close a table 909 * 910 * @param int $pos byte position in the original source 911 */ 912 function table_close($pos = null) { 913 $this->appendCommand("end", "tabular"); 914 $this->appendCommand("end", "center"); 915 $this->appendCommand("end", "table"); 916 } 917 918 /** 919 * Open a table header 920 */ 921 function tablethead_open() { 922 // Nothing to do 923 } 924 925 /** 926 * Close a table header 927 */ 928 function tablethead_close() { 929 // Nothing to do 930 } 931 932 /** 933 * Open a table body 934 */ 935 function tabletbody_open() { 936 // Nothing to do 937 } 938 939 /** 940 * Close a table body 941 */ 942 function tabletbody_close() { 943 // Nothing to do 944 } 945 946 /** 947 * Open a table footer 948 */ 949 function tabletfoot_open() { 950 // Nothing to do 951 } 952 953 /** 954 * Close a table footer 955 */ 956 function tabletfoot_close() { 957 // Nothing to do 958 } 959 960 private $firstCellInRow; 961 962 /** 963 * Open a table row 964 */ 965 function tablerow_open() { 966 $this->appendContent("\r\n"); 967 $this->firstCellInRow = true; 968 } 969 970 /** 971 * Close a table row 972 */ 973 function tablerow_close() { 974 $this->appendContent("\\\\\r\n"); 975 } 976 977 /** 978 * Open a table header cell 979 * 980 * @param int $colspan 981 * @param string $align left|center|right 982 * @param int $rowspan 983 */ 984 function tableheader_open($colspan = 1, $align = null, $rowspan = 1) { 985 if ($this->firstCellInRow) { 986 $this->firstCellInRow = false; 987 } else { 988 $this->appendContent(" &\r\n"); 989 } 990 $this->appendContent(" \\multicolumn{".$colspan."}".$this->alignment($align)."{\\multirow{".$rowspan."}{*}{\\thead{"); 991 } 992 993 /** 994 * Close a table header cell 995 */ 996 function tableheader_close() { 997 $this->appendContent("}}}"); 998 } 999 1000 /** 1001 * Open a table cell 1002 * 1003 * @param int $colspan 1004 * @param string $align left|center|right 1005 * @param int $rowspan 1006 */ 1007 function tablecell_open($colspan = 1, $align = null, $rowspan = 1) { 1008 if ($this->firstCellInRow) { 1009 $this->firstCellInRow = false; 1010 } else { 1011 $this->appendContent(" &\r\n"); 1012 } 1013 $this->appendContent(" \\multicolumn{".$colspan."}".$this->alignment($align)."{\\multirow{".$rowspan."}{*}{\\makecell{"); 1014 } 1015 1016 function alignment($align) { 1017 switch($align) { 1018 case "left": 1019 return "{|l|}"; 1020 case "right": 1021 return "{|r|}"; 1022 default: 1023 return "{|c|}"; 1024 } 1025 } 1026 1027 /** 1028 * Close a table cell 1029 */ 1030 function tablecell_close() { 1031 $this->appendContent("}}}"); 1032 } 1033 1034 function table_cline($start, $end) { 1035 $this->appendContent("\\cline{".$start." - ".$end."}"); 1036 } 1037 1038} 1039