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