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   * @since 1.0
46   */
47  public class LatexSink
48      extends AbstractTextSink
49  {
50      /**
51       * Flag that indicates if the document to be written is only a fragment.
52       *
53       * This implies that <code>\\begin{document}</code>, <code>\\title{..}</code> will not be output.
54       */
55      private final boolean fragmentDocument;
56  
57      private boolean ignoreText;
58  
59      private final LineBreaker out;
60  
61      private final String sinkCommands;
62  
63      private final String preamble;
64  
65      private boolean titleFlag;
66  
67      private int numberedListNesting;
68  
69      private boolean verbatimFlag;
70  
71      private boolean figureFlag;
72  
73      private boolean tableFlag;
74  
75      private boolean gridFlag;
76  
77      private int[] cellJustif;
78  
79      private int cellCount;
80  
81      private boolean isTitle;
82  
83      private String title;
84  
85      /** Keep track of the closing tags for inline events. */
86      protected Stack<List<String>> inlineStack = new Stack<>();
87  
88      // ----------------------------------------------------------------------
89      //
90      // ----------------------------------------------------------------------
91  
92      /**
93       * Constructor, initialize the Writer and the variables.
94       *
95       * @param out not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
96       * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
97       */
98      protected LatexSink( Writer out )
99      {
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 }