001package org.apache.maven.doxia.module.latex;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.doxia.sink.AbstractTextSink;
023import org.apache.maven.doxia.sink.Sink;
024import org.apache.maven.doxia.sink.SinkEventAttributes;
025import org.apache.maven.doxia.sink.SinkEventAttributeSet;
026import org.apache.maven.doxia.util.DoxiaUtils;
027import org.apache.maven.doxia.util.LineBreaker;
028
029import org.codehaus.plexus.util.IOUtil;
030import org.codehaus.plexus.util.StringUtils;
031
032import java.io.IOException;
033import java.io.InputStream;
034import java.io.Writer;
035import java.util.Locale;
036
037/**
038 * Latex Sink implementation.
039 * <br/>
040 * <b>Note</b>: The encoding used is UTF-8.
041 *
042 * @version $Id: LatexSink.html 905940 2014-04-12 16:27:29Z hboutemy $
043 * @since 1.0
044 */
045public class LatexSink
046    extends AbstractTextSink
047{
048    /**
049     * Flag that indicates if the document to be written is only a fragment.
050     *
051     * This implies that <code>\\begin{document}</code>, <code>\\title{..}</code> will not be output.
052     */
053    private final boolean fragmentDocument;
054
055    private boolean ignoreText;
056
057    private final LineBreaker out;
058
059    private final String sinkCommands;
060
061    private final String preamble;
062
063    private boolean titleFlag;
064
065    private int numberedListNesting;
066
067    private boolean verbatimFlag;
068
069    private boolean figureFlag;
070
071    private boolean tableFlag;
072
073    private boolean gridFlag;
074
075    private int[] cellJustif;
076
077    private int cellCount;
078
079    private boolean isTitle;
080
081    private String title;
082
083    // ----------------------------------------------------------------------
084    //
085    // ----------------------------------------------------------------------
086
087    /**
088     * Constructor, initialize the Writer and the variables.
089     *
090     * @param out not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
091     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
092     */
093    protected LatexSink( Writer out )
094    {
095        this( out, null, null );
096    }
097
098    /**
099     * 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        StringBuilder justif = new StringBuilder();
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        StringBuilder buffer = new StringBuilder( 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}