View Javadoc

1   package org.apache.maven.doxia.module.itext;
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 com.lowagie.text.BadElementException;
23  import com.lowagie.text.ElementTags;
24  import com.lowagie.text.Image;
25  
26  import java.awt.Color;
27  import java.io.File;
28  import java.io.IOException;
29  import java.io.LineNumberReader;
30  import java.io.StringReader;
31  import java.io.StringWriter;
32  import java.io.Writer;
33  import java.net.MalformedURLException;
34  import java.net.URL;
35  import java.util.HashMap;
36  import java.util.Locale;
37  import java.util.Map;
38  import java.util.Set;
39  import java.util.TreeSet;
40  
41  import org.apache.maven.doxia.sink.AbstractXmlSink;
42  import org.apache.maven.doxia.sink.Sink;
43  import org.apache.maven.doxia.sink.SinkEventAttributes;
44  import org.apache.maven.doxia.util.DoxiaUtils;
45  import org.apache.maven.doxia.util.HtmlTools;
46  
47  import org.codehaus.plexus.util.IOUtil;
48  import org.codehaus.plexus.util.StringUtils;
49  import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
50  import org.codehaus.plexus.util.xml.XMLWriter;
51  
52  /**
53   * <p>A doxia Sink which produces an XML Front End document for <code>iText</code> framework.</p>
54   * Known limitations:
55   * <ul>
56   * <li>Roman lists are not supported.</li>
57   * <li>Horizontal rule is not supported with 1.3.
58   * See <a href="http://www.mail-archive.com/itext-questions@lists.sourceforge.net/msg10323.html">
59   * http://www.mail-archive.com/itext-questions@lists.sourceforge.net/msg10323.html</a></li>
60   * <li>iText has some problems with <code>ElementTags.TABLE</code> and <code>ElementTags.TABLEFITSPAGE</code>.
61   * See http://sourceforge.net/tracker/index.php?func=detail&aid=786427&group_id=15255&atid=115255.</li>
62   * <li>Images could be on another page and next text on the last one.</li>
63   * </ul>
64   *
65   * @see <a href="http://www.lowagie.com/iText/tutorial/ch07.html">http://www.lowagie.com/iText/tutorial/ch07.html</a>
66   *
67   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
68   * @version $Id: ITextSink.java 1090706 2011-04-09 23:15:28Z hboutemy $
69   */
70  public class ITextSink
71      extends AbstractXmlSink
72  {
73      /** This is the place where the iText DTD is located. IMPORTANT: this DTD is not uptodate! */
74      public static final String DTD = "http://itext.sourceforge.net/itext.dtd";
75  
76      /** This is the reference to the DTD. */
77      public static final String DOCTYPE = "ITEXT SYSTEM \"" + DTD + "\"";
78  
79      /** This is the default leading for chapter title */
80      public static final String DEFAULT_CHAPTER_TITLE_LEADING = "36.0";
81  
82      /** This is the default leading for section title */
83      public static final String DEFAULT_SECTION_TITLE_LEADING = "24.0";
84  
85      /** The ClassLoader used */
86      private ClassLoader currentClassLoader;
87  
88      /** The action context */
89      private SinkActionContext actionContext;
90  
91      /** The Writer used */
92      private Writer writer;
93  
94      /** The XML Writer used */
95      private final XMLWriter xmlWriter;
96  
97      private boolean writeStart;
98  
99      /** The Header object */
100     private ITextHeader header;
101 
102     /** The font object */
103     private ITextFont font;
104 
105     private int numberDepth = 1;
106 
107     private int depth = 0;
108 
109     private StringWriter tableCaptionWriter = null;
110 
111     private XMLWriter tableCaptionXMLWriter = null;
112 
113     /** Flag to know if an anchor is defined or not. Used as workaround for iText which needs a defined local
114      * destination. */
115     private boolean anchorDefined = false;
116 
117     /** Flag to know if an figure event is called. */
118     private boolean figureDefined = false;
119 
120     /** Map of warn messages with a String as key to describe the error type and a Set as value.
121      * Using to reduce warn messages. */
122     private Map<String, Set<String>> warnMessages;
123 
124     /**
125      * <p>Constructor for ITextSink.</p>
126      *
127      * @param writer the writer.
128      */
129     protected ITextSink( Writer writer )
130     {
131         this( writer, "UTF-8" );
132     }
133 
134     /**
135      * <p>Constructor for ITextSink.</p>
136      *
137      * @param writer the writer.
138      * @param encoding the encoding.
139      * @since 1.1
140      */
141     protected ITextSink( Writer writer, String encoding )
142     {
143         // No doctype since itext doctype is not up to date!
144         this( new PrettyPrintXMLWriter( writer, encoding, null ) );
145 
146         this.writer = writer;
147         this.writeStart = true;
148     }
149 
150     /**
151      * <p>Constructor for ITextSink.</p>
152      *
153      * @param xmlWriter a pretty-printing xml writer.
154      */
155     protected ITextSink( PrettyPrintXMLWriter xmlWriter )
156     {
157         this.xmlWriter = xmlWriter;
158 
159         this.writeStart = false;
160 
161         init();
162     }
163 
164     /**
165      * Get the current classLoader
166      *
167      * @return the current class loader
168      */
169     public ClassLoader getClassLoader()
170     {
171         return currentClassLoader;
172     }
173 
174     /**
175      * Set a new class loader
176      *
177      * @param cl the class loader.
178      */
179     public void setClassLoader( ClassLoader cl )
180     {
181         currentClassLoader = cl;
182     }
183 
184     // ----------------------------------------------------------------------
185     // Document
186     // ----------------------------------------------------------------------
187 
188     /** {@inheritDoc} */
189     public void close()
190     {
191         IOUtil.close( writer );
192 
193         init();
194     }
195 
196     /** {@inheritDoc} */
197     public void flush()
198     {
199         if ( getLog().isWarnEnabled() && this.warnMessages != null )
200         {
201             for ( Map.Entry<String, Set<String>> entry : this.warnMessages.entrySet() )
202             {
203                 for ( String msg : entry.getValue() )
204                 {
205                     getLog().warn( msg );
206                 }
207             }
208         }
209 
210         this.warnMessages = null;
211     }
212 
213     // ----------------------------------------------------------------------
214     // Header
215     // ----------------------------------------------------------------------
216 
217     /** {@inheritDoc} */
218     public void head_()
219     {
220         actionContext.release();
221     }
222 
223     /** {@inheritDoc} */
224     public void head()
225     {
226         //init(); // why? this causes DOXIA-413
227 
228         actionContext.setAction( SinkActionContext.HEAD );
229     }
230 
231     /** {@inheritDoc} */
232     public void author_()
233     {
234         actionContext.release();
235     }
236 
237     /** {@inheritDoc} */
238     public void author()
239     {
240         actionContext.setAction( SinkActionContext.AUTHOR );
241     }
242 
243     /** {@inheritDoc} */
244     public void date_()
245     {
246         actionContext.release();
247     }
248 
249     /** {@inheritDoc} */
250     public void date()
251     {
252         actionContext.setAction( SinkActionContext.DATE );
253     }
254 
255     /** {@inheritDoc} */
256     public void title_()
257     {
258         actionContext.release();
259     }
260 
261     /** {@inheritDoc} */
262     public void title()
263     {
264         actionContext.setAction( SinkActionContext.TITLE );
265     }
266 
267     // ----------------------------------------------------------------------
268     // Body
269     // ----------------------------------------------------------------------
270 
271     /** {@inheritDoc} */
272     public void body_()
273     {
274         if ( writeStart )
275         {
276             writeEndElement(); // ElementTags.CHAPTER
277 
278             writeEndElement(); // ElementTags.ITEXT
279         }
280 
281         actionContext.release();
282     }
283 
284     /** {@inheritDoc} */
285     public void body()
286     {
287         if ( writeStart )
288         {
289             writeStartElement( ElementTags.ITEXT );
290             writeAddAttribute( ElementTags.TITLE, header.getTitle() );
291             writeAddAttribute( ElementTags.AUTHOR, header.getAuthors() );
292             writeAddAttribute( ElementTags.CREATIONDATE, header.getDate() );
293             writeAddAttribute( ElementTags.SUBJECT, header.getTitle() );
294             writeAddAttribute( ElementTags.KEYWORDS, "" );
295             writeAddAttribute( ElementTags.PRODUCER, "Generated with Doxia by " + System.getProperty( "user.name" ) );
296             writeAddAttribute( ElementTags.PAGE_SIZE, ITextUtil.getPageSize( ITextUtil.getDefaultPageSize() ) );
297 
298             writeStartElement( ElementTags.CHAPTER );
299             writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
300             writeAddAttribute( ElementTags.DEPTH, depth );
301             writeAddAttribute( ElementTags.INDENT, "0.0" );
302 
303             writeStartElement( ElementTags.TITLE );
304             writeAddAttribute( ElementTags.LEADING, DEFAULT_CHAPTER_TITLE_LEADING );
305             writeAddAttribute( ElementTags.FONT, ITextFont.DEFAULT_FONT_NAME );
306             writeAddAttribute( ElementTags.SIZE, ITextFont.getSectionFontSize( 0 ) );
307             writeAddAttribute( ElementTags.STYLE, ITextFont.BOLD );
308             writeAddAttribute( ElementTags.BLUE, ITextFont.DEFAULT_FONT_COLOR_BLUE );
309             writeAddAttribute( ElementTags.GREEN, ITextFont.DEFAULT_FONT_COLOR_GREEN );
310             writeAddAttribute( ElementTags.RED, ITextFont.DEFAULT_FONT_COLOR_RED );
311             writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER );
312 
313 //            startChunk( ITextFont.DEFAULT_FONT_NAME, ITextFont.getSectionFontSize( 0 ),
314 //                    ITextFont.BOLD, ITextFont.DEFAULT_FONT_COLOR_BLUE, ITextFont.DEFAULT_FONT_COLOR_GREEN,
315 //                    ITextFont.DEFAULT_FONT_COLOR_RED, "top" );
316 
317             writeStartElement( ElementTags.CHUNK );
318             writeAddAttribute( ElementTags.FONT, ITextFont.DEFAULT_FONT_NAME );
319             writeAddAttribute( ElementTags.SIZE, ITextFont.getSectionFontSize( 0 ) );
320             writeAddAttribute( ElementTags.STYLE, ITextFont.BOLD );
321             writeAddAttribute( ElementTags.BLUE, ITextFont.DEFAULT_FONT_COLOR_BLUE );
322             writeAddAttribute( ElementTags.GREEN, ITextFont.DEFAULT_FONT_COLOR_GREEN );
323             writeAddAttribute( ElementTags.RED, ITextFont.DEFAULT_FONT_COLOR_RED );
324 //            writeAddAttribute( ElementTags.LOCALDESTINATION, "top" );
325 
326             write( header.getTitle() );
327 
328             writeEndElement(); // ElementTags.CHUNK
329 
330             writeEndElement(); // ElementTags.TITLE
331         }
332 
333         actionContext.setAction( SinkActionContext.BODY );
334     }
335 
336     // ----------------------------------------------------------------------
337     // Sections
338     // ----------------------------------------------------------------------
339 
340     /** {@inheritDoc} */
341     public void sectionTitle()
342     {
343         actionContext.release();
344     }
345 
346     /** {@inheritDoc} */
347     public void sectionTitle_()
348     {
349         actionContext.setAction( SinkActionContext.SECTION_TITLE );
350     }
351 
352     /** {@inheritDoc} */
353     public void section1_()
354     {
355         writeEndElement(); // ElementTags.SECTION
356 
357         numberDepth--;
358         depth = 0;
359 
360         actionContext.release();
361     }
362 
363     /** {@inheritDoc} */
364     public void section1()
365     {
366         numberDepth++;
367         depth = 1;
368 
369         writeStartElement( ElementTags.SECTION );
370         writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
371         writeAddAttribute( ElementTags.DEPTH, depth );
372         writeAddAttribute( ElementTags.INDENT, "0.0" );
373 
374         lineBreak();
375 
376         actionContext.setAction( SinkActionContext.SECTION_1 );
377     }
378 
379     /** {@inheritDoc} */
380     public void sectionTitle1_()
381     {
382         writeEndElement(); // ElementTags.TITLE
383 
384         font.setSize( ITextFont.DEFAULT_FONT_SIZE );
385         bold_();
386 
387         actionContext.release();
388     }
389 
390     /** {@inheritDoc} */
391     public void sectionTitle1()
392     {
393         font.setSize( ITextFont.getSectionFontSize( 1 ) );
394         font.setColor( Color.BLACK );
395         bold();
396 
397         writeStartElement( ElementTags.TITLE );
398         writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
399         writeAddAttribute( ElementTags.FONT, font.getFontName() );
400         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
401         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
402         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
403         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
404         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
405 //        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
406 
407         actionContext.setAction( SinkActionContext.SECTION_TITLE_1 );
408     }
409 
410     /** {@inheritDoc} */
411     public void section2_()
412     {
413         writeEndElement(); // ElementTags.SECTION
414 
415         numberDepth--;
416         depth = 0;
417 
418         actionContext.release();
419     }
420 
421     /** {@inheritDoc} */
422     public void section2()
423     {
424         numberDepth++;
425         depth = 1;
426 
427         writeStartElement( ElementTags.SECTION );
428         writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
429         writeAddAttribute( ElementTags.DEPTH, depth );
430         writeAddAttribute( ElementTags.INDENT, "0.0" );
431 
432         lineBreak();
433 
434         actionContext.setAction( SinkActionContext.SECTION_2 );
435     }
436 
437     /** {@inheritDoc} */
438     public void sectionTitle2_()
439     {
440         writeEndElement(); // ElementTags.TITLE
441 
442         font.setSize( ITextFont.DEFAULT_FONT_SIZE );
443         bold_();
444 
445         actionContext.release();
446     }
447 
448     /** {@inheritDoc} */
449     public void sectionTitle2()
450     {
451         font.setSize( ITextFont.getSectionFontSize( 2 ) );
452         font.setColor( Color.BLACK );
453         bold();
454 
455         writeStartElement( ElementTags.TITLE );
456         writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
457         writeAddAttribute( ElementTags.FONT, font.getFontName() );
458         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
459         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
460         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
461         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
462         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
463 //        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
464 
465         actionContext.setAction( SinkActionContext.SECTION_TITLE_2 );
466     }
467 
468     /** {@inheritDoc} */
469     public void section3_()
470     {
471         writeEndElement(); // ElementTags.SECTION
472 
473         numberDepth--;
474         depth = 1;
475 
476         actionContext.release();
477     }
478 
479     /** {@inheritDoc} */
480     public void section3()
481     {
482         numberDepth++;
483         depth = 1;
484 
485         writeStartElement( ElementTags.SECTION );
486         writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
487         writeAddAttribute( ElementTags.DEPTH, depth );
488         writeAddAttribute( ElementTags.INDENT, "0.0" );
489 
490         lineBreak();
491 
492         actionContext.setAction( SinkActionContext.SECTION_3 );
493     }
494 
495     /** {@inheritDoc} */
496     public void sectionTitle3_()
497     {
498         writeEndElement(); // ElementTags.TITLE
499 
500         font.setSize( ITextFont.DEFAULT_FONT_SIZE );
501         bold_();
502 
503         actionContext.release();
504     }
505 
506     /** {@inheritDoc} */
507     public void sectionTitle3()
508     {
509         font.setSize( ITextFont.getSectionFontSize( 3 ) );
510         font.setColor( Color.BLACK );
511         bold();
512 
513         writeStartElement( ElementTags.TITLE );
514         writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
515         writeAddAttribute( ElementTags.FONT, font.getFontName() );
516         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
517         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
518         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
519         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
520         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
521 //        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
522 
523         actionContext.setAction( SinkActionContext.SECTION_TITLE_3 );
524     }
525 
526     /** {@inheritDoc} */
527     public void section4_()
528     {
529         writeEndElement(); // ElementTags.SECTION
530 
531         numberDepth--;
532         depth = 1;
533 
534         actionContext.release();
535     }
536 
537     /** {@inheritDoc} */
538     public void section4()
539     {
540         numberDepth++;
541         depth = 1;
542 
543         writeStartElement( ElementTags.SECTION );
544         writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
545         writeAddAttribute( ElementTags.DEPTH, depth );
546         writeAddAttribute( ElementTags.INDENT, "0.0" );
547 
548         lineBreak();
549 
550         actionContext.setAction( SinkActionContext.SECTION_4 );
551     }
552 
553     /** {@inheritDoc} */
554     public void sectionTitle4_()
555     {
556         writeEndElement(); // ElementTags.TITLE
557 
558         font.setSize( ITextFont.DEFAULT_FONT_SIZE );
559         bold_();
560 
561         actionContext.release();
562     }
563 
564     /** {@inheritDoc} */
565     public void sectionTitle4()
566     {
567         font.setSize( ITextFont.getSectionFontSize( 4 ) );
568         font.setColor( Color.BLACK );
569         bold();
570 
571         writeStartElement( ElementTags.TITLE );
572         writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
573         writeAddAttribute( ElementTags.FONT, font.getFontName() );
574         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
575         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
576         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
577         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
578         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
579 //        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
580 
581         actionContext.setAction( SinkActionContext.SECTION_TITLE_4 );
582     }
583 
584     /** {@inheritDoc} */
585     public void section5_()
586     {
587         writeEndElement(); // ElementTags.SECTION
588 
589         numberDepth--;
590         depth = 1;
591 
592         actionContext.release();
593     }
594 
595     /** {@inheritDoc} */
596     public void section5()
597     {
598         numberDepth++;
599         depth = 1;
600 
601         writeStartElement( ElementTags.SECTION );
602         writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
603         writeAddAttribute( ElementTags.DEPTH, depth );
604         writeAddAttribute( ElementTags.INDENT, "0.0" );
605 
606         lineBreak();
607 
608         actionContext.setAction( SinkActionContext.SECTION_5 );
609     }
610 
611     /** {@inheritDoc} */
612     public void sectionTitle5_()
613     {
614         writeEndElement(); // ElementTags.TITLE
615 
616         font.setSize( ITextFont.DEFAULT_FONT_SIZE );
617         bold_();
618 
619         actionContext.release();
620     }
621 
622     /** {@inheritDoc} */
623     public void sectionTitle5()
624     {
625         font.setSize( ITextFont.getSectionFontSize( 5 ) );
626         font.setColor( Color.BLACK );
627         bold();
628 
629         writeStartElement( ElementTags.TITLE );
630         writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
631         writeAddAttribute( ElementTags.FONT, font.getFontName() );
632         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
633         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
634         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
635         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
636         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
637 //        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
638 
639         actionContext.setAction( SinkActionContext.SECTION_TITLE_5 );
640     }
641 
642     // ----------------------------------------------------------------------
643     // Paragraph
644     // ----------------------------------------------------------------------
645 
646     /** {@inheritDoc} */
647     public void paragraph_()
648     {
649         // Special case
650         if ( ( actionContext.getCurrentAction() == SinkActionContext.LIST_ITEM )
651             || ( actionContext.getCurrentAction() == SinkActionContext.NUMBERED_LIST_ITEM )
652             || ( actionContext.getCurrentAction() == SinkActionContext.DEFINITION ) )
653         {
654             return;
655         }
656 
657         writeEndElement(); // ElementTags.PARAGRAPH
658 
659         actionContext.release();
660     }
661 
662     /** {@inheritDoc} */
663     public void paragraph()
664     {
665         // Special case
666         if ( ( actionContext.getCurrentAction() == SinkActionContext.LIST_ITEM )
667             || ( actionContext.getCurrentAction() == SinkActionContext.NUMBERED_LIST_ITEM )
668             || ( actionContext.getCurrentAction() == SinkActionContext.DEFINITION ) )
669         {
670             return;
671         }
672 
673         writeStartElement( ElementTags.PARAGRAPH );
674         writeStartElement( ElementTags.NEWLINE );
675         writeEndElement();
676 
677         actionContext.setAction( SinkActionContext.PARAGRAPH );
678     }
679 
680     // ----------------------------------------------------------------------
681     // Lists
682     // ----------------------------------------------------------------------
683 
684     /** {@inheritDoc} */
685     public void list_()
686     {
687         writeEndElement(); // ElementTags.LIST
688 
689         writeEndElement(); // ElementTags.CHUNK
690 
691         actionContext.release();
692     }
693 
694     /** {@inheritDoc} */
695     public void list()
696     {
697         writeStartElement( ElementTags.CHUNK );
698         writeAddAttribute( ElementTags.FONT, font.getFontName() );
699         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
700         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
701         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
702         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
703         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
704 
705         writeStartElement( ElementTags.LIST );
706         writeAddAttribute( ElementTags.NUMBERED, Boolean.FALSE.toString() );
707         writeAddAttribute( ElementTags.SYMBOLINDENT, "15" );
708 
709         actionContext.setAction( SinkActionContext.LIST );
710     }
711 
712     /** {@inheritDoc} */
713     public void listItem_()
714     {
715         writeEndElement(); // ElementTags.LISTITEM
716 
717         actionContext.release();
718     }
719 
720     /** {@inheritDoc} */
721     public void listItem()
722     {
723         writeStartElement( ElementTags.LISTITEM );
724         writeAddAttribute( ElementTags.INDENTATIONLEFT, "20.0" );
725 
726         actionContext.setAction( SinkActionContext.LIST_ITEM );
727     }
728 
729     /** {@inheritDoc} */
730     public void numberedList_()
731     {
732         writeEndElement(); // ElementTags.LIST
733 
734         writeEndElement(); // ElementTags.CHUNK
735 
736         actionContext.release();
737     }
738 
739     /** {@inheritDoc} */
740     public void numberedList( int numbering )
741     {
742         writeStartElement( ElementTags.CHUNK );
743         writeAddAttribute( ElementTags.FONT, font.getFontName() );
744         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
745         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
746         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
747         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
748         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
749 
750         writeStartElement( ElementTags.LIST );
751         writeAddAttribute( ElementTags.NUMBERED, Boolean.TRUE.toString() );
752         writeAddAttribute( ElementTags.SYMBOLINDENT, "20" );
753 
754         switch ( numbering )
755         {
756             case Sink.NUMBERING_UPPER_ALPHA:
757                 writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() );
758                 writeAddAttribute( ElementTags.FIRST, 'A' );
759                 break;
760 
761             case Sink.NUMBERING_LOWER_ALPHA:
762                 writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() );
763                 writeAddAttribute( ElementTags.FIRST, 'a' );
764                 break;
765 
766             // TODO Doesn't work
767             case Sink.NUMBERING_UPPER_ROMAN:
768                 writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() );
769                 writeAddAttribute( ElementTags.FIRST, 'I' );
770                 break;
771 
772             case Sink.NUMBERING_LOWER_ROMAN:
773                 writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() );
774                 writeAddAttribute( ElementTags.FIRST, 'i' );
775                 break;
776 
777             case Sink.NUMBERING_DECIMAL:
778             default:
779                 writeAddAttribute( ElementTags.LETTERED, Boolean.FALSE.toString() );
780         }
781 
782         actionContext.setAction( SinkActionContext.NUMBERED_LIST );
783     }
784 
785     /** {@inheritDoc} */
786     public void numberedListItem_()
787     {
788         writeEndElement(); // ElementTags.LISTITEM
789 
790         actionContext.release();
791     }
792 
793     /** {@inheritDoc} */
794     public void numberedListItem()
795     {
796         writeStartElement( ElementTags.LISTITEM );
797         writeAddAttribute( ElementTags.INDENTATIONLEFT, "20" );
798 
799         actionContext.setAction( SinkActionContext.NUMBERED_LIST_ITEM );
800     }
801 
802     /** {@inheritDoc} */
803     public void definitionList_()
804     {
805         actionContext.release();
806     }
807 
808     /** {@inheritDoc} */
809     public void definitionList()
810     {
811         lineBreak();
812 
813         actionContext.setAction( SinkActionContext.DEFINITION_LIST );
814     }
815 
816     /** {@inheritDoc} */
817     public void definedTerm_()
818     {
819         font.setSize( ITextFont.DEFAULT_FONT_SIZE );
820         bold_();
821 
822         writeEndElement(); // ElementTags.CHUNK
823 
824         actionContext.release();
825 
826         lineBreak();
827     }
828 
829     /** {@inheritDoc} */
830     public void definedTerm()
831     {
832         font.setSize( ITextFont.DEFAULT_FONT_SIZE + 2 );
833         bold();
834 
835         writeStartElement( ElementTags.CHUNK );
836         writeAddAttribute( ElementTags.FONT, font.getFontName() );
837         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
838         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
839         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
840         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
841         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
842 
843         actionContext.setAction( SinkActionContext.DEFINED_TERM );
844     }
845 
846     /** {@inheritDoc} */
847     public void definition_()
848     {
849         writeEndElement(); // ElementTags.CHUNK
850 
851         actionContext.release();
852 
853         lineBreak();
854     }
855 
856     /** {@inheritDoc} */
857     public void definition()
858     {
859         writeStartElement( ElementTags.CHUNK );
860         writeAddAttribute( ElementTags.FONT, font.getFontName() );
861         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
862         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
863         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
864         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
865         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
866 
867 
868         writeStartElement( ElementTags.CHUNK );
869         writeAddAttribute( ElementTags.FONT, font.getFontName() );
870         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
871         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
872         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
873         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
874         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
875 
876         // We need to add a non break space first to display empty string
877         write( "\u00A0" + StringUtils.repeat( " ", 16 ), false, false );
878 
879         writeEndElement(); // ElementTags.CHUNK
880 
881         actionContext.setAction( SinkActionContext.DEFINITION );
882     }
883 
884     /** {@inheritDoc} */
885     public void definitionListItem_()
886     {
887         actionContext.release();
888     }
889 
890     /** {@inheritDoc} */
891     public void definitionListItem()
892     {
893         actionContext.setAction( SinkActionContext.DEFINITION_LIST_ITEM );
894     }
895 
896     // ----------------------------------------------------------------------
897     //  Tables
898     // ----------------------------------------------------------------------
899 
900     /** {@inheritDoc} */
901     public void table_()
902     {
903         if ( tableCaptionXMLWriter != null )
904         {
905             tableCaptionXMLWriter = null;
906 
907             writeEndElement(); // ElementTags.TABLE
908 
909             writeEndElement(); // ElementTags.CHUNK
910 
911             writeStartElement( ElementTags.PARAGRAPH );
912             writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER );
913 
914             write( tableCaptionWriter.toString(), true );
915 
916             writeEndElement(); // ElementTags.PARAGRAPH
917 
918             tableCaptionWriter = null;
919         }
920         else
921         {
922             writeEndElement(); // ElementTags.TABLE
923 
924             writeEndElement(); // ElementTags.CHUNK
925         }
926         actionContext.release();
927     }
928 
929     /** {@inheritDoc} */
930     public void table()
931     {
932         writeStartElement( ElementTags.CHUNK );
933         writeAddAttribute( ElementTags.FONT, font.getFontName() );
934         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
935         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
936         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
937         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
938         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
939 
940         writeStartElement( ElementTags.TABLE );
941         writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
942         writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
943         writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
944         writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
945         writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER );
946         writeAddAttribute( ElementTags.WIDTH, "100.0%" );
947         writeAddAttribute( ElementTags.TABLEFITSPAGE, Boolean.TRUE.toString() );
948         writeAddAttribute( ElementTags.CELLSFITPAGE, Boolean.TRUE.toString() );
949         writeAddAttribute( ElementTags.CELLPADDING, "10" );
950         //writeAddAttribute( ElementTags.COLUMNS, "2" );
951 
952         actionContext.setAction( SinkActionContext.TABLE );
953     }
954 
955     /** {@inheritDoc} */
956     public void tableCaption_()
957     {
958         actionContext.release();
959     }
960 
961     /** {@inheritDoc} */
962     public void tableCaption()
963     {
964         tableCaptionWriter = new StringWriter();
965         tableCaptionXMLWriter = new PrettyPrintXMLWriter( tableCaptionWriter );
966         actionContext.setAction( SinkActionContext.TABLE_CAPTION );
967     }
968 
969     /** {@inheritDoc} */
970     public void tableCell_()
971     {
972         writeEndElement(); // ElementTags.CELL
973 
974         actionContext.release();
975     }
976 
977     /** {@inheritDoc} */
978     public void tableCell()
979     {
980         writeStartElement( ElementTags.CELL );
981         writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
982         writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
983         writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
984         writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
985         writeAddAttribute( ElementTags.HORIZONTALALIGN, ElementTags.ALIGN_LEFT );
986 
987         actionContext.setAction( SinkActionContext.TABLE_CELL );
988     }
989 
990     /** {@inheritDoc} */
991     public void tableCell( String width )
992     {
993         actionContext.setAction( SinkActionContext.TABLE_CELL );
994     }
995 
996     /** {@inheritDoc} */
997     public void tableHeaderCell_()
998     {
999         writeEndElement(); // ElementTags.CELL
1000 
1001         actionContext.release();
1002     }
1003 
1004     /** {@inheritDoc} */
1005     public void tableHeaderCell()
1006     {
1007         writeStartElement( ElementTags.CELL );
1008         writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
1009         writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
1010         writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
1011         writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
1012         writeAddAttribute( ElementTags.HEADER, Boolean.TRUE.toString() );
1013         writeAddAttribute( ElementTags.BGRED, Color.GRAY.getRed() );
1014         writeAddAttribute( ElementTags.BGBLUE, Color.GRAY.getBlue() );
1015         writeAddAttribute( ElementTags.BGGREEN, Color.GRAY.getGreen() );
1016         writeAddAttribute( ElementTags.HORIZONTALALIGN, ElementTags.ALIGN_CENTER );
1017 
1018         actionContext.setAction( SinkActionContext.TABLE_HEADER_CELL );
1019     }
1020 
1021     /** {@inheritDoc} */
1022     public void tableHeaderCell( String width )
1023     {
1024         actionContext.setAction( SinkActionContext.TABLE_HEADER_CELL );
1025     }
1026 
1027     /** {@inheritDoc} */
1028     public void tableRow_()
1029     {
1030         writeEndElement(); // ElementTags.ROW
1031 
1032         actionContext.release();
1033     }
1034 
1035     /** {@inheritDoc} */
1036     public void tableRow()
1037     {
1038         writeStartElement( ElementTags.ROW );
1039 
1040         actionContext.setAction( SinkActionContext.TABLE_ROW );
1041     }
1042 
1043     /** {@inheritDoc} */
1044     public void tableRows_()
1045     {
1046         //writeEndElement(); // ElementTags.TABLE
1047 
1048         actionContext.release();
1049     }
1050 
1051     /** {@inheritDoc} */
1052     public void tableRows( int[] justification, boolean grid )
1053     {
1054         // ElementTags.TABLE
1055         writeAddAttribute( ElementTags.COLUMNS, justification.length );
1056 
1057         actionContext.setAction( SinkActionContext.TABLE_ROWS );
1058     }
1059 
1060     // ----------------------------------------------------------------------
1061     // Verbatim
1062     // ----------------------------------------------------------------------
1063 
1064     /** {@inheritDoc} */
1065     public void verbatim_()
1066     {
1067         writeEndElement(); // ElementTags.CELL
1068 
1069         writeEndElement(); // ElementTags.ROW
1070 
1071         writeEndElement(); // ElementTags.TABLE
1072 
1073         writeEndElement(); // ElementTags.CHUNK
1074 
1075         actionContext.release();
1076     }
1077 
1078     /** {@inheritDoc} */
1079     public void verbatim( boolean boxed )
1080     {
1081         // Always boxed
1082         writeStartElement( ElementTags.CHUNK );
1083         writeAddAttribute( ElementTags.FONT, font.getFontName() );
1084         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
1085         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
1086         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
1087         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
1088         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
1089 
1090         writeStartElement( ElementTags.TABLE );
1091         writeAddAttribute( ElementTags.COLUMNS, "1" );
1092         writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
1093         writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
1094         writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
1095         writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
1096         writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER );
1097         writeAddAttribute( ElementTags.TABLEFITSPAGE, Boolean.TRUE.toString() );
1098         writeAddAttribute( ElementTags.CELLSFITPAGE, Boolean.TRUE.toString() );
1099         writeAddAttribute( ElementTags.CELLPADDING, "10" );
1100         writeAddAttribute( ElementTags.WIDTH, "100.0%" );
1101 
1102         writeStartElement( ElementTags.ROW );
1103 
1104         writeStartElement( ElementTags.CELL );
1105         writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
1106         writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
1107         writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
1108         writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
1109 
1110         actionContext.setAction( SinkActionContext.VERBATIM );
1111     }
1112 
1113     // ----------------------------------------------------------------------
1114     // Figures
1115     // ----------------------------------------------------------------------
1116 
1117     /** {@inheritDoc} */
1118     public void figure_()
1119     {
1120         writeEndElement(); // ElementTags.IMAGE
1121 
1122         writeEndElement(); // ElementTags.CHUNK
1123 
1124         actionContext.release();
1125 
1126         figureDefined = false;
1127     }
1128 
1129     /** {@inheritDoc} */
1130     public void figure()
1131     {
1132         figureDefined = true;
1133 
1134         writeStartElement( ElementTags.CHUNK );
1135         writeAddAttribute( ElementTags.FONT, font.getFontName() );
1136         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
1137         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
1138         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
1139         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
1140         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
1141 
1142         writeStartElement( ElementTags.IMAGE );
1143 
1144         actionContext.setAction( SinkActionContext.FIGURE );
1145     }
1146 
1147     /** {@inheritDoc} */
1148     public void figureCaption_()
1149     {
1150         actionContext.release();
1151     }
1152 
1153     /** {@inheritDoc} */
1154     public void figureCaption()
1155     {
1156         actionContext.setAction( SinkActionContext.FIGURE_CAPTION );
1157     }
1158 
1159     /**
1160      * If the <code>name</code> is a relative link, the internal link will used a System property <code>itext.basedir</code>,
1161      * or the class loader.
1162      * {@inheritDoc} */
1163     public void figureGraphics( String name )
1164     {
1165         String urlName = null;
1166         File nameFile = null;
1167         if ( ( name.toLowerCase( Locale.ENGLISH ).startsWith( "http://" ) )
1168             || ( name.toLowerCase( Locale.ENGLISH ).startsWith( "https://" ) ) )
1169         {
1170             urlName = name;
1171         }
1172         else
1173         {
1174             if ( System.getProperty( "itext.basedir" ) != null )
1175             {
1176                 try
1177                 {
1178                     nameFile = new File( System.getProperty( "itext.basedir" ), name );
1179                     urlName = nameFile.toURI().toURL().toString();
1180                 }
1181                 catch ( MalformedURLException e )
1182                 {
1183                     getLog().error( "MalformedURLException: " + e.getMessage(), e );
1184                 }
1185             }
1186             else
1187             {
1188                 if ( getClassLoader() != null )
1189                 {
1190                     if ( getClassLoader().getResource( name ) != null )
1191                     {
1192                         urlName = getClassLoader().getResource( name ).toString();
1193                     }
1194                 }
1195                 else
1196                 {
1197                     if ( ITextSink.class.getClassLoader().getResource( name ) != null )
1198                     {
1199                         urlName = ITextSink.class.getClassLoader().getResource( name ).toString();
1200                     }
1201                 }
1202             }
1203         }
1204 
1205         if ( urlName == null )
1206         {
1207             String msg =
1208                 "No image '" + name
1209                     + "' found in the class loader. Try to call setClassLoader(ClassLoader) before.";
1210             logMessage( "imageNotFound", msg );
1211 
1212             return;
1213         }
1214 
1215         if ( nameFile != null && !nameFile.exists() )
1216         {
1217             String msg = "No image '" + nameFile + "' found in your system, check the path.";
1218             logMessage( "imageNotFound", msg );
1219 
1220             return;
1221         }
1222 
1223         boolean figureCalled = figureDefined;
1224         if ( !figureCalled )
1225         {
1226             figure();
1227         }
1228 
1229         float width = 0;
1230         float height = 0;
1231         try
1232         {
1233             Image image = Image.getInstance( new URL( urlName ) );
1234             image.scaleToFit( ITextUtil.getDefaultPageSize().width() / 2, ITextUtil.getDefaultPageSize().height() / 2 );
1235             width = image.plainWidth();
1236             height = image.plainHeight();
1237         }
1238         catch ( BadElementException e )
1239         {
1240             getLog().error( "BadElementException: " + e.getMessage(), e );
1241         }
1242         catch ( MalformedURLException e )
1243         {
1244             getLog().error( "MalformedURLException: " + e.getMessage(), e );
1245         }
1246         catch ( IOException e )
1247         {
1248             getLog().error( "IOException: " + e.getMessage(), e );
1249         }
1250 
1251         writeAddAttribute( ElementTags.URL, urlName );
1252         writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_MIDDLE );
1253         writeAddAttribute( ElementTags.PLAINWIDTH, String.valueOf( width ) );
1254         writeAddAttribute( ElementTags.PLAINHEIGHT, String.valueOf( height ) );
1255 
1256         actionContext.setAction( SinkActionContext.FIGURE_GRAPHICS );
1257 
1258         if ( !figureCalled )
1259         {
1260             figure_();
1261         }
1262     }
1263 
1264     // ----------------------------------------------------------------------
1265     // Fonts
1266     // ----------------------------------------------------------------------
1267 
1268     /** {@inheritDoc} */
1269     public void bold_()
1270     {
1271         font.removeBold();
1272     }
1273 
1274     /** {@inheritDoc} */
1275     public void bold()
1276     {
1277         font.addBold();
1278     }
1279 
1280     /** {@inheritDoc} */
1281     public void italic_()
1282     {
1283         font.removeItalic();
1284     }
1285 
1286     /** {@inheritDoc} */
1287     public void italic()
1288     {
1289         font.addItalic();
1290     }
1291 
1292     /** {@inheritDoc} */
1293     public void monospaced_()
1294     {
1295         font.setMonoSpaced( false );
1296     }
1297 
1298     /** {@inheritDoc} */
1299     public void monospaced()
1300     {
1301         font.setMonoSpaced( true );
1302     }
1303 
1304     // ----------------------------------------------------------------------
1305     // Links
1306     // ----------------------------------------------------------------------
1307 
1308     /** {@inheritDoc} */
1309     public void link_()
1310     {
1311         writeEndElement(); // ElementTags.ANCHOR
1312 
1313         font.setColor( Color.BLACK );
1314         font.removeUnderlined();
1315 
1316         actionContext.release();
1317     }
1318 
1319     /** {@inheritDoc} */
1320     public void link( String name )
1321     {
1322         if ( name == null )
1323         {
1324             throw new NullPointerException( "Link name cannot be null!" );
1325         }
1326 
1327         font.setColor( Color.BLUE );
1328         font.addUnderlined();
1329 
1330         writeStartElement( ElementTags.ANCHOR );
1331         if ( StringUtils.isNotEmpty( name )&& name.startsWith( "#" ) && StringUtils.isNotEmpty( header.getTitle() ))
1332         {
1333             name = "#" + DoxiaUtils.encodeId( header.getTitle(), true ) + "_" + name.substring( 1 );
1334         }
1335         writeAddAttribute( ElementTags.REFERENCE, HtmlTools.escapeHTML( name ) );
1336         writeAddAttribute( ElementTags.FONT, font.getFontName() );
1337         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
1338         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
1339         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
1340         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
1341         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
1342 
1343         actionContext.setAction( SinkActionContext.LINK );
1344     }
1345 
1346     /** {@inheritDoc} */
1347     public void anchor_()
1348     {
1349         if ( !anchorDefined )
1350         {
1351             // itext needs a defined local destination, we put an invisible text
1352             writeAddAttribute( ElementTags.BLUE, "255" );
1353             writeAddAttribute( ElementTags.GREEN, "255" );
1354             writeAddAttribute( ElementTags.RED, "255" );
1355 
1356             write( "_" );
1357         }
1358 
1359         anchorDefined = false;
1360 
1361         writeEndElement(); // ElementTags.ANCHOR
1362 
1363         actionContext.release();
1364     }
1365 
1366     /** {@inheritDoc} */
1367     public void anchor( String name )
1368     {
1369         if ( name == null )
1370         {
1371             throw new NullPointerException( "Anchor name cannot be null!" );
1372         }
1373 
1374         if ( StringUtils.isNotEmpty( header.getTitle() ) )
1375         {
1376             name = header.getTitle() + "_" + name;
1377         }
1378         String id = name;
1379 
1380         if ( !DoxiaUtils.isValidId( id ) )
1381         {
1382             id = DoxiaUtils.encodeId( name, true );
1383 
1384             String msg = "Modified invalid link: '" + name + "' to '" + id + "'";
1385             logMessage( "modifiedLink", msg );
1386         }
1387 
1388         writeStartElement( ElementTags.ANCHOR );
1389         writeAddAttribute( ElementTags.NAME, id );
1390         writeAddAttribute( ElementTags.FONT, font.getFontName() );
1391         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
1392         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
1393 
1394         actionContext.setAction( SinkActionContext.ANCHOR );
1395     }
1396 
1397     // ----------------------------------------------------------------------
1398     // Misc
1399     // ----------------------------------------------------------------------
1400 
1401     /** {@inheritDoc} */
1402     public void lineBreak()
1403     {
1404         // Special case for the header
1405         if ( ( actionContext.getCurrentAction() == SinkActionContext.AUTHOR )
1406             || ( actionContext.getCurrentAction() == SinkActionContext.DATE )
1407             || ( actionContext.getCurrentAction() == SinkActionContext.TITLE ) )
1408         {
1409             return;
1410         }
1411 
1412         writeStartElement( ElementTags.NEWLINE );
1413         writeEndElement();
1414     }
1415 
1416     /** {@inheritDoc} */
1417     public void nonBreakingSpace()
1418     {
1419         write( " " );
1420     }
1421 
1422     /** {@inheritDoc} */
1423     public void pageBreak()
1424     {
1425         writeStartElement( ElementTags.NEWPAGE );
1426         writeEndElement();
1427     }
1428 
1429     /** {@inheritDoc} */
1430     public void horizontalRule()
1431     {
1432         writeStartElement( ElementTags.PARAGRAPH );
1433         writeAddAttribute( ElementTags.BLUE, "255" );
1434         writeAddAttribute( ElementTags.GREEN, "255" );
1435         writeAddAttribute( ElementTags.RED, "255" );
1436         write( "_" );
1437         writeEndElement();
1438 
1439         writeStartElement( ElementTags.PARAGRAPH );
1440         writeStartElement( ElementTags.HORIZONTALRULE );
1441         writeEndElement();
1442         writeEndElement();
1443     }
1444 
1445     // ----------------------------------------------------------------------
1446     // Text
1447     // ----------------------------------------------------------------------
1448 
1449     /** {@inheritDoc} */
1450     public void rawText( String text )
1451     {
1452         writeStartElement( ElementTags.CHUNK );
1453         writeAddAttribute( ElementTags.FONT, font.getFontName() );
1454         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
1455         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
1456         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
1457         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
1458         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
1459 
1460         write( text, false );
1461 
1462         writeEndElement(); // ElementTags.CHUNK
1463     }
1464 
1465     /** {@inheritDoc} */
1466     public void text( String text )
1467     {
1468         if ( StringUtils.isEmpty( text ) )
1469         {
1470             return;
1471         }
1472 
1473         switch ( actionContext.getCurrentAction() )
1474         {
1475             case SinkActionContext.AUTHOR:
1476                 header.addAuthor( text );
1477                 break;
1478 
1479             case SinkActionContext.DATE:
1480                 header.setDate( text );
1481                 break;
1482 
1483             case SinkActionContext.TITLE:
1484                 header.setTitle( text );
1485                 break;
1486 
1487             case SinkActionContext.TABLE_CAPTION:
1488                 this.tableCaptionXMLWriter.writeText( text );
1489                 break;
1490 
1491             case SinkActionContext.VERBATIM:
1492                 // Used to preserve indentation and formating
1493                 LineNumberReader lnr = new LineNumberReader( new StringReader( text ) );
1494                 String line;
1495                 try
1496                 {
1497                     while ( ( line = lnr.readLine() ) != null )
1498                     {
1499                         writeStartElement( ElementTags.CHUNK );
1500                         writeAddAttribute( ElementTags.FONT, font.getFontName() );
1501                         writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
1502                         writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
1503                         writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
1504                         writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
1505                         writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
1506 
1507                         write( "<![CDATA[", true );
1508                         // Special case
1509                         line = StringUtils.replace( line, "<![CDATA[", "< ![CDATA[" );
1510                         line = StringUtils.replace( line, "]]>", "]] >" );
1511                         write( line, true, false );
1512                         write( "]]>", true );
1513 
1514                         writeEndElement();
1515                         lineBreak();
1516                     }
1517                 }
1518                 catch ( IOException e )
1519                 {
1520                     throw new RuntimeException( "IOException: ", e );
1521                 }
1522                 break;
1523 
1524             case SinkActionContext.FIGURE_CAPTION:
1525                 writeAddAttribute( ElementTags.ALT, text );
1526                 break;
1527 
1528             case SinkActionContext.SECTION_TITLE:
1529             case SinkActionContext.SECTION_1:
1530             case SinkActionContext.SECTION_2:
1531             case SinkActionContext.SECTION_3:
1532             case SinkActionContext.SECTION_4:
1533             case SinkActionContext.SECTION_5:
1534             case SinkActionContext.FIGURE:
1535             case SinkActionContext.FIGURE_GRAPHICS:
1536             case SinkActionContext.TABLE_ROW:
1537             case SinkActionContext.TABLE:
1538             case SinkActionContext.HEAD:
1539             case SinkActionContext.UNDEFINED:
1540                 break;
1541 
1542             case SinkActionContext.ANCHOR:
1543                 anchorDefined = true;
1544             case SinkActionContext.PARAGRAPH:
1545             case SinkActionContext.LINK:
1546             case SinkActionContext.TABLE_CELL:
1547             case SinkActionContext.TABLE_HEADER_CELL:
1548             case SinkActionContext.DEFINITION:
1549             case SinkActionContext.DEFINED_TERM:
1550             case SinkActionContext.NUMBERED_LIST_ITEM:
1551             case SinkActionContext.LIST_ITEM:
1552             case SinkActionContext.SECTION_TITLE_5:
1553             case SinkActionContext.SECTION_TITLE_4:
1554             case SinkActionContext.SECTION_TITLE_3:
1555             case SinkActionContext.SECTION_TITLE_2:
1556             case SinkActionContext.SECTION_TITLE_1:
1557             default:
1558                 writeStartElement( ElementTags.CHUNK );
1559                 writeAddAttribute( ElementTags.FONT, font.getFontName() );
1560                 writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
1561                 writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
1562                 writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
1563                 writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
1564                 writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
1565 
1566                 write( text );
1567 
1568                 writeEndElement(); // ElementTags.CHUNK
1569         }
1570     }
1571 
1572     /**
1573      * {@inheritDoc}
1574      *
1575      * Unkown events just log a warning message but are ignored otherwise.
1576      * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
1577      */
1578     public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
1579     {
1580         String msg = "Unknown Sink event: '" + name + "', ignoring!";
1581         logMessage( "unknownEvent", msg );
1582     }
1583 
1584     /** {@inheritDoc} */
1585     protected void init()
1586     {
1587         super.init();
1588 
1589         this.actionContext = new SinkActionContext();
1590         this.font = new ITextFont();
1591         this.header = new ITextHeader();
1592 
1593         this.numberDepth = 1;
1594         this.depth = 0;
1595         this.tableCaptionWriter = null;
1596         this.tableCaptionXMLWriter = null;
1597         this.anchorDefined = false;
1598         this.figureDefined = false;
1599         this.warnMessages = null;
1600     }
1601 
1602     /**
1603      * Convenience method to write a starting element.
1604      *
1605      * @param tag the name of the tag
1606      */
1607     private void writeStartElement( String tag )
1608     {
1609         if ( tableCaptionXMLWriter == null )
1610         {
1611             xmlWriter.startElement( tag );
1612         }
1613         else
1614         {
1615             tableCaptionXMLWriter.startElement( tag );
1616         }
1617     }
1618 
1619     /**
1620      * Convenience method to write a key-value pair.
1621      *
1622      * @param key the name of an attribute
1623      * @param value the value of an attribute
1624      */
1625     private void writeAddAttribute( String key, String value )
1626     {
1627         if ( tableCaptionXMLWriter == null )
1628         {
1629             xmlWriter.addAttribute( key, value );
1630         }
1631         else
1632         {
1633             tableCaptionXMLWriter.addAttribute( key, value );
1634         }
1635     }
1636 
1637     /**
1638      * Convenience method to write a key-value pair.
1639      *
1640      * @param key the name of an attribute
1641      * @param value the value of an attribute
1642      */
1643     private void writeAddAttribute( String key, int value )
1644     {
1645         if ( tableCaptionXMLWriter == null )
1646         {
1647             xmlWriter.addAttribute( key, String.valueOf( value ) );
1648         }
1649         else
1650         {
1651             tableCaptionXMLWriter.addAttribute( key, String.valueOf( value ) );
1652         }
1653     }
1654 
1655     /**
1656      * Convenience method to write an end element.
1657      */
1658     private void writeEndElement()
1659     {
1660         if ( tableCaptionXMLWriter == null )
1661         {
1662             xmlWriter.endElement();
1663         }
1664         else
1665         {
1666             tableCaptionXMLWriter.endElement();
1667         }
1668     }
1669 
1670     /**
1671      * Convenience method to write a String
1672      *
1673      * @param aString
1674      */
1675     protected void write( String aString )
1676     {
1677         write( aString, false );
1678     }
1679 
1680     /**
1681      * Convenience method to write a String depending the escapeHtml flag
1682      *
1683      * @param aString
1684      * @param escapeHtml
1685      */
1686     private void write( String aString, boolean escapeHtml )
1687     {
1688         write( aString, escapeHtml, true );
1689     }
1690 
1691     /**
1692      * Convenience method to write a String depending the escapeHtml flag
1693      *
1694      * @param aString
1695      * @param escapeHtml
1696      * @param trim
1697      */
1698     private void write( String aString, boolean escapeHtml, boolean trim )
1699     {
1700         if ( aString == null )
1701         {
1702             return;
1703         }
1704 
1705         if ( trim )
1706         {
1707             aString = StringUtils.replace( aString, "\n", "" );
1708 
1709             LineNumberReader lnr = new LineNumberReader( new StringReader( aString ) );
1710             StringBuffer sb = new StringBuffer();
1711             String line;
1712             try
1713             {
1714                 while ( ( line = lnr.readLine() ) != null )
1715                 {
1716                     sb.append( beautifyPhrase( line.trim() ) );
1717                     sb.append( " " );
1718                 }
1719 
1720                 aString = sb.toString();
1721             }
1722             catch ( IOException e )
1723             {
1724                 // nop
1725             }
1726             if ( aString.trim().length() == 0 )
1727             {
1728                 return;
1729             }
1730         }
1731         if ( escapeHtml )
1732         {
1733             if ( tableCaptionXMLWriter == null )
1734             {
1735                 xmlWriter.writeMarkup( aString );
1736             }
1737             else
1738             {
1739                 tableCaptionXMLWriter.writeMarkup( aString );
1740             }
1741         }
1742         else
1743         {
1744             if ( tableCaptionXMLWriter == null )
1745             {
1746                 xmlWriter.writeText( aString );
1747             }
1748             else
1749             {
1750                 tableCaptionXMLWriter.writeText( aString );
1751             }
1752         }
1753     }
1754 
1755     /**
1756      * Convenience method to return a beautify phrase, i.e. one space between words.
1757      *
1758      * @param aString
1759      * @return a String with only one space between words
1760      */
1761     private static String beautifyPhrase( String aString )
1762     {
1763         String[] strings = StringUtils.split( aString, " " );
1764         StringBuffer sb = new StringBuffer();
1765         for ( int i = 0; i < strings.length; i++ )
1766         {
1767             if ( strings[i].trim().length() != 0 )
1768             {
1769                 sb.append( strings[i].trim() );
1770                 sb.append( " " );
1771             }
1772         }
1773 
1774         return sb.toString().trim();
1775     }
1776 
1777     private void startChunk( String fontName, int fontSize, String fontStyle, int fontColorBlue, int fontColorGreen,
1778                              int fontColorRed, String localDestination )
1779     {
1780         writeStartElement( ElementTags.CHUNK );
1781         writeAddAttribute( ElementTags.FONT, fontName );
1782         writeAddAttribute( ElementTags.SIZE, fontSize );
1783         writeAddAttribute( ElementTags.STYLE, fontStyle );
1784         writeAddAttribute( ElementTags.BLUE, fontColorBlue );
1785         writeAddAttribute( ElementTags.GREEN, fontColorGreen );
1786         writeAddAttribute( ElementTags.RED, fontColorRed );
1787 //        writeAddAttribute( ElementTags.LOCALDESTINATION, localDestination );
1788     }
1789 
1790     /**
1791      * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
1792      *
1793      * @param key not null
1794      * @param msg not null
1795      * @see #close()
1796      * @since 1.1.1
1797      */
1798     private void logMessage( String key, String msg )
1799     {
1800         msg = "[iText Sink] " + msg;
1801         if ( getLog().isDebugEnabled() )
1802         {
1803             getLog().debug( msg );
1804 
1805             return;
1806         }
1807 
1808         if ( warnMessages == null )
1809         {
1810             warnMessages = new HashMap<String, Set<String>>();
1811         }
1812 
1813         Set<String> set = warnMessages.get( key );
1814         if ( set == null )
1815         {
1816             set = new TreeSet<String>();
1817         }
1818         set.add( msg );
1819         warnMessages.put( key, set );
1820     }
1821 }