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