001package org.apache.maven.doxia.module.latex; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.maven.doxia.sink.Sink; 023import org.apache.maven.doxia.sink.SinkEventAttributes; 024import org.apache.maven.doxia.sink.impl.AbstractTextSink; 025import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet; 026import org.apache.maven.doxia.util.DoxiaUtils; 027import org.apache.maven.doxia.util.LineBreaker; 028 029import org.codehaus.plexus.util.IOUtil; 030import org.codehaus.plexus.util.StringUtils; 031 032import java.io.IOException; 033import java.io.InputStream; 034import java.io.Writer; 035import java.util.ArrayList; 036import java.util.List; 037import java.util.Locale; 038import java.util.Stack; 039 040/** 041 * Latex Sink implementation. 042 * <br> 043 * <b>Note</b>: The encoding used is UTF-8. 044 * 045 * @since 1.0 046 */ 047public class LatexSink 048 extends AbstractTextSink 049{ 050 /** 051 * Flag that indicates if the document to be written is only a fragment. 052 * 053 * This implies that <code>\\begin{document}</code>, <code>\\title{..}</code> will not be output. 054 */ 055 private final boolean fragmentDocument; 056 057 private boolean ignoreText; 058 059 private final LineBreaker out; 060 061 private final String sinkCommands; 062 063 private final String preamble; 064 065 private boolean titleFlag; 066 067 private int numberedListNesting; 068 069 private boolean verbatimFlag; 070 071 private boolean figureFlag; 072 073 private boolean tableFlag; 074 075 private boolean gridFlag; 076 077 private int[] cellJustif; 078 079 private int cellCount; 080 081 private boolean isTitle; 082 083 private String title; 084 085 /** Keep track of the closing tags for inline events. */ 086 protected Stack<List<String>> inlineStack = new Stack<>(); 087 088 // ---------------------------------------------------------------------- 089 // 090 // ---------------------------------------------------------------------- 091 092 /** 093 * Constructor, initialize the Writer and the variables. 094 * 095 * @param out not null writer to write the result. <b>Should</b> be an UTF-8 Writer. 096 * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}. 097 */ 098 protected LatexSink( Writer out ) 099 { 100 this( out, null, null ); 101 } 102 103 /** 104 * Constructor, initialize the Writer and the variables. 105 * 106 * @param out not null writer to write the result. <b>Should</b> be an UTF-8 Writer. 107 * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}. 108 * @param sinkCommands A String representation of commands that go before \documentclass. 109 * @param preamble A String representation of commands that go between \documentclass and \begin{document}. 110 */ 111 protected LatexSink( Writer out, String sinkCommands, String preamble ) 112 { 113 this( out, sinkCommands, preamble, false ); 114 } 115 116 /** 117 * Constructor, initialize the Writer and the variables. 118 * 119 * @param out not null writer to write the result. <b>Should</b> be an UTF-8 Writer. 120 * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}. 121 * @param sinkCommands A String representation of commands that go before \documentclass. 122 * @param preamble A String representation of commands that go between \documentclass and \begin{document}. 123 * @param fragmentDocument If this receives events that that are only part of a document. 124 * Typically, headers are omitted if this is true. 125 */ 126 protected LatexSink( Writer out, String sinkCommands, String preamble, boolean fragmentDocument ) 127 { 128 this.out = new LineBreaker( out ); 129 130 if ( sinkCommands == null ) 131 { 132 sinkCommands = defaultSinkCommands(); 133 } 134 if ( preamble == null ) 135 { 136 preamble = defaultPreamble(); 137 } 138 139 this.sinkCommands = sinkCommands; 140 this.preamble = preamble; 141 this.fragmentDocument = fragmentDocument; 142 143 init(); 144 } 145 146 // ---------------------------------------------------------------------- 147 // Overridables 148 // ---------------------------------------------------------------------- 149 150 /** 151 * Returns a default \documentclass declaration. 152 * 153 * @return String. 154 */ 155 protected String getDocumentStart() 156 { 157 return "\\documentclass[a4paper]{article}" + EOL + EOL; 158 } 159 160 /** 161 * Returns a default \begin{document} declaration. 162 * 163 * @return String. 164 */ 165 protected String getDocumentBegin() 166 { 167 return "\\begin{document}" + EOL + EOL; 168 } 169 170 /** 171 * Returns a default \end{document} declaration. 172 * 173 * @return String. 174 */ 175 protected String getDocumentEnd() 176 { 177 return "\\end{document}" + EOL; 178 } 179 180 // ---------------------------------------------------------------------- 181 // Sink Implementation 182 // ---------------------------------------------------------------------- 183 184 /** 185 * {@inheritDoc} 186 */ 187 public void head() 188 { 189 head( null ); 190 } 191 192 /** {@inheritDoc} */ 193 public void head( SinkEventAttributes attributes ) 194 { 195 init(); 196 197 if ( !fragmentDocument ) 198 { 199 markup( sinkCommands ); 200 201 markup( getDocumentStart() ); 202 203 markup( preamble ); 204 205 markup( getDocumentBegin() ); 206 } 207 } 208 209 /** 210 * {@inheritDoc} 211 */ 212 public void body() 213 { 214 body( null ); 215 } 216 217 /** {@inheritDoc} */ 218 public void body( SinkEventAttributes attributes ) 219 { 220 if ( titleFlag ) 221 { 222 if ( fragmentDocument ) 223 { 224 markup( "\\section" ); 225 } 226 else 227 { 228 titleFlag = false; 229 markup( "\\maketitle" + EOL + EOL ); 230 } 231 } 232 } 233 234 /** 235 * {@inheritDoc} 236 */ 237 public void body_() 238 { 239 if ( !fragmentDocument ) 240 { 241 markup( getDocumentEnd() ); 242 } 243 244 flush(); 245 } 246 247 /** 248 * {@inheritDoc} 249 */ 250 public void title() 251 { 252 title( null ); 253 } 254 255 /** {@inheritDoc} */ 256 public void title( SinkEventAttributes attributes ) 257 { 258 if ( !fragmentDocument ) 259 { 260 titleFlag = true; 261 markup( "\\title{" ); 262 } 263 else 264 { 265 ignoreText = true; 266 } 267 } 268 269 /** 270 * {@inheritDoc} 271 */ 272 public void title_() 273 { 274 if ( !fragmentDocument ) 275 { 276 markup( "}" + EOL ); 277 } 278 else 279 { 280 ignoreText = false; 281 } 282 } 283 284 /** 285 * {@inheritDoc} 286 */ 287 public void author() 288 { 289 author( null ); 290 } 291 292 /** {@inheritDoc} */ 293 public void author( SinkEventAttributes attributes ) 294 { 295 if ( !fragmentDocument ) 296 { 297 markup( "\\author{" ); 298 } 299 else 300 { 301 ignoreText = true; 302 } 303 } 304 305 /** 306 * {@inheritDoc} 307 */ 308 public void author_() 309 { 310 if ( !fragmentDocument ) 311 { 312 markup( "}" + EOL ); 313 } 314 else 315 { 316 ignoreText = false; 317 } 318 } 319 320 /** 321 * {@inheritDoc} 322 */ 323 public void date() 324 { 325 date( null ); 326 } 327 328 /** {@inheritDoc} */ 329 public void date( SinkEventAttributes attributes ) 330 { 331 if ( !fragmentDocument ) 332 { 333 markup( "\\date{" ); 334 } 335 else 336 { 337 ignoreText = true; 338 } 339 } 340 341 /** 342 * {@inheritDoc} 343 */ 344 public void date_() 345 { 346 if ( !fragmentDocument ) 347 { 348 markup( "}" + EOL ); 349 } 350 else 351 { 352 ignoreText = false; 353 } 354 } 355 356 /** {@inheritDoc} */ 357 public void sectionTitle( int level, SinkEventAttributes attributes ) 358 { 359 isTitle = true; 360 } 361 362 /** {@inheritDoc} */ 363 public void sectionTitle_( int level ) 364 { 365 String command; 366 switch ( level ) 367 { 368 case SECTION_LEVEL_1: 369 command = "section"; 370 break; 371 case SECTION_LEVEL_2: 372 command = "subsection"; 373 break; 374 case SECTION_LEVEL_3: 375 command = "subsubsection"; 376 break; 377 case SECTION_LEVEL_4: 378 command = "paragraph"; 379 break; 380 case SECTION_LEVEL_5: 381 command = "subparagraph"; 382 break; 383 default: 384 throw new IllegalArgumentException( "Not a section level: " + level ); 385 } 386 387 isTitle = false; 388 389 if ( StringUtils.isNotEmpty( title ) ) 390 { 391 markup( EOL + "\\" + command + "{" + title + "}" + EOL ); 392 393 title = null; 394 } 395 } 396 397 // ---------------------------------------------------------------------- 398 // Section Title 1 399 // ---------------------------------------------------------------------- 400 401 /** 402 * {@inheritDoc} 403 */ 404 public void sectionTitle1() 405 { 406 sectionTitle( SECTION_LEVEL_1, null ); 407 } 408 409 /** 410 * {@inheritDoc} 411 */ 412 public void sectionTitle1_() 413 { 414 sectionTitle_( SECTION_LEVEL_1 ); 415 } 416 417 // ---------------------------------------------------------------------- 418 // Section Title 2 419 // ---------------------------------------------------------------------- 420 421 /** 422 * {@inheritDoc} 423 */ 424 public void sectionTitle2() 425 { 426 sectionTitle( SECTION_LEVEL_2, null ); 427 } 428 429 /** 430 * {@inheritDoc} 431 */ 432 public void sectionTitle2_() 433 { 434 sectionTitle_( SECTION_LEVEL_2 ); 435 } 436 437 // ---------------------------------------------------------------------- 438 // Section Title 3 439 // ---------------------------------------------------------------------- 440 441 /** 442 * {@inheritDoc} 443 */ 444 public void sectionTitle3() 445 { 446 sectionTitle( SECTION_LEVEL_3, null ); 447 } 448 449 /** 450 * {@inheritDoc} 451 */ 452 public void sectionTitle3_() 453 { 454 sectionTitle_( SECTION_LEVEL_3 ); 455 } 456 457 // ---------------------------------------------------------------------- 458 // Section Title 4 459 // ---------------------------------------------------------------------- 460 461 /** 462 * {@inheritDoc} 463 */ 464 public void sectionTitle4() 465 { 466 sectionTitle( SECTION_LEVEL_4, null ); 467 } 468 469 /** 470 * {@inheritDoc} 471 */ 472 public void sectionTitle4_() 473 { 474 sectionTitle_( SECTION_LEVEL_4 ); 475 } 476 477 // ---------------------------------------------------------------------- 478 // Section Title 5 479 // ---------------------------------------------------------------------- 480 481 /** 482 * {@inheritDoc} 483 */ 484 public void sectionTitle5() 485 { 486 sectionTitle( SECTION_LEVEL_5, null ); 487 } 488 489 /** 490 * {@inheritDoc} 491 */ 492 public void sectionTitle5_() 493 { 494 sectionTitle_( SECTION_LEVEL_5 ); 495 } 496 497 // ---------------------------------------------------------------------- 498 // List 499 // ---------------------------------------------------------------------- 500 501 /** 502 * {@inheritDoc} 503 */ 504 public void list() 505 { 506 list( null ); 507 } 508 509 /** {@inheritDoc} */ 510 public void list( SinkEventAttributes attributes ) 511 { 512 markup( EOL + "\\begin{itemize}" ); 513 } 514 515 /** 516 * {@inheritDoc} 517 */ 518 public void list_() 519 { 520 markup( EOL + "\\end{itemize}" + EOL ); 521 } 522 523 /** 524 * {@inheritDoc} 525 */ 526 public void listItem() 527 { 528 listItem( null ); 529 } 530 531 /** {@inheritDoc} */ 532 public void listItem( SinkEventAttributes attributes ) 533 { 534 markup( EOL + "\\item " ); 535 } 536 537 /** {@inheritDoc} */ 538 public void numberedList( int numbering ) 539 { 540 numberedList( numbering, null ); 541 } 542 543 /** {@inheritDoc} */ 544 public void numberedList( int numbering, SinkEventAttributes attributes ) 545 { 546 ++numberedListNesting; 547 548 String counter; 549 switch ( numberedListNesting ) 550 { 551 case 1: 552 counter = "enumi"; 553 break; 554 case 2: 555 counter = "enumii"; 556 break; 557 case 3: 558 counter = "enumiii"; 559 break; 560 case 4: 561 default: 562 counter = "enumiv"; 563 } 564 565 String style; 566 switch ( numbering ) 567 { 568 case NUMBERING_UPPER_ALPHA: 569 style = "Alph"; 570 break; 571 case NUMBERING_LOWER_ALPHA: 572 style = "alph"; 573 break; 574 case NUMBERING_UPPER_ROMAN: 575 style = "Roman"; 576 break; 577 case NUMBERING_LOWER_ROMAN: 578 style = "roman"; 579 break; 580 case NUMBERING_DECIMAL: 581 default: 582 style = "arabic"; 583 } 584 585 markup( EOL + "\\begin{enumerate}" + EOL ); 586 markup( "\\renewcommand{\\the" + counter + "}{\\" + style + "{" + counter + "}}" + EOL ); 587 } 588 589 /** 590 * {@inheritDoc} 591 */ 592 public void numberedList_() 593 { 594 markup( EOL + "\\end{enumerate}" + EOL ); 595 --numberedListNesting; 596 } 597 598 /** 599 * {@inheritDoc} 600 */ 601 public void numberedListItem() 602 { 603 numberedListItem( null ); 604 } 605 606 /** {@inheritDoc} */ 607 public void numberedListItem( SinkEventAttributes attributes ) 608 { 609 markup( "\\item " ); 610 } 611 612 /** 613 * {@inheritDoc} 614 */ 615 public void definitionList() 616 { 617 definitionList( null ); 618 } 619 620 /** {@inheritDoc} */ 621 public void definitionList( SinkEventAttributes attributes ) 622 { 623 markup( EOL + "\\begin{description}" ); 624 } 625 626 /** 627 * {@inheritDoc} 628 */ 629 public void definitionList_() 630 { 631 markup( EOL + "\\end{description}" + EOL ); 632 } 633 634 /** 635 * {@inheritDoc} 636 */ 637 public void definedTerm() 638 { 639 definedTerm( null ); 640 } 641 642 /** {@inheritDoc} */ 643 public void definedTerm( SinkEventAttributes attributes ) 644 { 645 markup( EOL + "\\item[\\mbox{" ); 646 } 647 648 /** 649 * {@inheritDoc} 650 */ 651 public void definedTerm_() 652 { 653 markup( "}] " ); 654 } 655 656 /** 657 * {@inheritDoc} 658 */ 659 public void definitionListItem() 660 { 661 definitionListItem( null ); 662 } 663 664 /** {@inheritDoc} */ 665 public void definitionListItem( SinkEventAttributes attributes ) 666 { 667 // nop 668 } 669 670 /** 671 * {@inheritDoc} 672 */ 673 public void definitionListItem_() 674 { 675 // nop 676 } 677 678 /** 679 * {@inheritDoc} 680 */ 681 public void definition() 682 { 683 definition( null ); 684 } 685 686 /** {@inheritDoc} */ 687 public void definition( SinkEventAttributes attributes ) 688 { 689 // nop 690 } 691 692 /** 693 * {@inheritDoc} 694 */ 695 public void definition_() 696 { 697 // nop 698 } 699 700 // ---------------------------------------------------------------------- 701 // Figure 702 // ---------------------------------------------------------------------- 703 704 /** 705 * {@inheritDoc} 706 */ 707 public void figure() 708 { 709 figure( null ); 710 } 711 712 /** {@inheritDoc} */ 713 public void figure( SinkEventAttributes attributes ) 714 { 715 figureFlag = true; 716 markup( EOL + "\\begin{figure}[htb]" + EOL ); 717 } 718 719 /** 720 * {@inheritDoc} 721 */ 722 public void figure_() 723 { 724 markup( "\\end{figure}" + EOL ); 725 figureFlag = false; 726 } 727 728 /** {@inheritDoc} */ 729 public void figureGraphics( String name ) 730 { 731 figureGraphics( name, null ); 732 } 733 734 /** {@inheritDoc} */ 735 public void figureGraphics( String src, SinkEventAttributes attributes ) 736 { 737 if ( !src.toLowerCase( Locale.ENGLISH ).endsWith( ".eps" ) ) 738 { 739 getLog().warn( "[Latex Sink] Found non-eps figure graphics!" ); 740 } 741 742 markup( "\\begin{center}" + EOL ); 743 markup( "\\includegraphics{" + src + "}" + EOL ); 744 markup( "\\end{center}" + EOL ); 745 } 746 747 /** 748 * {@inheritDoc} 749 */ 750 public void figureCaption() 751 { 752 figureCaption( null ); 753 } 754 755 /** {@inheritDoc} */ 756 public void figureCaption( SinkEventAttributes attributes ) 757 { 758 markup( "\\caption{" ); 759 } 760 761 /** 762 * {@inheritDoc} 763 */ 764 public void figureCaption_() 765 { 766 markup( "}" + EOL ); 767 } 768 769 // ---------------------------------------------------------------------- 770 // Table 771 // ---------------------------------------------------------------------- 772 773 /** 774 * {@inheritDoc} 775 */ 776 public void table() 777 { 778 table( null ); 779 } 780 781 /** {@inheritDoc} */ 782 public void table( SinkEventAttributes attributes ) 783 { 784 tableFlag = true; 785 markup( EOL + "\\begin{table}[htp]" + EOL ); 786 } 787 788 /** 789 * {@inheritDoc} 790 */ 791 public void table_() 792 { 793 markup( "\\end{table}" + EOL ); 794 tableFlag = false; 795 } 796 797 /** {@inheritDoc} */ 798 public void tableRows( int[] justification, boolean grid ) 799 800 { 801 StringBuilder justif = new StringBuilder(); 802 for ( int i1 : justification ) 803 { 804 if ( grid ) 805 { 806 justif.append( '|' ); 807 } 808 switch ( i1 ) 809 { 810 case Sink.JUSTIFY_CENTER: 811 justif.append( 'c' ); 812 break; 813 case Sink.JUSTIFY_LEFT: 814 justif.append( 'l' ); 815 break; 816 case Sink.JUSTIFY_RIGHT: 817 justif.append( 'r' ); 818 break; 819 default: 820 break; 821 } 822 } 823 if ( grid ) 824 { 825 justif.append( '|' ); 826 } 827 828 markup( "\\begin{center}" + EOL ); 829 markup( "\\begin{tabular}{" + justif.toString() + "}" + EOL ); 830 if ( grid ) 831 { 832 markup( "\\hline" + EOL ); 833 } 834 gridFlag = grid; 835 cellJustif = justification; 836 } 837 838 /** 839 * {@inheritDoc} 840 */ 841 public void tableRows_() 842 { 843 markup( "\\end{tabular}" + EOL ); 844 markup( "\\end{center}" + EOL ); 845 846 gridFlag = false; 847 cellJustif = null; 848 } 849 850 /** 851 * {@inheritDoc} 852 */ 853 public void tableRow() 854 { 855 tableRow( null ); 856 } 857 858 /** {@inheritDoc} */ 859 public void tableRow( SinkEventAttributes attributes ) 860 { 861 cellCount = 0; 862 } 863 864 /** 865 * {@inheritDoc} 866 */ 867 public void tableRow_() 868 { 869 markup( "\\\\" + EOL ); 870 if ( gridFlag || lastCellWasHeader ) 871 { 872 markup( "\\hline" + EOL ); 873 } 874 cellCount = 0; 875 lastCellWasHeader = false; 876 } 877 878 /** 879 * {@inheritDoc} 880 */ 881 public void tableCell() 882 { 883 tableCell( (SinkEventAttributes) null ); 884 } 885 886 /** {@inheritDoc} */ 887 public void tableCell( String width ) 888 { 889 SinkEventAttributeSet att = new SinkEventAttributeSet(); 890 att.addAttribute( javax.swing.text.html.HTML.Attribute.WIDTH, width ); 891 892 tableCell( att ); 893 } 894 895 /** {@inheritDoc} */ 896 public void tableCell( SinkEventAttributes attributes ) 897 { 898 tableCell( false ); 899 } 900 901 /** 902 * {@inheritDoc} 903 */ 904 public void tableCell_() 905 { 906 markup( "\\end{tabular}" ); 907 ++cellCount; 908 } 909 910 /** 911 * {@inheritDoc} 912 */ 913 public void tableHeaderCell() 914 { 915 tableCell( (SinkEventAttributes) null ); 916 } 917 918 /** {@inheritDoc} */ 919 public void tableHeaderCell( String width ) 920 { 921 SinkEventAttributeSet att = new SinkEventAttributeSet(); 922 att.addAttribute( javax.swing.text.html.HTML.Attribute.WIDTH, width ); 923 924 tableHeaderCell( att ); 925 } 926 927 /** {@inheritDoc} */ 928 public void tableHeaderCell( SinkEventAttributes attributes ) 929 { 930 tableCell( true ); 931 } 932 933 /** 934 * {@inheritDoc} 935 */ 936 public void tableHeaderCell_() 937 { 938 tableCell_(); 939 } 940 941 private boolean lastCellWasHeader = false; 942 943 /** 944 * Starts a table cell. 945 * 946 * @param header True if this is a header cell. 947 */ 948 private void tableCell( boolean header ) 949 { 950 lastCellWasHeader = header; 951 952 if ( cellCount > 0 ) 953 { 954 markup( " &" + EOL ); 955 } 956 957 char justif; 958 switch ( cellJustif[cellCount] ) 959 { 960 case Sink.JUSTIFY_LEFT: 961 justif = 'l'; 962 break; 963 case Sink.JUSTIFY_RIGHT: 964 justif = 'r'; 965 break; 966 case Sink.JUSTIFY_CENTER: 967 default: 968 justif = 'c'; 969 break; 970 } 971 markup( "\\begin{tabular}[t]{" + justif + "}" ); 972 } 973 974 /** 975 * {@inheritDoc} 976 */ 977 public void tableCaption() 978 { 979 tableCaption( null ); 980 } 981 982 /** {@inheritDoc} */ 983 public void tableCaption( SinkEventAttributes attributes ) 984 { 985 markup( "\\caption{" ); 986 } 987 988 /** 989 * {@inheritDoc} 990 */ 991 public void tableCaption_() 992 { 993 markup( "}" + EOL ); 994 } 995 996 /** 997 * {@inheritDoc} 998 */ 999 public void paragraph() 1000 { 1001 paragraph( null ); 1002 } 1003 1004 /** {@inheritDoc} */ 1005 public void paragraph( SinkEventAttributes attributes ) 1006 { 1007 markup( EOL + EOL ); 1008 } 1009 1010 /** 1011 * {@inheritDoc} 1012 */ 1013 public void paragraph_() 1014 { 1015 markup( EOL ); 1016 } 1017 1018 /** {@inheritDoc} */ 1019 public void verbatim( boolean boxed ) 1020 { 1021 verbatim( boxed ? SinkEventAttributeSet.BOXED : null ); 1022 } 1023 1024 /** 1025 * {@inheritDoc} 1026 * 1027 * @param attributes a {@link org.apache.maven.doxia.sink.SinkEventAttributes} object. 1028 */ 1029 public void verbatim( SinkEventAttributes attributes ) 1030 { 1031 boolean boxed = false; 1032 1033 if ( attributes != null && attributes.isDefined( SinkEventAttributes.DECORATION ) ) 1034 { 1035 boxed = "boxed".equals( 1036 attributes.getAttribute( SinkEventAttributes.DECORATION ) ); 1037 } 1038 1039 markup( EOL + "\\begin{small}" + EOL ); 1040 1041 if ( boxed ) 1042 { 1043 markup( "\\begin{Verbatim}[frame=single]" + EOL ); 1044 } 1045 else 1046 { 1047 markup( "\\begin{Verbatim}" + EOL ); 1048 } 1049 1050 verbatimFlag = true; 1051 } 1052 1053 /** 1054 * {@inheritDoc} 1055 */ 1056 public void verbatim_() 1057 { 1058 markup( EOL + "\\end{Verbatim}" + EOL ); 1059 markup( "\\end{small}" + EOL ); 1060 1061 verbatimFlag = false; 1062 } 1063 1064 /** 1065 * {@inheritDoc} 1066 */ 1067 public void horizontalRule() 1068 { 1069 horizontalRule( null ); 1070 } 1071 1072 /** {@inheritDoc} */ 1073 public void horizontalRule( SinkEventAttributes attributes ) 1074 { 1075 markup( EOL + "\\begin{center}\\rule[0.5ex]{\\linewidth}{1pt}\\end{center}" + EOL ); 1076 } 1077 1078 /** 1079 * {@inheritDoc} 1080 */ 1081 public void pageBreak() 1082 { 1083 markup( EOL + "\\newpage" + EOL ); 1084 } 1085 1086 /** {@inheritDoc} */ 1087 public void anchor( String name ) 1088 { 1089 anchor( name, null ); 1090 } 1091 1092 /** {@inheritDoc} */ 1093 public void anchor( String name, SinkEventAttributes attributes ) 1094 { 1095 markup( "\\hypertarget{" + name + "}{" ); 1096 } 1097 1098 /** 1099 * {@inheritDoc} 1100 */ 1101 public void anchor_() 1102 { 1103 markup( "}" ); 1104 } 1105 1106 /** {@inheritDoc} */ 1107 public void link( String name ) 1108 { 1109 link( name, null ); 1110 } 1111 1112 /** {@inheritDoc} */ 1113 public void link( String name, SinkEventAttributes attributes ) 1114 { 1115 // TODO: use \\url for simple links 1116 if ( DoxiaUtils.isExternalLink( name ) ) 1117 { 1118 markup( "\\href{" + name + "}{" ); 1119 } 1120 else 1121 { 1122 markup( "\\hyperlink{" + name + "}{" ); 1123 } 1124 } 1125 1126 /** 1127 * {@inheritDoc} 1128 */ 1129 public void link_() 1130 { 1131 markup( "}" ); 1132 } 1133 1134 /** 1135 * {@inheritDoc} 1136 */ 1137 public void inline() 1138 { 1139 inline( null ); 1140 } 1141 1142 /** {@inheritDoc} */ 1143 public void inline( SinkEventAttributes attributes ) 1144 { 1145 List<String> tags = new ArrayList<>(); 1146 1147 if ( attributes != null ) 1148 { 1149 1150 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "italic" ) ) 1151 { 1152 markup( "\\textit{" ); 1153 tags.add( 0, "}" ); 1154 } 1155 1156 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "bold" ) ) 1157 { 1158 markup( "\\textbf{" ); 1159 tags.add( 0, "}" ); 1160 } 1161 1162 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "code" ) ) 1163 { 1164 markup( "\\texttt{\\small " ); 1165 tags.add( 0, "}" ); 1166 } 1167 1168 } 1169 1170 inlineStack.push( tags ); 1171 } 1172 1173 /** 1174 * {@inheritDoc} 1175 */ 1176 public void inline_() 1177 { 1178 for ( String tag: inlineStack.pop() ) 1179 { 1180 markup( tag ); 1181 } 1182 } 1183 1184 /** 1185 * {@inheritDoc} 1186 */ 1187 public void italic() 1188 { 1189 inline( SinkEventAttributeSet.Semantics.ITALIC ); 1190 } 1191 1192 /** 1193 * {@inheritDoc} 1194 */ 1195 public void italic_() 1196 { 1197 inline_(); 1198 } 1199 1200 /** 1201 * {@inheritDoc} 1202 */ 1203 public void bold() 1204 { 1205 inline( SinkEventAttributeSet.Semantics.BOLD ); 1206 } 1207 1208 /** 1209 * {@inheritDoc} 1210 */ 1211 public void bold_() 1212 { 1213 inline_(); 1214 } 1215 1216 /** 1217 * {@inheritDoc} 1218 */ 1219 public void monospaced() 1220 { 1221 inline( SinkEventAttributeSet.Semantics.CODE ); 1222 } 1223 1224 /** 1225 * {@inheritDoc} 1226 */ 1227 public void monospaced_() 1228 { 1229 inline_(); 1230 } 1231 1232 /** 1233 * {@inheritDoc} 1234 */ 1235 public void lineBreak() 1236 { 1237 lineBreak( null ); 1238 } 1239 1240 /** {@inheritDoc} */ 1241 public void lineBreak( SinkEventAttributes attributes ) 1242 { 1243 markup( ( figureFlag || tableFlag || titleFlag || verbatimFlag ) ? EOL : "\\newline" + EOL ); 1244 } 1245 1246 /** 1247 * {@inheritDoc} 1248 */ 1249 public void nonBreakingSpace() 1250 { 1251 markup( "~" ); 1252 } 1253 1254 /** {@inheritDoc} */ 1255 public void text( String text ) 1256 { 1257 text( text, null ); 1258 } 1259 1260 /** {@inheritDoc} */ 1261 public void text( String text, SinkEventAttributes attributes ) 1262 { 1263 if ( ignoreText ) 1264 { 1265 return; 1266 } 1267 if ( isTitle ) 1268 { 1269 title = text; 1270 } 1271 else if ( verbatimFlag ) 1272 { 1273 verbatimContent( text ); 1274 } 1275 else 1276 { 1277 content( text ); 1278 } 1279 } 1280 1281 /** {@inheritDoc} */ 1282 public void rawText( String text ) 1283 { 1284 verbatimContent( text ); 1285 } 1286 1287 /** {@inheritDoc} */ 1288 public void comment( String comment ) 1289 { 1290 rawText( EOL + "%" + comment ); 1291 } 1292 1293 /** 1294 * {@inheritDoc} 1295 * 1296 * Unkown events just log a warning message but are ignored otherwise. 1297 * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes) 1298 */ 1299 public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes ) 1300 { 1301 getLog().warn( "[Latex Sink] Unknown Sink event: '" + name + "', ignoring!" ); 1302 } 1303 1304 // ----------------------------------------------------------------------- 1305 1306 /** 1307 * Writes the text, preserving whitespace. 1308 * 1309 * @param text the text to write. 1310 */ 1311 protected void markup( String text ) 1312 { 1313 if ( text != null ) 1314 { 1315 out.write( text, /*preserveSpace*/ true ); 1316 } 1317 } 1318 1319 /** 1320 * Writes the text, without preserving whitespace. 1321 * 1322 * @param text the text to write. 1323 */ 1324 protected void content( String text ) 1325 { 1326 out.write( escaped( text ), /*preserveSpace*/ false ); 1327 } 1328 1329 /** 1330 * Writes the text, preserving whitespace. 1331 * 1332 * @param text the text to write. 1333 */ 1334 protected void verbatimContent( String text ) 1335 { 1336 out.write( text, /*preserveSpace*/ true ); 1337 } 1338 1339 // ----------------------------------------------------------------------- 1340 1341 /** 1342 * Escapes special characters. 1343 * 1344 * @param text The text to escape. 1345 * @return The text with special characters replaced. 1346 */ 1347 public static String escaped( String text ) 1348 { 1349 int length = text.length(); 1350 StringBuilder buffer = new StringBuilder( length ); 1351 1352 for ( int i = 0; i < length; ++i ) 1353 { 1354 char c = text.charAt( i ); 1355 switch ( c ) 1356 { 1357 case '-': 1358 case '<': 1359 case '>': 1360 buffer.append( "\\symbol{" ).append( (int) c ).append( "}" ); 1361 break; 1362 case '~': 1363 buffer.append( "\\textasciitilde " ); 1364 break; 1365 case '^': 1366 buffer.append( "\\textasciicircum " ); 1367 break; 1368 case '|': 1369 buffer.append( "\\textbar " ); 1370 break; 1371 case '\\': 1372 buffer.append( "\\textbackslash " ); 1373 break; 1374 case '$': 1375 buffer.append( "\\$" ); 1376 break; 1377 case '&': 1378 buffer.append( "\\&" ); 1379 break; 1380 case '%': 1381 buffer.append( "\\%" ); 1382 break; 1383 case '#': 1384 buffer.append( "\\#" ); 1385 break; 1386 case '{': 1387 buffer.append( "\\{" ); 1388 break; 1389 case '}': 1390 buffer.append( "\\}" ); 1391 break; 1392 case '_': 1393 buffer.append( "\\_" ); 1394 break; 1395 default: 1396 buffer.append( c ); 1397 } 1398 } 1399 1400 return buffer.toString(); 1401 } 1402 1403 // ---------------------------------------------------------------------- 1404 // 1405 // ---------------------------------------------------------------------- 1406 1407 /** 1408 * {@inheritDoc} 1409 */ 1410 public void flush() 1411 { 1412 out.flush(); 1413 } 1414 1415 /** 1416 * {@inheritDoc} 1417 */ 1418 public void close() 1419 { 1420 out.close(); 1421 1422 init(); 1423 } 1424 1425 // ---------------------------------------------------------------------- 1426 // 1427 // ---------------------------------------------------------------------- 1428 1429 /** 1430 * Returns the default sink commands from a resource. 1431 * 1432 * @throws java.io.IOException if the resource file cannot be read. 1433 * @return InputStream 1434 */ 1435 private static InputStream getDefaultSinkCommands() 1436 throws IOException 1437 { 1438 return LatexSink.class.getResource( "default_sink_commands.tex" ).openStream(); 1439 } 1440 1441 /** 1442 * Returns the default preamble from a resource. 1443 * 1444 * @return InputStream 1445 * @throws java.io.IOException if the resource file cannot be read. 1446 */ 1447 private static InputStream getDefaultPreamble() 1448 throws IOException 1449 { 1450 return LatexSink.class.getResource( "default_preamble.tex" ).openStream(); 1451 } 1452 1453 /** 1454 * Returns the default sink commands. 1455 * 1456 * @return String. 1457 */ 1458 protected String defaultSinkCommands() 1459 { 1460 try 1461 { 1462 return IOUtil.toString( getDefaultSinkCommands() ); 1463 } 1464 catch ( IOException ioe ) 1465 { 1466 // this should not happen 1467 getLog().warn( "Could not read default LaTeX commands, the generated LaTeX file will not compile!" ); 1468 getLog().debug( ioe ); 1469 1470 return ""; 1471 } 1472 } 1473 1474 /** 1475 * Returns the default preamble. 1476 * 1477 * @return String. 1478 */ 1479 protected String defaultPreamble() 1480 { 1481 try 1482 { 1483 return IOUtil.toString( getDefaultPreamble() ); 1484 } 1485 catch ( IOException ioe ) 1486 { 1487 // this should not happen 1488 getLog().warn( "Could not read default LaTeX preamble, the generated LaTeX file will not compile!" ); 1489 getLog().debug( ioe ); 1490 1491 return ""; 1492 } 1493 } 1494 1495 /** 1496 * {@inheritDoc} 1497 */ 1498 protected void init() 1499 { 1500 super.init(); 1501 1502 this.ignoreText = false; 1503 this.titleFlag = false; 1504 this.numberedListNesting = 0; 1505 this.verbatimFlag = false; 1506 this.figureFlag = false; 1507 this.tableFlag = false; 1508 this.gridFlag = false; 1509 this.cellJustif = null; 1510 this.cellCount = 0; 1511 this.isTitle = false; 1512 this.title = null; 1513 } 1514}