[doxia] 05/15: Imported Upstream version 1.1.4

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Thu Aug 6 09:41:08 UTC 2015


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to annotated tag debian/1.1.4-3
in repository doxia.

commit 669f69ad5a309a5bcc92b822d9b9794a6a5fbebb
Author: Emmanuel Bourg <ebourg at apache.org>
Date:   Sat Feb 21 23:36:06 2015 +0100

    Imported Upstream version 1.1.4
---
 doxia-book/pom.xml                                 |  151 +
 .../org/apache/maven/doxia/book/BookDoxia.java     |   79 +
 .../maven/doxia/book/BookDoxiaException.java       |   55 +
 .../apache/maven/doxia/book/DefaultBookDoxia.java  |  155 +
 .../doxia/book/InvalidBookDescriptorException.java |   61 +
 .../maven/doxia/book/context/BookContext.java      |  258 ++
 .../apache/maven/doxia/book/context/BookIndex.java |   40 +
 .../doxia/book/services/indexer/BookIndexer.java   |   46 +
 .../book/services/indexer/DefaultBookIndexer.java  |  152 +
 .../maven/doxia/book/services/io/BookIo.java       |   57 +
 .../doxia/book/services/io/DefaultBookIo.java      |  151 +
 .../renderer/AbstractITextBookRenderer.java        |  343 +++
 .../doxia/book/services/renderer/BookRenderer.java |   44 +
 .../services/renderer/DocbookBookRenderer.java     |  228 ++
 .../book/services/renderer/LatexBookRenderer.java  |  255 ++
 .../book/services/renderer/PdfBookRenderer.java    |   52 +
 .../book/services/renderer/RtfBookRenderer.java    |   52 +
 .../book/services/renderer/XHtmlBookRenderer.java  |  213 ++
 .../book/services/renderer/XdocBookRenderer.java   |  452 +++
 .../services/renderer/docbook/DocBookBookSink.java |  270 ++
 .../services/renderer/latex/LatexBookSink.java     |  124 +
 .../renderer/xdoc/AbstractXdocBookSink.java        |  126 +
 .../renderer/xdoc/ChapterXdocBookSink.java         |  151 +
 .../services/renderer/xdoc/IndexXdocBookSink.java  |   84 +
 .../renderer/xdoc/SectionXdocBookSink.java         |  166 ++
 .../services/renderer/xhtml/XhtmlBookSink.java     |  248 ++
 .../book/services/validation/BookValidator.java    |   42 +
 .../services/validation/DefaultBookValidator.java  |  108 +
 .../book/services/validation/ValidationResult.java |   81 +
 doxia-book/src/main/modello/book.mdo               |  537 ++++
 .../src/main/resources/book-renderer.properties    |   21 +
 .../src/main/resources/book-renderer_en.properties |   23 +
 .../src/main/resources/book-renderer_fr.properties |   21 +
 doxia-book/src/site/apt/index.apt                  |   20 +
 doxia-book/src/site/apt/usage.apt                  |   31 +
 doxia-book/src/site/apt/using-book-xsd.apt         |   41 +
 doxia-book/src/site/site.xml                       |   47 +
 .../apache/maven/doxia/book/BookRendererTest.java  |   75 +
 .../book/services/indexer/BookIndexerTest.java     |  118 +
 .../renderer/docbook/DocBookBookSinkTest.java      |  490 ++++
 doxia-book/src/test/resources/book-1.xml           |   51 +
 doxia-book/src/test/resources/book-1/section-1.apt |   61 +
 doxia-book/src/test/resources/book-1/section-2.apt |   16 +
 doxia-book/src/test/resources/book-1/section-3.apt |   17 +
 doxia-book/src/test/resources/book-1/section-4.apt |   16 +
 .../expected/doc-book/plexus-user-guide.xml        |  124 +
 doxia-core/pom.xml                                 |  117 +
 .../java/org/apache/maven/doxia/DefaultDoxia.java  |   72 +
 .../main/java/org/apache/maven/doxia/Doxia.java    |   66 +
 .../org/apache/maven/doxia/index/IndexEntry.java   |  312 ++
 .../org/apache/maven/doxia/index/IndexingSink.java |  309 ++
 .../apache/maven/doxia/macro/AbstractMacro.java    |  118 +
 .../org/apache/maven/doxia/macro/EchoMacro.java    |   58 +
 .../java/org/apache/maven/doxia/macro/Macro.java   |   51 +
 .../maven/doxia/macro/MacroExecutionException.java |   60 +
 .../org/apache/maven/doxia/macro/MacroRequest.java |   93 +
 .../org/apache/maven/doxia/macro/SwfMacro.java     |  182 ++
 .../doxia/macro/manager/DefaultMacroManager.java   |   53 +
 .../maven/doxia/macro/manager/MacroManager.java    |   47 +
 .../macro/manager/MacroNotFoundException.java      |   74 +
 .../maven/doxia/macro/snippet/SnippetMacro.java    |  289 ++
 .../maven/doxia/macro/snippet/SnippetReader.java   |  201 ++
 .../org/apache/maven/doxia/macro/toc/TocMacro.java |  237 ++
 .../org/apache/maven/doxia/markup/HtmlMarkup.java  |  467 +++
 .../java/org/apache/maven/doxia/markup/Markup.java |   82 +
 .../org/apache/maven/doxia/markup/TextMarkup.java  |   38 +
 .../org/apache/maven/doxia/markup/XmlMarkup.java   |   54 +
 .../doxia/module/site/AbstractSiteModule.java      |   80 +
 .../apache/maven/doxia/module/site/SiteModule.java |   54 +
 .../site/manager/DefaultSiteModuleManager.java     |   77 +
 .../module/site/manager/SiteModuleManager.java     |   55 +
 .../site/manager/SiteModuleNotFoundException.java  |   75 +
 .../apache/maven/doxia/parser/AbstractParser.java  |  184 ++
 .../maven/doxia/parser/AbstractTextParser.java     |   40 +
 .../maven/doxia/parser/AbstractXmlParser.java      |  878 ++++++
 .../apache/maven/doxia/parser/ParseException.java  |  228 ++
 .../java/org/apache/maven/doxia/parser/Parser.java |   69 +
 .../apache/maven/doxia/parser/XhtmlBaseParser.java | 1003 +++++++
 .../doxia/parser/manager/DefaultParserManager.java |   55 +
 .../maven/doxia/parser/manager/ParserManager.java  |   46 +
 .../parser/manager/ParserNotFoundException.java    |   74 +
 .../doxia/sink/AbstractBinarySinkFactory.java      |   78 +
 .../org/apache/maven/doxia/sink/AbstractSink.java  |  114 +
 .../apache/maven/doxia/sink/AbstractTextSink.java  |   36 +
 .../maven/doxia/sink/AbstractTextSinkFactory.java  |   97 +
 .../apache/maven/doxia/sink/AbstractXmlSink.java   |  203 ++
 .../maven/doxia/sink/AbstractXmlSinkFactory.java   |   47 +
 .../org/apache/maven/doxia/sink/PipelineSink.java  |   94 +
 .../org/apache/maven/doxia/sink/SinkAdapter.java   |  856 ++++++
 .../maven/doxia/sink/SinkEventAttributeSet.java    |  413 +++
 .../org/apache/maven/doxia/sink/SinkUtils.java     |  283 ++
 .../org/apache/maven/doxia/sink/XhtmlBaseSink.java | 2073 ++++++++++++++
 .../maven/doxia/sink/render/RenderingContext.java  |  194 ++
 .../maven/doxia/util/ByLineReaderSource.java       |  155 +
 .../org/apache/maven/doxia/util/ByLineSource.java  |   78 +
 .../org/apache/maven/doxia/util/DoxiaUtils.java    |  411 +++
 .../org/apache/maven/doxia/util/HtmlTools.java     |  485 ++++
 .../org/apache/maven/doxia/util/LineBreaker.java   |  216 ++
 .../org/apache/maven/doxia/util/XmlValidator.java  |  268 ++
 .../javadoc/org/apache/maven/doxia/package.html    |   60 +
 doxia-core/src/main/mdo/document.mdo               | 1268 +++++++++
 doxia-core/src/site/apt/using-document-xsd.apt     |   42 +
 doxia-core/src/site/site.xml                       |   41 +
 .../org/apache/maven/doxia/AbstractModuleTest.java |  219 ++
 .../maven/doxia/document/DocumentModelTest.java    |  371 +++
 .../apache/maven/doxia/index/IndexEntryTest.java   |   76 +
 .../apache/maven/doxia/macro/EchoMacroTest.java    |   81 +
 .../org/apache/maven/doxia/macro/SwfMacroTest.java |  109 +
 .../doxia/macro/manager/MacroManagerTest.java      |   55 +
 .../doxia/macro/snippet/SnippetMacroTest.java      |  117 +
 .../apache/maven/doxia/macro/toc/TocMacroTest.java |  179 ++
 .../maven/doxia/module/AbstractIdentityTest.java   |  166 ++
 .../maven/doxia/parser/AbstractParserTest.java     |  123 +
 .../maven/doxia/parser/AbstractParserTestCase.java |   77 +
 .../maven/doxia/parser/XhtmlBaseParserTest.java    |  675 +++++
 .../apache/maven/doxia/sink/AbstractSinkTest.java  | 1014 +++++++
 .../maven/doxia/sink/AbstractSinkTestCase.java     |  147 +
 .../maven/doxia/sink/AbstractXmlSinkTest.java      |  154 +
 .../apache/maven/doxia/sink/SinkAdapterTest.java   |  554 ++++
 .../doxia/sink/SinkEventAttributeSetTest.java      |  281 ++
 .../apache/maven/doxia/sink/SinkEventElement.java  |   86 +
 .../maven/doxia/sink/SinkEventTestingSink.java     |  823 ++++++
 .../apache/maven/doxia/sink/SinkTestDocument.java  |  604 ++++
 .../org/apache/maven/doxia/sink/SinkUtilsTest.java |   85 +
 .../java/org/apache/maven/doxia/sink/TextSink.java |  832 ++++++
 .../doxia/sink/WellformednessCheckingSink.java     |  843 ++++++
 .../apache/maven/doxia/sink/XhtmlBaseSinkTest.java |  950 +++++++
 .../doxia/sink/render/RenderingContextTest.java    |   63 +
 .../maven/doxia/util/ByLineReaderSourceTest.java   |   66 +
 .../apache/maven/doxia/util/DoxiaUtilsTest.java    |  242 ++
 .../org/apache/maven/doxia/util/HtmlToolsTest.java |  159 ++
 .../maven/doxia/xsd/AbstractXmlValidatorTest.java  |  547 ++++
 .../test/resources/macro/snippet/testSnippet.txt   |   14 +
 doxia-logging-api/pom.xml                          |   42 +
 .../java/org/apache/maven/doxia/logging/Log.java   |  196 ++
 .../org/apache/maven/doxia/logging/LogEnabled.java |   40 +
 .../maven/doxia/logging/PlexusLoggerWrapper.java   |  181 ++
 .../maven/doxia/logging/SystemStreamLog.java       |  239 ++
 doxia-logging-api/src/site/site.xml                |   34 +
 doxia-maven-plugin/pom.xml                         |  198 ++
 .../java/org/apache/maven/doxia/plugin/Book.java   |   97 +
 .../maven/doxia/plugin/DoxiaRenderBooksMojo.java   |  336 +++
 .../java/org/apache/maven/doxia/plugin/Format.java |   43 +
 doxia-maven-plugin/src/site/apt/index.apt          |   66 +
 doxia-maven-plugin/src/site/apt/usage.apt.vm       |   49 +
 doxia-maven-plugin/src/site/site.xml               |   44 +
 doxia-modules/doxia-module-apt/pom.xml             |   69 +
 .../src/main/components/components.xml             |   36 +
 .../apache/maven/doxia/module/apt/AptMarkup.java   |  162 ++
 .../maven/doxia/module/apt/AptParseException.java  |   96 +
 .../apache/maven/doxia/module/apt/AptParser.java   | 2980 ++++++++++++++++++++
 .../maven/doxia/module/apt/AptReaderSource.java    |  107 +
 .../org/apache/maven/doxia/module/apt/AptSink.java | 1020 +++++++
 .../maven/doxia/module/apt/AptSinkFactory.java     |   44 +
 .../maven/doxia/module/apt/AptSiteModule.java      |   42 +
 .../apache/maven/doxia/module/apt/AptSource.java   |   52 +
 .../apache/maven/doxia/module/apt/AptUtils.java    |  194 ++
 doxia-modules/doxia-module-apt/src/site/site.xml   |   34 +
 .../maven/doxia/module/apt/AptIdentityTest.java    |   46 +
 .../maven/doxia/module/apt/AptParserTest.java      |  588 ++++
 .../apache/maven/doxia/module/apt/AptSinkTest.java |  274 ++
 .../maven/doxia/module/apt/AptUtilsTest.java       |  162 ++
 .../doxia-module-apt/src/test/resources/test.apt   |  110 +
 .../src/test/resources/test/comments.apt           |    9 +
 .../src/test/resources/test/linebreak.apt          |   10 +
 .../src/test/resources/test/macro.apt              |    9 +
 .../src/test/resources/test/snippet.apt            |   14 +
 .../src/test/resources/test/toc.apt                |   55 +
 doxia-modules/doxia-module-confluence/pom.xml      |   59 +
 .../doxia/module/confluence/ConfluenceMarkup.java  |  100 +
 .../doxia/module/confluence/ConfluenceParser.java  |  166 ++
 .../doxia/module/confluence/ConfluenceSink.java    | 1118 ++++++++
 .../module/confluence/ConfluenceSinkFactory.java   |   44 +
 .../module/confluence/ConfluenceSiteModule.java    |   42 +
 .../confluence/parser/AbstractFatherBlock.java     |   90 +
 .../module/confluence/parser/AnchorBlock.java      |   47 +
 .../doxia/module/confluence/parser/Block.java      |   37 +
 .../module/confluence/parser/BlockParser.java      |   50 +
 .../doxia/module/confluence/parser/BoldBlock.java  |   55 +
 .../confluence/parser/ChildBlocksBuilder.java      |  322 +++
 .../confluence/parser/DefinitionListBlock.java     |   67 +
 .../parser/DefinitionListBlockParser.java          |   92 +
 .../module/confluence/parser/FigureBlock.java      |   60 +
 .../confluence/parser/FigureBlockParser.java       |  105 +
 .../confluence/parser/HorizontalRuleBlock.java     |   35 +
 .../parser/HorizontalRuleBlockParser.java          |   45 +
 .../module/confluence/parser/ItalicBlock.java      |   48 +
 .../module/confluence/parser/LinebreakBlock.java   |   36 +
 .../doxia/module/confluence/parser/LinkBlock.java  |   53 +
 .../module/confluence/parser/MonospaceBlock.java   |   47 +
 .../module/confluence/parser/ParagraphBlock.java   |   64 +
 .../confluence/parser/ParagraphBlockParser.java    |  141 +
 .../module/confluence/parser/SectionBlock.java     |   99 +
 .../confluence/parser/SectionBlockParser.java      |   75 +
 .../doxia/module/confluence/parser/TextBlock.java  |   50 +
 .../module/confluence/parser/VerbatimBlock.java    |   48 +
 .../confluence/parser/VerbatimBlockParser.java     |   66 +
 .../confluence/parser/list/BulletedListBlock.java  |   49 +
 .../module/confluence/parser/list/ListBlock.java   |   37 +
 .../confluence/parser/list/ListBlockParser.java    |  147 +
 .../confluence/parser/list/ListItemBlock.java      |   68 +
 .../confluence/parser/list/NumberedListBlock.java  |   53 +
 .../confluence/parser/list/TreeComponent.java      |  124 +
 .../confluence/parser/list/TreeListBuilder.java    |  138 +
 .../module/confluence/parser/table/TableBlock.java |   63 +
 .../confluence/parser/table/TableBlockParser.java  |  145 +
 .../confluence/parser/table/TableCellBlock.java    |   49 +
 .../parser/table/TableCellHeaderBlock.java         |   49 +
 .../confluence/parser/table/TableRowBlock.java     |   49 +
 .../doxia-module-confluence/src/site/site.xml      |   34 +
 .../module/confluence/ConfluenceParserTest.java    |  558 ++++
 .../module/confluence/ConfluenceSinkTest.java      |  288 ++
 .../src/test/resources/anchor.confluence           |    7 +
 .../src/test/resources/code.confluence             |   21 +
 .../src/test/resources/escapes.confluence          |    6 +
 .../src/test/resources/figure.confluence           |   18 +
 .../src/test/resources/linebreak.confluence        |    5 +
 .../src/test/resources/link.confluence             |    5 +
 .../src/test/resources/nested-format.confluence    |    6 +
 .../resources/nested-list-heterogenous.confluence  |    4 +
 .../src/test/resources/nested-list.confluence      |    8 +
 .../src/test/resources/note-tip-info.confluence    |   27 +
 .../src/test/resources/paragraph-figure.confluence |    6 +
 .../src/test/resources/paragraph-header.confluence |    6 +
 .../src/test/resources/paragraph-list.confluence   |    8 +
 .../src/test/resources/section.confluence          |    9 +
 .../src/test/resources/simple-list.confluence      |   14 +
 .../src/test/resources/simple-paragraph.confluence |    8 +
 .../src/test/resources/table-link.confluence       |    4 +
 .../src/test/resources/table.confluence            |   13 +
 .../src/test/resources/test.confluence             |   98 +
 .../src/test/resources/unknown-macro.confluence    |    5 +
 .../src/test/site/confluence/faq.confluence        |  228 ++
 .../src/test/site/confluence/page.confluence       |   98 +
 doxia-modules/doxia-module-docbook-simple/pom.xml  |   65 +
 .../src/main/components/components.xml             |   44 +
 .../maven/doxia/module/docbook/DocBookParser.java  | 1096 +++++++
 .../maven/doxia/module/docbook/DocBookSink.java    | 1725 +++++++++++
 .../doxia/module/docbook/DocBookSiteModule.java    |   42 +
 .../maven/doxia/module/docbook/DocbookMarkup.java  |  474 ++++
 .../doxia/module/docbook/DocbookSinkFactory.java   |   49 +
 .../maven/doxia/module/docbook/DocbookUtils.java   |  191 ++
 .../module/docbook/SimplifiedDocbookMarkup.java    | 1303 +++++++++
 .../doxia-module-docbook-simple/src/site/site.xml  |   34 +
 .../doxia/module/docbook/DocBookIdentityTest.java  |   46 +
 .../doxia/module/docbook/DocBookParserTest.java    |  407 +++
 .../doxia/module/docbook/DocBookSinkTest.java      |  259 ++
 .../doxia/module/docbook/DocbookUtilsTest.java     |  132 +
 .../src/test/resources/book.xml                    |  134 +
 .../src/test/resources/sdocbook_full.xml           |  544 ++++
 .../src/test/resources/test.xml                    |  154 +
 doxia-modules/doxia-module-fml/pom.xml             |  160 ++
 .../src/main/components/components.xml             |   36 +
 .../maven/doxia/module/fml/FmlContentParser.java   |  120 +
 .../apache/maven/doxia/module/fml/FmlMarkup.java   |  121 +
 .../apache/maven/doxia/module/fml/FmlParser.java   |  763 +++++
 .../maven/doxia/module/fml/FmlSiteModule.java      |   42 +
 .../doxia-module-fml/src/main/mdo/fml.mdo          |  153 +
 .../src/main/resources/fml-1.0.1.xsd               | 2928 +++++++++++++++++++
 .../src/site/apt/using-fml-xsd.apt                 |   43 +
 doxia-modules/doxia-module-fml/src/site/site.xml   |   40 +
 .../maven/doxia/module/fml/FmlParserTest.java      |  298 ++
 .../maven/doxia/module/fml/FmlValidatorTest.java   |   86 +
 .../doxia-module-fml/src/test/resources/macro.fml  |   49 +
 .../src/test/resources/simpleFaq.fml               |   37 +
 .../doxia-module-fml/src/test/resources/test.fml   |  769 +++++
 doxia-modules/doxia-module-fo/pom.xml              |  128 +
 .../maven/doxia/module/fo/FoAggregateSink.java     | 1272 +++++++++
 .../maven/doxia/module/fo/FoConfiguration.java     |  216 ++
 .../org/apache/maven/doxia/module/fo/FoMarkup.java |  333 +++
 .../org/apache/maven/doxia/module/fo/FoSink.java   | 1831 ++++++++++++
 .../maven/doxia/module/fo/FoSinkFactory.java       |   49 +
 .../org/apache/maven/doxia/module/fo/FoUtils.java  |  230 ++
 .../maven/doxia/module/fo/NumberedListItem.java    |  183 ++
 .../src/main/resources/doxia-fo.properties         |   19 +
 .../src/main/resources/doxia-fo_en.properties      |   23 +
 .../src/main/resources/fo-styles.xslt              |  509 ++++
 .../src/main/resources/log4j.properties            |   24 +
 doxia-modules/doxia-module-fo/src/site/site.xml    |   44 +
 .../doxia-module-fo/src/site/xdoc/index.xml        |   56 +
 .../doxia-module-fo/src/site/xdoc/links.xml        |   44 +
 .../doxia-module-fo/src/site/xdoc/usage.xml        |  202 ++
 .../maven/doxia/module/fo/FoAggregateSinkTest.java |  204 ++
 .../maven/doxia/module/fo/FoConfigurationTest.java |   70 +
 .../apache/maven/doxia/module/fo/FoSinkTest.java   |  528 ++++
 .../doxia-module-fo/src/test/resources/figure.png  |  Bin 0 -> 4545 bytes
 doxia-modules/doxia-module-itext/pom.xml           |   79 +
 .../apache/maven/doxia/module/itext/ITextFont.java |  389 +++
 .../maven/doxia/module/itext/ITextHeader.java      |  139 +
 .../apache/maven/doxia/module/itext/ITextSink.java | 1829 ++++++++++++
 .../maven/doxia/module/itext/ITextSinkFactory.java |   73 +
 .../apache/maven/doxia/module/itext/ITextUtil.java |  181 ++
 .../doxia/module/itext/SinkActionContext.java      |  159 ++
 doxia-modules/doxia-module-itext/src/site/site.xml |   34 +
 .../doxia/module/itext/ITextSinkTestCase.java      |  155 +
 .../maven/doxia/module/itext/ITextUtilTest.java    |   94 +
 .../src/test/resources/images/Context_Menu.png     |  Bin 0 -> 22300 bytes
 .../src/test/resources/images/Generate_Site.png    |  Bin 0 -> 36219 bytes
 .../src/test/resources/images/Open_Project.png     |  Bin 0 -> 12405 bytes
 .../src/test/resources/images/Project_Opened.png   |  Bin 0 -> 17655 bytes
 .../src/test/resources/images/Refresh_Project.png  |  Bin 0 -> 29710 bytes
 .../resources/images/Refreshed_Context_Menu.png    |  Bin 0 -> 22630 bytes
 .../resources/images/Run_Build_Main_Project.png    |  Bin 0 -> 32784 bytes
 .../resources/images/Run_Clean_Main_Project.png    |  Bin 0 -> 24235 bytes
 .../src/test/resources/images/Window_Files.png     |  Bin 0 -> 16790 bytes
 .../src/test/resources/images/figure.png           |  Bin 0 -> 17655 bytes
 .../src/test/resources/itext/itext.xml             | 1937 +++++++++++++
 doxia-modules/doxia-module-latex/pom.xml           |   43 +
 .../apache/maven/doxia/module/latex/LatexSink.java | 1459 ++++++++++
 .../maven/doxia/module/latex/LatexSinkFactory.java |   45 +
 .../maven/doxia/module/latex/default_preamble.tex  |   14 +
 .../doxia/module/latex/default_sink_commands.tex   |    0
 doxia-modules/doxia-module-latex/src/site/site.xml |   34 +
 .../maven/doxia/module/latex/LatexSinkTest.java    |  242 ++
 doxia-modules/doxia-module-rtf/pom.xml             |   46 +
 .../maven/doxia/module/rtf/AlphaNumerals.java      |   51 +
 .../org/apache/maven/doxia/module/rtf/Font.java    |  104 +
 .../apache/maven/doxia/module/rtf/FontMetrics.java |  102 +
 .../apache/maven/doxia/module/rtf/Monospace.java   |  162 ++
 .../maven/doxia/module/rtf/MonospaceBold.java      |  163 ++
 .../doxia/module/rtf/MonospaceBoldItalic.java      |  162 ++
 .../maven/doxia/module/rtf/MonospaceItalic.java    |  162 ++
 .../apache/maven/doxia/module/rtf/PBMReader.java   |  309 ++
 .../maven/doxia/module/rtf/RomanNumerals.java      |   58 +
 .../org/apache/maven/doxia/module/rtf/RtfSink.java | 2244 +++++++++++++++
 .../maven/doxia/module/rtf/RtfSinkFactory.java     |   45 +
 .../apache/maven/doxia/module/rtf/SansSerif.java   |  162 ++
 .../maven/doxia/module/rtf/SansSerifBold.java      |  162 ++
 .../doxia/module/rtf/SansSerifBoldItalic.java      |  162 ++
 .../maven/doxia/module/rtf/SansSerifItalic.java    |  162 ++
 .../org/apache/maven/doxia/module/rtf/Serif.java   |  162 ++
 .../apache/maven/doxia/module/rtf/SerifBold.java   |  162 ++
 .../maven/doxia/module/rtf/SerifBoldItalic.java    |  162 ++
 .../apache/maven/doxia/module/rtf/SerifItalic.java |  162 ++
 .../apache/maven/doxia/module/rtf/WMFWriter.java   |  549 ++++
 doxia-modules/doxia-module-rtf/src/site/site.xml   |   34 +
 .../apache/maven/doxia/module/rtf/RtfSinkTest.java |   93 +
 .../doxia-module-rtf/src/test/resources/test.apt   |   16 +
 doxia-modules/doxia-module-twiki/pom.xml           |   56 +
 .../maven/doxia/module/twiki/TWikiMarkup.java      |  138 +
 .../maven/doxia/module/twiki/TWikiParser.java      |  249 ++
 .../apache/maven/doxia/module/twiki/TWikiSink.java | 1294 +++++++++
 .../maven/doxia/module/twiki/TWikiSinkFactory.java |   44 +
 .../maven/doxia/module/twiki/TWikiSiteModule.java  |   42 +
 .../module/twiki/parser/AbstractFatherBlock.java   |  138 +
 .../doxia/module/twiki/parser/AnchorBlock.java     |   89 +
 .../maven/doxia/module/twiki/parser/Block.java     |   40 +
 .../doxia/module/twiki/parser/BlockParser.java     |   51 +
 .../maven/doxia/module/twiki/parser/BoldBlock.java |   54 +
 .../module/twiki/parser/FormatedTextParser.java    |  311 ++
 .../twiki/parser/GenericListBlockParser.java       |  544 ++++
 .../module/twiki/parser/HRuleBlockParser.java      |   81 +
 .../module/twiki/parser/HorizontalRuleBlock.java   |   51 +
 .../doxia/module/twiki/parser/ImageBlock.java      |   87 +
 .../doxia/module/twiki/parser/ItalicBlock.java     |   54 +
 .../maven/doxia/module/twiki/parser/LinkBlock.java |  106 +
 .../maven/doxia/module/twiki/parser/ListBlock.java |   45 +
 .../doxia/module/twiki/parser/ListItemBlock.java   |  122 +
 .../doxia/module/twiki/parser/MonospaceBlock.java  |   54 +
 .../maven/doxia/module/twiki/parser/NopBlock.java  |   51 +
 .../module/twiki/parser/NumeratedListBlock.java    |   86 +
 .../doxia/module/twiki/parser/ParagraphBlock.java  |   47 +
 .../module/twiki/parser/ParagraphBlockParser.java  |  267 ++
 .../doxia/module/twiki/parser/SectionBlock.java    |  177 ++
 .../module/twiki/parser/SectionBlockParser.java    |  167 ++
 .../doxia/module/twiki/parser/TableBlock.java      |   67 +
 .../module/twiki/parser/TableBlockParser.java      |  115 +
 .../doxia/module/twiki/parser/TableCellBlock.java  |   54 +
 .../module/twiki/parser/TableCellHeaderBlock.java  |   54 +
 .../doxia/module/twiki/parser/TableRowBlock.java   |   54 +
 .../maven/doxia/module/twiki/parser/TextBlock.java |   99 +
 .../doxia/module/twiki/parser/TextParser.java      |  418 +++
 .../module/twiki/parser/UnorderedListBlock.java    |   60 +
 .../doxia/module/twiki/parser/VerbatimBlock.java   |   55 +
 .../module/twiki/parser/VerbatimBlockParser.java   |   83 +
 .../doxia/module/twiki/parser/WikiWordBlock.java   |  125 +
 .../module/twiki/parser/WikiWordLinkResolver.java  |   37 +
 .../doxia/module/twiki/parser/XHTMLBlock.java      |   85 +
 .../twiki/parser/XHTMLWikiWordLinkResolver.java    |   43 +
 doxia-modules/doxia-module-twiki/src/site/site.xml |   34 +
 .../maven/doxia/module/twiki/TWikiSinkTest.java    |  282 ++
 .../module/twiki/parser/AbstractBlockTestCase.java |  108 +
 .../maven/doxia/module/twiki/parser/BlockTest.java |  154 +
 .../module/twiki/parser/FormatedTextTest.java      |  213 ++
 .../maven/doxia/module/twiki/parser/ListTest.java  |  128 +
 .../doxia/module/twiki/parser/ParagraphTest.java   |  169 ++
 .../doxia/module/twiki/parser/SectionTest.java     |  176 ++
 .../maven/doxia/module/twiki/parser/TableTest.java |   88 +
 .../maven/doxia/module/twiki/parser/TitleTest.java |  131 +
 .../doxia/module/twiki/parser/VerbatimTest.java    |  116 +
 .../maven/doxia/module/twiki/parser/WordsTest.java |  327 +++
 .../src/test/resources/TWikiParserTest.twiki       |  109 +
 doxia-modules/doxia-module-xdoc/pom.xml            |  151 +
 .../src/main/components/components.xml             |   36 +
 .../apache/maven/doxia/module/xdoc/XdocMarkup.java |  128 +
 .../apache/maven/doxia/module/xdoc/XdocParser.java |  511 ++++
 .../apache/maven/doxia/module/xdoc/XdocSink.java   |  532 ++++
 .../maven/doxia/module/xdoc/XdocSinkFactory.java   |   49 +
 .../maven/doxia/module/xdoc/XdocSiteModule.java    |   42 +
 .../maven/doxia/module/xdoc/XmlWriterXdocSink.java |   93 +
 .../src/main/resources/xdoc-2.0.xsd                | 2963 +++++++++++++++++++
 .../src/site/apt/using-xdoc-xsd.apt                |   42 +
 doxia-modules/doxia-module-xdoc/src/site/site.xml  |   41 +
 .../maven/doxia/module/xdoc/XdocIdentityTest.java  |   83 +
 .../maven/doxia/module/xdoc/XdocParserTest.java    |  540 ++++
 .../maven/doxia/module/xdoc/XdocSinkTest.java      |  293 ++
 .../module/xdoc/XdocSinkWithLanguageIdTest.java    |   49 +
 .../maven/doxia/module/xdoc/XdocValidatorTest.java |   86 +
 .../doxia/module/xdoc/XmlWriterXdocSinkTest.java   |  106 +
 .../doxia-module-xdoc/src/test/resources/macro.xml |   36 +
 .../src/test/resources/report.xml                  |   41 +
 .../doxia-module-xdoc/src/test/resources/test.xml  |  137 +
 .../doxia-module-xdoc/src/test/resources/toc.xml   |   50 +
 doxia-modules/doxia-module-xhtml/pom.xml           |   43 +
 .../doxia/module/xhtml/AbstractXhtmlSink.java      |   34 +
 .../maven/doxia/module/xhtml/XhtmlMarkup.java      |   45 +
 .../maven/doxia/module/xhtml/XhtmlParser.java      |  215 ++
 .../apache/maven/doxia/module/xhtml/XhtmlSink.java |  268 ++
 .../maven/doxia/module/xhtml/XhtmlSinkFactory.java |   49 +
 .../maven/doxia/module/xhtml/XhtmlSiteModule.java  |   42 +
 doxia-modules/doxia-module-xhtml/src/site/site.xml |   34 +
 .../doxia/module/xhtml/XhtmlIdentityTest.java      |   83 +
 .../maven/doxia/module/xhtml/XhtmlParserTest.java  |  157 ++
 .../maven/doxia/module/xhtml/XhtmlSinkTest.java    |  342 +++
 .../module/xhtml/XhtmlSinkWithLanguageIdTest.java  |   48 +
 .../src/test/resources/download.apt.vm             |   79 +
 .../src/test/resources/file.with.dot.in.name.xml   |    1 +
 .../doxia-module-xhtml/src/test/resources/fun.html |   66 +
 .../src/test/resources/index.xml.vm                |  211 ++
 .../src/test/resources/test.xhtml                  |  130 +
 doxia-modules/pom.xml                              |   68 +
 doxia-modules/src/site/site.xml                    |   36 +
 doxia-sink-api/pom.xml                             |   42 +
 .../java/org/apache/maven/doxia/sink/Sink.java     | 1372 +++++++++
 .../maven/doxia/sink/SinkEventAttributes.java      |  359 +++
 .../org/apache/maven/doxia/sink/SinkFactory.java   |   85 +
 .../main/java/org/codehaus/doxia/sink/Sink.java    |   32 +
 .../org/apache/maven/doxia/sink/package.html       |  120 +
 doxia-sink-api/src/site/apt/index.apt              |   48 +
 doxia-sink-api/src/site/site.xml                   |   34 +
 doxia-test-docs/pom.xml                            |   35 +
 .../src/main/resources/doxia-site/fml/faq.fml      |  103 +
 .../doxia-site/xdoc/references/fml-format.xml      |  100 +
 .../resources/doxia-site/xdoc/references/index.xml |  174 ++
 .../doxia-site/xdoc/references/xdoc-format.xml     |  293 ++
 .../main/resources/maven-ant-plugin/fml/faq.fml    |   41 +
 .../main/resources/maven-antrun-plugin/fml/faq.fml |   37 +
 .../resources/maven-assembly-plugin/fml/faq.fml    |   68 +
 .../resources/maven-changelog-plugin/fml/faq.fml   |  180 ++
 .../resources/maven-changes-plugin/fml/faq.fml     |   65 +
 .../resources/maven-checkstyle-plugin/fml/faq.fml  |   54 +
 .../main/resources/maven-clean-plugin/fml/faq.fml  |   56 +
 .../resources/maven-compiler-plugin/fml/faq.fml    |   51 +
 .../resources/maven-dependency-plugin/fml/faq.fml  |   74 +
 .../main/resources/maven-deploy-plugin/fml/faq.fml |   75 +
 .../main/resources/maven-doap-plugin/fml/faq.fml   |   62 +
 .../main/resources/maven-docck-plugin/fml/faq.fml  |   49 +
 .../main/resources/maven-ear-plugin/fml/faq.fml    |   47 +
 .../resources/maven-eclipse-plugin/fml/faq.fml     |   49 +
 .../main/resources/maven-ejb-plugin/fml/faq.fml    |   63 +
 .../main/resources/maven-gpg-plugin/fml/faq.fml    |   33 +
 .../main/resources/maven-help-plugin/fml/faq.fml   |   41 +
 .../main/resources/maven-idea-plugin/fml/faq.fml   |   47 +
 .../resources/maven-install-plugin/fml/faq.fml     |   34 +
 .../resources/maven-invoker-plugin/fml/faq.fml     |   73 +
 .../main/resources/maven-jar-plugin/fml/faq.fml    |   36 +
 .../resources/maven-javadoc-plugin/fml/faq.fml     |  403 +++
 .../main/resources/maven-one-plugin/fml/faq.fml    |   49 +
 .../main/resources/maven-patch-plugin/fml/faq.fml  |   46 +
 .../main/resources/maven-pmd-plugin/fml/faq.fml    |   38 +
 .../maven-project-info-reports-plugin/fml/faq.fml  |   65 +
 .../main/resources/maven-rar-plugin/fml/faq.fml    |   50 +
 .../maven-remote-resources-plugin/fml/faq.fml      |   84 +
 .../resources/maven-repository-plugin/fml/faq.fml  |   47 +
 .../resources/maven-ressources-plugin/fml/faq.fml  |   79 +
 .../main/resources/maven-site-plugin/fml/faq.fml   |  130 +
 .../main/resources/maven-site-plugin/xdoc/i18n.xml |  399 +++
 .../src/main/resources/maven-site/fml/about.fml    |  120 +
 .../src/main/resources/maven-site/fml/general.fml  |  390 +++
 .../src/main/resources/maven-site/fml/maven1.fml   |  186 ++
 .../main/resources/maven-site/fml/project-faq.fml  |  114 +
 .../main/resources/maven-site/xdoc/articles.xml    |  381 +++
 .../xdoc/developers/mojo-api-specification.xml     |  779 +++++
 .../resources/maven-site/xdoc/docs-required.xml    |  170 ++
 .../main/resources/maven-site/xdoc/errors/404.xml  |   41 +
 .../main/resources/maven-site/xdoc/index.xml.vm    |  195 ++
 .../maven-site/xdoc/source-repository.xml          |  105 +
 .../main/resources/maven-source-plugin/fml/faq.fml |   33 +
 .../main/resources/maven-stage-plugin/fml/faq.fml  |   36 +
 .../resources/maven-verifier-plugin/fml/faq.fml    |   33 +
 .../main/resources/maven-war-plugin/fml/faq.fml    |  138 +
 doxia-test-docs/src/main/resources/pom-4.0.0.xml   |  158 ++
 pom.xml                                            |  510 ++++
 src/main/resources/config/doxia_checkstyle.xml     |  193 ++
 src/site/resources/images/doxia-deps.png           |  Bin 0 -> 26346 bytes
 src/site/site.xml                                  |   52 +
 src/site/xdoc/doxia-deps.odg                       |  Bin 0 -> 13820 bytes
 src/site/xdoc/index.xml                            |   73 +
 498 files changed, 98198 insertions(+)

diff --git a/doxia-book/pom.xml b/doxia-book/pom.xml
new file mode 100644
index 0000000..4e9ea7a
--- /dev/null
+++ b/doxia-book/pom.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.doxia</groupId>
+    <artifactId>doxia</artifactId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-book</artifactId>
+  <name>Doxia :: Book Component</name>
+  <description>A component to write books like user manuals and guides in any format supported by Doxia.</description>
+
+  <dependencies>
+    <!-- doxia core -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-sink-api</artifactId>
+    </dependency>
+
+    <!-- doxia modules ordered -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-apt</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-docbook-simple</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-itext</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-latex</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-xdoc</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-xhtml</artifactId>
+    </dependency>
+
+    <!-- plexus -->
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-container-default</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-i18n</artifactId>
+      <version>1.0-beta-6</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+
+    <!-- test -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.modello</groupId>
+        <artifactId>modello-maven-plugin</artifactId>
+        <configuration>
+          <models>
+            <model>src/main/modello/book.mdo</model>
+          </models>
+          <version>1.0.0</version>
+        </configuration>
+        <executions>
+          <execution>
+            <id>site-docs</id>
+            <phase>pre-site</phase>
+            <goals>
+              <goal>xdoc</goal>
+              <goal>xsd</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>descriptor</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>java</goal>
+              <goal>xpp3-reader</goal>
+              <goal>xsd</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>reporting</id>
+      <reporting>
+        <plugins>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>l10n-maven-plugin</artifactId>
+            <version>1.0-alpha-2</version>
+            <configuration>
+              <locales>
+                <locale>en</locale>
+                <locale>fr</locale>
+              </locales>
+            </configuration>
+          </plugin>
+        </plugins>
+      </reporting>
+    </profile>
+  </profiles>
+</project>
\ No newline at end of file
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/BookDoxia.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/BookDoxia.java
new file mode 100644
index 0000000..1165c0b
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/BookDoxia.java
@@ -0,0 +1,79 @@
+package org.apache.maven.doxia.book;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.model.BookModel;
+
+import java.io.File;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * An interface to create books in different output formats from a book descriptor.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: BookDoxia.java 748141 2009-02-26 13:29:10Z vsiveton $
+ */
+public interface BookDoxia
+{
+    /** The plexus lookup role. */
+    String ROLE = BookDoxia.class.getName();
+
+    /**
+     * Load a BookModel from a descriptor file.
+     *
+     * @param bookDescriptor the book descriptor file.
+     * @return BookModel
+     * @throws org.apache.maven.doxia.book.BookDoxiaException if the model cannot be loaded.
+     */
+    BookModel loadBook( File bookDescriptor )
+        throws BookDoxiaException;
+
+    /**
+     * Creates a book from a BookModel using the {@link Locale#getDefault()} and UTF-8 as default encoding.
+     *
+     * @param book the BookModel.
+     * @param bookRendererId the id of the output format.
+     * @param files a list of source files.
+     * @param outputDirectory the output directory.
+     * @throws org.apache.maven.doxia.book.BookDoxiaException if the model cannot be loaded.
+     * @see #renderBook(BookModel, String, List, File, Locale, String, String)
+     * @see Locale#getDefault()
+     */
+    void renderBook( BookModel book, String bookRendererId, List files, File outputDirectory )
+        throws BookDoxiaException;
+
+    /**
+     * Creates a book from a BookModel.
+     *
+     * @param book the BookModel.
+     * @param bookRendererId the id of the output format.
+     * @param files a list of source files.
+     * @param outputDirectory the output directory.
+     * @param locale the wanted locale.
+     * @param inputEncoding the input encoding when processing <code>files</code>.
+     * @param outputEncoding the output encoding when writing files in <code>ouputDirectory</code>.
+     * @throws org.apache.maven.doxia.book.BookDoxiaException if the model cannot be loaded.
+     * @since 1.1
+     */
+    void renderBook( BookModel book, String bookRendererId, List files, File outputDirectory, Locale locale,
+                     String inputEncoding, String outputEncoding )
+        throws BookDoxiaException;
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/BookDoxiaException.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/BookDoxiaException.java
new file mode 100644
index 0000000..f5c710e
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/BookDoxiaException.java
@@ -0,0 +1,55 @@
+package org.apache.maven.doxia.book;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Wraps an exception when rendering books.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: BookDoxiaException.java 740275 2009-02-03 11:17:28Z vsiveton $
+ */
+public class BookDoxiaException
+    extends Exception
+{
+    /** serialVersionUID */
+    private static final long serialVersionUID = 87146681585045106L;
+
+    /**
+     * Construct a new BookDoxiaException with the specified detail message.
+     *
+     * @param message The detailed message. This can later be retrieved by the Throwable.getMessage() method.
+     */
+    public BookDoxiaException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Construct a new BookDoxiaException with the specified detail message and cause.
+     *
+     * @param message The detailed message. This can later be retrieved by the Throwable.getMessage() method.
+     * @param cause the cause. This can be retrieved later by the Throwable.getCause() method
+     * (a null value is permitted, and indicates that the cause is nonexistent or unknown).
+     */
+    public BookDoxiaException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/DefaultBookDoxia.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/DefaultBookDoxia.java
new file mode 100644
index 0000000..fe43c71
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/DefaultBookDoxia.java
@@ -0,0 +1,155 @@
+package org.apache.maven.doxia.book;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.services.indexer.BookIndexer;
+import org.apache.maven.doxia.book.services.io.BookIo;
+import org.apache.maven.doxia.book.services.renderer.BookRenderer;
+import org.apache.maven.doxia.book.services.validation.BookValidator;
+import org.apache.maven.doxia.book.services.validation.ValidationResult;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Default implementation of BookDoxia.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: DefaultBookDoxia.java 740705 2009-02-04 11:57:33Z vsiveton $
+ * @plexus.component
+ */
+public class DefaultBookDoxia
+    extends AbstractLogEnabled
+    implements BookDoxia
+{
+    /**
+     * @plexus.requirement
+     */
+    private BookIo bookIo;
+
+    /**
+     * @plexus.requirement
+     */
+    private BookValidator bookValidator;
+
+    /**
+     * @plexus.requirement
+     */
+    private BookIndexer bookIndexer;
+
+    /**
+     * @plexus.requirement role="org.apache.maven.doxia.book.services.renderer.BookRenderer"
+     */
+    private Map bookRenderers;
+
+    // ----------------------------------------------------------------------
+    // BookDoxia Implementation
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public BookModel loadBook( File bookDescriptor )
+        throws BookDoxiaException
+    {
+        return bookIo.readBook( bookDescriptor );
+    }
+
+    /** {@inheritDoc} */
+    public void renderBook( BookModel book, String bookRendererId, List files, File outputDirectory )
+        throws BookDoxiaException
+    {
+        renderBook( book, bookRendererId, files, outputDirectory, Locale.getDefault(), "UTF-8", "UTF-8" );
+    }
+
+    /** {@inheritDoc} */
+    public void renderBook( BookModel book, String bookRendererId, List files, File outputDirectory, Locale locale,
+                            String inputEncoding, String outputEncoding )
+        throws BookDoxiaException
+    {
+        // ----------------------------------------------------------------------
+        //
+        // ----------------------------------------------------------------------
+
+        ValidationResult validationResult = bookValidator.validateBook( book );
+
+        if ( !validationResult.isAllOk() )
+        {
+            throw new InvalidBookDescriptorException( validationResult );
+        }
+
+        // ----------------------------------------------------------------------
+        // Create and initialize the context
+        // ----------------------------------------------------------------------
+
+        BookContext context = new BookContext();
+
+        context.setBook( book );
+
+        context.setOutputDirectory( outputDirectory );
+
+        context.setLocale( locale );
+
+        context.setInputEncoding( inputEncoding );
+
+        context.setOutputEncoding( outputEncoding );
+
+        // -----------------------------------------------------------------------
+        //
+        // -----------------------------------------------------------------------
+
+        bookIo.loadFiles( context, files );
+
+        // ----------------------------------------------------------------------
+        // Generate indexes
+        // ----------------------------------------------------------------------
+
+        bookIndexer.indexBook( book, context );
+
+        // ----------------------------------------------------------------------
+        // Render the book
+        // ----------------------------------------------------------------------
+
+        BookRenderer bookRenderer = (BookRenderer) bookRenderers.get( bookRendererId );
+
+        if ( bookRenderer == null )
+        {
+            throw new BookDoxiaException( "No such book renderer '" + bookRendererId + "'." );
+        }
+
+        bookRenderer.renderBook( context );
+    }
+
+    /**
+     * Returns a Set of ids of the BookRenderers that are available in this BookDoxia.
+     *
+     * @return Set
+     */
+    public Set getAvailableBookRenderers()
+    {
+        return Collections.unmodifiableSet( bookRenderers.keySet() );
+    }
+
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/InvalidBookDescriptorException.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/InvalidBookDescriptorException.java
new file mode 100644
index 0000000..2724f37
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/InvalidBookDescriptorException.java
@@ -0,0 +1,61 @@
+package org.apache.maven.doxia.book;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.services.validation.ValidationResult;
+
+/**
+ * Indicates that the book descriptor file could not be parsed correctly.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: InvalidBookDescriptorException.java 769433 2009-04-28 15:26:55Z ltheussl $
+ */
+public class InvalidBookDescriptorException
+    extends BookDoxiaException
+{
+
+    /** serialVersionUID */
+    private static final long serialVersionUID = -5706648416915909753L;
+
+    /** ValidationResult. */
+    private ValidationResult validationResult;
+
+    /**
+     * Construct a new InvalidBookDescriptorException and stores the given ValidationResult.
+     *
+     * @param validationResult The ValidationResult to store.
+     */
+    public InvalidBookDescriptorException( ValidationResult validationResult )
+    {
+        super( "Invalid book descriptor." );
+
+        this.validationResult = validationResult;
+    }
+
+    /**
+     * Return the ValidationResult.
+     *
+     * @return the ValidationResult associated with this Exception.
+     */
+    public ValidationResult getValidationResult()
+    {
+        return validationResult;
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/context/BookContext.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/context/BookContext.java
new file mode 100644
index 0000000..5e896b0
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/context/BookContext.java
@@ -0,0 +1,258 @@
+package org.apache.maven.doxia.book.context;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.model.BookModel;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Context to render a book.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: BookContext.java 746976 2009-02-23 12:15:38Z vsiveton $
+ */
+public class BookContext
+{
+    /** The BookModel of this context. */
+    private BookModel book;
+
+    /** The files. */
+    private Map files;
+
+    /** The output directory. */
+    private File outputDirectory;
+
+    /** The BookIndex of this context. */
+    private BookIndex index;
+
+    /** The Locale used to generate the navigation. */
+    private Locale locale;
+
+    /** The input encoding used to read Doxia file. */
+    private String inputEncoding;
+
+    /** The output encoding used to write the renderer files. */
+    private String outputEncoding;
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /** Represents a BookFile. */
+    public static class BookFile
+    {
+        /** The file. */
+        private File file;
+
+        /** The id of the parser. */
+        private String parserId;
+
+        /**
+         * Constructor.
+         *
+         * @param file the file.
+         * @param parserId the parser id.
+         */
+        public BookFile( File file, String parserId )
+        {
+            this.file = file;
+            this.parserId = parserId;
+        }
+
+        /**
+         * Return the file of this BookFile.
+         *
+         * @return File.
+         */
+        public File getFile()
+        {
+            return file;
+        }
+
+        /**
+         * Return the parserId of this BookFile.
+         *
+         * @return String.
+         */
+        public String getParserId()
+        {
+            return parserId;
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Accessors
+    // ----------------------------------------------------------------------
+
+    /**
+     * Return the BookModel of this BookContext.
+     *
+     * @return BookModel.
+     */
+    public BookModel getBook()
+    {
+        return book;
+    }
+
+    /**
+     * Set the BookModel of this BookContext.
+     *
+     * @param book the BookModel.
+     */
+    public void setBook( BookModel book )
+    {
+        this.book = book;
+    }
+
+    /**
+     * Return the files of this BookContext.
+     *
+     * @return Map. A new HashMap is constructed if the current Map is null.
+     */
+    public Map getFiles()
+    {
+        if ( files == null )
+        {
+            files = new HashMap();
+        }
+
+        return files;
+    }
+
+    /**
+     * Set the files of this BookContext.
+     *
+     * @param files the Map of files.
+     */
+    public void setFiles( Map files )
+    {
+        this.files = files;
+    }
+
+    /**
+     * Return the outputDirectory of this BookContext.
+     *
+     * @return File.
+     */
+    public File getOutputDirectory()
+    {
+        return outputDirectory;
+    }
+
+    /**
+     * Set the outputDirectory of this BookContext.
+     *
+     * @param outputDirectory the output directory.
+     */
+    public void setOutputDirectory( File outputDirectory )
+    {
+        this.outputDirectory = outputDirectory;
+    }
+
+    /**
+     * Return the index of this BookContext.
+     *
+     * @return BookIndex.
+     */
+    public BookIndex getIndex()
+    {
+        return index;
+    }
+
+    /**
+     * Set the index of this BookContext.
+     *
+     * @param index the index to set.
+     */
+    public void setIndex( BookIndex index )
+    {
+        this.index = index;
+    }
+
+    /**
+     * <p>Getter for the field <code>locale</code>.</p>
+     *
+     * @return the locale
+     * @since 1.1
+     */
+    public Locale getLocale()
+    {
+        return locale;
+    }
+
+    /**
+     * <p>Setter for the field <code>locale</code>.</p>
+     *
+     * @param locale the locale to set
+     * @since 1.1
+     */
+    public void setLocale( Locale locale )
+    {
+        this.locale = locale;
+    }
+
+    /**
+     * <p>Getter for the field <code>inputEncoding</code>.</p>
+     *
+     * @return the inputEncoding
+     * @since 1.1
+     */
+    public String getInputEncoding()
+    {
+        return inputEncoding;
+    }
+
+    /**
+     * <p>Setter for the field <code>inputEncoding</code>.</p>
+     *
+     * @param inputEncoding the inputEncoding to set
+     * @since 1.1
+     */
+    public void setInputEncoding( String inputEncoding )
+    {
+        this.inputEncoding = inputEncoding;
+    }
+
+    /**
+     * <p>Getter for the field <code>outputEncoding</code>.</p>
+     *
+     * @return the outputEncoding
+     * @since 1.1
+     */
+    public String getOutputEncoding()
+    {
+        return outputEncoding;
+    }
+
+    /**
+     * <p>Setter for the field <code>outputEncoding</code>.</p>
+     *
+     * @param outputEncoding the outputEncoding to set
+     * @since 1.1
+     */
+    public void setOutputEncoding( String outputEncoding )
+    {
+        this.outputEncoding = outputEncoding;
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/context/BookIndex.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/context/BookIndex.java
new file mode 100644
index 0000000..6b66d06
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/context/BookIndex.java
@@ -0,0 +1,40 @@
+package org.apache.maven.doxia.book.context;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.index.IndexEntry;
+
+/**
+ * <p>BookIndex class.</p>
+ *
+ * @author <a href="mailto:trygve.laugstol at objectware.no">Trygve Laugstøl</a>
+ * @version $Id: BookIndex.java 746976 2009-02-23 12:15:38Z vsiveton $
+ */
+public class BookIndex
+    extends IndexEntry
+{
+    /**
+     * Constructs a new BookIndex.
+     */
+    public BookIndex()
+    {
+        super( "book" );
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/indexer/BookIndexer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/indexer/BookIndexer.java
new file mode 100644
index 0000000..ed4c8bf
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/indexer/BookIndexer.java
@@ -0,0 +1,46 @@
+package org.apache.maven.doxia.book.services.indexer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.BookDoxiaException;
+
+/**
+ * Index a book.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: BookIndexer.java 746976 2009-02-23 12:15:38Z vsiveton $
+ */
+public interface BookIndexer
+{
+    /** The plexus lookup role. */
+    String ROLE = BookIndexer.class.getName();
+
+    /**
+     * Index a book.
+     *
+     * @param book the book to index.
+     * @param bookContext the BookContext.
+     * @throws org.apache.maven.doxia.book.BookDoxiaException if the book cannot be indexed.
+     */
+    void indexBook( BookModel book, BookContext bookContext )
+        throws BookDoxiaException;
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/indexer/DefaultBookIndexer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/indexer/DefaultBookIndexer.java
new file mode 100644
index 0000000..3fa2f13
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/indexer/DefaultBookIndexer.java
@@ -0,0 +1,152 @@
+package org.apache.maven.doxia.book.services.indexer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.Iterator;
+
+import org.apache.maven.doxia.Doxia;
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.context.BookIndex;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.model.Chapter;
+import org.apache.maven.doxia.book.model.Section;
+import org.apache.maven.doxia.index.IndexEntry;
+import org.apache.maven.doxia.index.IndexingSink;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+/**
+ * Default implementation of BookIndexer.
+ *
+ * @plexus.component
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: DefaultBookIndexer.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+public class DefaultBookIndexer
+    extends AbstractLogEnabled
+    implements BookIndexer
+{
+    /**
+     * @plexus.requirement
+     */
+    private Doxia doxia;
+
+    // ----------------------------------------------------------------------
+    // BookIndexer Implementation
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void indexBook( BookModel book, BookContext bookContext )
+        throws BookDoxiaException
+    {
+        BookIndex index = new BookIndex();
+
+        for ( Iterator it = book.getChapters().iterator(); it.hasNext(); )
+        {
+            Chapter chapter = (Chapter) it.next();
+
+            indexChapter( bookContext, index, chapter );
+        }
+
+        bookContext.setIndex( index );
+    }
+
+    // ----------------------------------------------------------------------
+    // Private
+    // ----------------------------------------------------------------------
+
+    /**
+     * Index a chapter.
+     *
+     * @param context the BookContext.
+     * @param bookEntry the IndexEntry.
+     * @param chapter the Chapter to index.
+     * @throws BookDoxiaException if the chapter cannot be indexed.
+     */
+    private void indexChapter( BookContext context, IndexEntry bookEntry, Chapter chapter )
+        throws BookDoxiaException
+    {
+        IndexEntry chapterEntry = new IndexEntry( bookEntry, chapter.getId( ) );
+
+        chapterEntry.setTitle( chapter.getTitle() );
+
+        for ( Iterator it = chapter.getSections().iterator(); it.hasNext(); )
+        {
+            Section section = (Section) it.next();
+
+            indexSection( context, chapterEntry, section );
+        }
+    }
+
+    /**
+     * Index a section.
+     *
+     * @param bookContext the BookContext.
+     * @param chapterEntry the IndexEntry.
+     * @param section the Section to index.
+     * @throws BookDoxiaException if the section cannot be indexed.
+     */
+    private void indexSection( BookContext bookContext, IndexEntry chapterEntry, Section section )
+        throws BookDoxiaException
+    {
+        BookContext.BookFile bookFile = (BookContext.BookFile) bookContext.getFiles().get( section.getId() );
+
+        if ( bookFile == null )
+        {
+            throw new BookDoxiaException( "No document that matches section with id="
+                        + section.getId() + "." );
+        }
+
+        // ----------------------------------------------------------------------
+        //
+        // ----------------------------------------------------------------------
+
+        IndexEntry sectionEntry = new IndexEntry( chapterEntry, section.getId() );
+
+        IndexingSink sink = new IndexingSink( sectionEntry );
+
+        try
+        {
+            doxia.parse( new FileReader( bookFile.getFile() ), bookFile.getParserId(), sink );
+        }
+        catch ( ParserNotFoundException e )
+        {
+            throw new BookDoxiaException( "Parser not found: "
+                        + bookFile.getParserId() + ".", e );
+        }
+        catch ( ParseException e )
+        {
+            throw new BookDoxiaException( "Error while parsing document: "
+                        + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        catch ( FileNotFoundException e )
+        {
+            throw new BookDoxiaException( "Could not find document: "
+                        + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+
+        sectionEntry.setTitle( sink.getTitle() );
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/io/BookIo.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/io/BookIo.java
new file mode 100644
index 0000000..b001c53
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/io/BookIo.java
@@ -0,0 +1,57 @@
+package org.apache.maven.doxia.book.services.io;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.context.BookContext;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Common book-related IO methods.
+ *
+ * @author <a href="mailto:trygve.laugstol at objectware.no">Trygve Laugstøl</a>
+ * @version $Id: BookIo.java 746976 2009-02-23 12:15:38Z vsiveton $
+ */
+public interface BookIo
+{
+    /** The plexus lookup role. */
+    String ROLE = BookIo.class.getName();
+
+    /**
+     * Read a BookModel from a descriptor file.
+     *
+     * @param bookDescriptor the book descriptor file.
+     * @return BookModel
+     * @throws org.apache.maven.doxia.book.BookDoxiaException if the model cannot be read.
+     */
+    BookModel readBook( File bookDescriptor )
+        throws BookDoxiaException;
+
+    /**
+     * Loads files in a given context.
+     *
+     * @param context the BookContext.
+     * @param files a list of files.
+     */
+    void loadFiles( BookContext context, List files );
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/io/DefaultBookIo.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/io/DefaultBookIo.java
new file mode 100644
index 0000000..a67932b
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/io/DefaultBookIo.java
@@ -0,0 +1,151 @@
+package org.apache.maven.doxia.book.services.io;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.model.io.xpp3.BookModelXpp3Reader;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.module.site.SiteModule;
+import org.apache.maven.doxia.module.site.manager.SiteModuleManager;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.Reader;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.List;
+
+/**
+ * <p>DefaultBookIo class.</p>
+ *
+ * @plexus.component
+ * @author <a href="mailto:trygve.laugstol at objectware.no">Trygve Laugstøl</a>
+ * @version $Id: DefaultBookIo.java 769433 2009-04-28 15:26:55Z ltheussl $
+ */
+public class DefaultBookIo
+    extends AbstractLogEnabled
+    implements BookIo
+{
+    /**
+     * @plexus.requirement
+     */
+    private SiteModuleManager siteModuleManager;
+
+    // -----------------------------------------------------------------------
+    // DefaultBookIo Implementation
+    // -----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public BookModel readBook( File bookDescriptor )
+        throws BookDoxiaException
+    {
+        Reader reader = null;
+        try
+        {
+            reader = ReaderFactory.newXmlReader( bookDescriptor );
+            return new BookModelXpp3Reader().read( reader, true );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while reading book descriptor.", e );
+        }
+        catch ( XmlPullParserException e )
+        {
+            throw new BookDoxiaException( "Error while reading book descriptor.", e );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void loadFiles( BookContext context, List files )
+    {
+        // ----------------------------------------------------------------------
+        // Find all the files, map the file names to ids
+        // ----------------------------------------------------------------------
+
+        Collection siteModules = siteModuleManager.getSiteModules();
+
+        for ( Iterator it = siteModules.iterator(); it.hasNext(); )
+        {
+            SiteModule siteModule = (SiteModule) it.next();
+
+            String extension = siteModule.getExtension();
+
+            String sourceDirectory = File.separator + siteModule.getSourceDirectory() + File.separator;
+
+            String parserId = siteModule.getParserId();
+
+            for ( Iterator j = files.iterator(); j.hasNext(); )
+            {
+                File file = (File) j.next();
+
+                String name = file.getName();
+
+                String path = file.getAbsolutePath();
+
+                // first check if the file path contains one of the recognized source dir identifiers
+                // (there's trouble if a pathname contains 2 identifiers), then match file extensions (not unique).
+
+                if ( path.indexOf( sourceDirectory ) != -1 )
+                {
+                    name = name.substring( 0, name.length() - extension.length() - 1 );
+
+                    context.getFiles().put( name, new BookContext.BookFile( file, parserId ) );
+                }
+                else if ( name.endsWith( extension ) )
+                {
+                    name = name.substring( 0, name.length() - extension.length() - 1 );
+
+                    // don't overwrite if it's there already
+                    if ( !context.getFiles().containsKey( name ) )
+                    {
+                        context.getFiles().put( name, new BookContext.BookFile( file, parserId ) );
+                    }
+                }
+            }
+        }
+
+        if ( getLogger().isDebugEnabled() )
+        {
+            getLogger().debug( "Dumping document <-> id mapping:" );
+
+            Map map = new TreeMap( context.getFiles() );
+
+            for ( Iterator it = map.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                BookContext.BookFile file = (BookContext.BookFile) entry.getValue();
+
+                getLogger().debug( " " + entry.getKey() + "=" + file.getFile() + ", parser: " + file.getParserId() );
+            }
+        }
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/AbstractITextBookRenderer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/AbstractITextBookRenderer.java
new file mode 100644
index 0000000..f0cbd94
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/AbstractITextBookRenderer.java
@@ -0,0 +1,343 @@
+package org.apache.maven.doxia.book.services.renderer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.apache.maven.doxia.Doxia;
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.model.Chapter;
+import org.apache.maven.doxia.book.model.Section;
+import org.apache.maven.doxia.module.itext.ITextSinkFactory;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.WriterFactory;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+
+/**
+ * Base class for <code>iText</code> renderer.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractITextBookRenderer.java 1003021 2010-09-30 11:51:11Z ltheussl $
+ */
+public abstract class AbstractITextBookRenderer
+    extends AbstractLogEnabled
+    implements BookRenderer
+{
+    /**
+     * @plexus.requirement
+     */
+    private Doxia doxia;
+
+    // ----------------------------------------------------------------------
+    // BookRenderer Implementation
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void renderBook( BookContext context )
+        throws BookDoxiaException
+    {
+        BookModel book = context.getBook();
+
+        if ( !context.getOutputDirectory().exists() )
+        {
+            if ( !context.getOutputDirectory().mkdirs() )
+            {
+                throw new BookDoxiaException( "Could not make directory: "
+                    + context.getOutputDirectory().getAbsolutePath() + "." );
+            }
+        }
+
+        File bookFile = new File( context.getOutputDirectory(), book.getId() + ".xml" );
+
+        Writer fileWriter;
+        try
+        {
+            fileWriter = WriterFactory.newXmlWriter( bookFile );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while opening file.", e );
+        }
+
+        // ----------------------------------------------------------------------
+        // Create the XML File
+        // ----------------------------------------------------------------------
+
+        PrettyPrintXMLWriter writer = new PrettyPrintXMLWriter( fileWriter, "UTF-8", null );
+        writer.startElement( "itext" );
+        writer.addAttribute( "creationdate", DateFormat.getDateTimeInstance().format( new Date() ) );
+        writer.addAttribute( "producer", "Doxia iText" );
+
+        //        writer.startElement( "paragraph" );
+        //        writer.addAttribute( "leading", "18.0" );
+        //        writer.addAttribute( "font", "unknown" );
+        //        writer.addAttribute( "align", "Default" );
+        //        writer.writeText( "Please visit my" + System.getProperty( "line.separator" ) );
+        //
+        //        writer.startElement( "anchor" );
+        //        writer.addAttribute( "leading", "18.0" );
+        //        writer.addAttribute( "font", "Helvetica" );
+        //        writer.addAttribute( "size", "12.0" );
+        //        writer.addAttribute( "fontstyle", "normal, underline" );
+        //        writer.addAttribute( "red", "0" );
+        //        writer.addAttribute( "green", "0" );
+        //        writer.addAttribute( "blue", "255" );
+        //        writer.addAttribute( "name", "top" );
+        //        writer.addAttribute( "reference", "http://www.lowagie.com/iText/" );
+        //
+        //        writer.startElement( "chunk" );
+        //        writer.addAttribute( "font", "Helvetica" );
+        //        writer.addAttribute( "size", "12.0" );
+        //        writer.addAttribute( "fontstyle", "normal, underline" );
+        //        writer.addAttribute( "red", "0" );
+        //        writer.addAttribute( "green", "0" );
+        //        writer.addAttribute( "blue", "255" );
+        //        writer.writeText( "website (external reference)" );
+        //        writer.endElement();
+        //
+        //        writer.endElement(); // anchor
+        //
+        //        writer.endElement(); // paragraph
+
+        // TODO: Write out TOC
+
+        System.setProperty( "itext.basedir", bookFile.getParentFile().getAbsolutePath() );
+        Sink sink = new ITextSinkFactory().createSink( writer );
+
+        try
+        {
+            for ( Iterator it = book.getChapters().iterator(); it.hasNext(); )
+            {
+                Chapter chapter = (Chapter) it.next();
+
+                renderChapter( sink, writer, chapter, context );
+            }
+
+            writer.endElement(); // itext
+        }
+        finally
+        {
+            sink.flush();
+            sink.close();
+
+            IOUtil.close( fileWriter );
+            System.getProperties().remove( "itext.basedir" );
+        }
+
+        // ----------------------------------------------------------------------
+        // Render the XML to PDF
+        // ----------------------------------------------------------------------
+        File outputFile = new File( context.getOutputDirectory(), book.getId() + "." + getOutputExtension() );
+        try
+        {
+            renderXML( bookFile, outputFile );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while rendering file", e );
+        }
+    }
+
+    /**
+     * Get the output extension supported.
+     *
+     * @return the ouput extension supported.
+     */
+    public abstract String getOutputExtension();
+
+    /**
+     * Generate an ouput file with the iText framework.
+     *
+     * @param iTextFile the input file.
+     * @param iTextOutput the output file.
+     * @throws java.io.IOException if any.
+     */
+    public abstract void renderXML( File iTextFile, File iTextOutput )
+        throws IOException;
+
+    // ----------------------------------------------------------------------
+    // Private
+    // ----------------------------------------------------------------------
+
+    /**
+     * Write a chapter.
+     *
+     * @param writer the writer.
+     * @param chapter the Chapter.
+     * @param context the BookContext.
+     * @throws BookDoxiaException if the chapter cannot be written.
+     */
+    private void renderChapter( Sink sink, PrettyPrintXMLWriter writer, Chapter chapter, BookContext context )
+        throws BookDoxiaException
+    {
+        writer.startElement( "chapter" );
+        writer.addAttribute( "numberdepth", "1" );
+        writer.addAttribute( "depth", "1" );
+        writer.addAttribute( "indent", "1" );
+
+        startTitle( writer, "36.0", "Helvetica", "24.0", "normal", "255", "0", "0" );
+        chunk( writer, chapter.getTitle(), "Helvetica", "24.0", "normal", "255", "0", "0" );
+        writer.endElement(); // title
+
+        //        writer.startElement( "sectioncontent" );
+        for ( Iterator it = chapter.getSections().iterator(); it.hasNext(); )
+        {
+            Section section = (Section) it.next();
+
+            renderSection( sink, writer, section, context );
+        }
+        //        writer.endElement(); // sectioncontent
+
+        writer.endElement(); // chapter
+    }
+
+    /**
+     * Write a section.
+     *
+     * @param writer the writer.
+     * @param section the Section.
+     * @param context the BookContext.
+     * @throws BookDoxiaException if the section cannot be written.
+     */
+    private void renderSection( Sink sink, PrettyPrintXMLWriter writer, Section section, BookContext context )
+        throws BookDoxiaException
+    {
+        //        writer.startElement( "section" );
+
+        // ----------------------------------------------------------------------
+        //
+        // ----------------------------------------------------------------------
+
+        BookContext.BookFile bookFile = (BookContext.BookFile) context.getFiles().get( section.getId() );
+
+        if ( bookFile == null )
+        {
+            throw new BookDoxiaException( "No document that matches section with id=" + section.getId() + "." );
+        }
+
+        // ----------------------------------------------------------------------
+        //
+        // ----------------------------------------------------------------------
+
+        Reader reader = null;
+        try
+        {
+            reader = ReaderFactory.newReader( bookFile.getFile(), context.getInputEncoding() );
+            doxia.parse( reader, bookFile.getParserId(), sink );
+        }
+        catch ( ParserNotFoundException e )
+        {
+            throw new BookDoxiaException( "Parser not found: " + bookFile.getParserId() + ".", e );
+        }
+        catch ( ParseException e )
+        {
+            throw new BookDoxiaException(
+                                          "Error while parsing document: " + bookFile.getFile().getAbsolutePath() + ".",
+                                          e );
+        }
+        catch ( FileNotFoundException e )
+        {
+            throw new BookDoxiaException( "Could not find document: " + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while rendering book: "
+                      + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+    }
+
+    /**
+     * Start a title.
+     *
+     * @param writer the writer.
+     * @param leading leading.
+     * @param font the font.
+     * @param size the size.
+     * @param fontstyle the fontstyle.
+     * @param red red.
+     * @param green green.
+     * @param blue blue.
+     */
+    private void startTitle( PrettyPrintXMLWriter writer, String leading, String font, String size, String fontstyle,
+                             String red, String green, String blue )
+    {
+        writer.startElement( "title" );
+        writer.addAttribute( "leading", leading );
+        writer.addAttribute( "font", font );
+        writer.addAttribute( "size", size );
+        writer.addAttribute( "fontstyle", fontstyle );
+        writer.addAttribute( "red", red );
+        writer.addAttribute( "green", green );
+        writer.addAttribute( "blue", blue );
+    }
+
+    /**
+     * Write a chunk.
+     *
+     * @param writer the writer.
+     * @param title the title.
+     * @param font the font.
+     * @param size the size.
+     * @param fontstyle the fontstyle.
+     * @param red red.
+     * @param green green.
+     * @param blue blue.
+     */
+    private void chunk( PrettyPrintXMLWriter writer, String title, String font, String size, String fontstyle,
+                        String red, String green, String blue )
+    {
+        writer.startElement( "chunk" );
+        writer.addAttribute( "font", font );
+        writer.addAttribute( "size", size );
+        writer.addAttribute( "fontstyle", fontstyle );
+        writer.addAttribute( "red", red );
+        writer.addAttribute( "green", green );
+        writer.addAttribute( "blue", blue );
+        if ( StringUtils.isNotEmpty( title ) )
+        {
+            writer.writeText( title );
+        }
+        else
+        {
+            writer.writeText( "<Missing title>" );
+        }
+        writer.endElement(); // chunk
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/BookRenderer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/BookRenderer.java
new file mode 100644
index 0000000..019117d
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/BookRenderer.java
@@ -0,0 +1,44 @@
+package org.apache.maven.doxia.book.services.renderer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.BookDoxiaException;
+
+/**
+ * Render a book.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: BookRenderer.java 746976 2009-02-23 12:15:38Z vsiveton $
+ */
+public interface BookRenderer
+{
+    /** The plexus lookup role. */
+    String ROLE = BookRenderer.class.getName();
+
+    /**
+     * Render a book.
+     *
+     * @param context the BookContext.
+     * @throws org.apache.maven.doxia.book.BookDoxiaException if the book cannot be rendered.
+     */
+    void renderBook( BookContext context )
+        throws BookDoxiaException;
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/DocbookBookRenderer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/DocbookBookRenderer.java
new file mode 100644
index 0000000..0ecdfd8
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/DocbookBookRenderer.java
@@ -0,0 +1,228 @@
+package org.apache.maven.doxia.book.services.renderer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Iterator;
+
+import org.apache.maven.doxia.Doxia;
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.model.Chapter;
+import org.apache.maven.doxia.book.model.Section;
+import org.apache.maven.doxia.book.services.renderer.docbook.DocBookBookSink;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.WriterFactory;
+
+/**
+ * An implementation of <code>BookRenderer</code> for docbook
+ *
+ * @plexus.component role-hint="doc-book"
+ * @author Eric Redmond
+ * @version $Id: DocbookBookRenderer.java 1003021 2010-09-30 11:51:11Z ltheussl $
+ */
+public class DocbookBookRenderer
+    extends AbstractLogEnabled
+    implements BookRenderer
+{
+    /**
+     * @plexus.requirement
+     */
+    private Doxia doxia;
+
+    // ----------------------------------------------------------------------
+    // BookRenderer Implementation
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void renderBook( BookContext context )
+        throws BookDoxiaException
+    {
+        BookModel book = context.getBook();
+
+        if ( !context.getOutputDirectory().exists() )
+        {
+            if ( !context.getOutputDirectory().mkdirs() )
+            {
+                throw new BookDoxiaException( "Could not make directory: "
+                        + context.getOutputDirectory().getAbsolutePath() + "." );
+            }
+        }
+
+        File bookFile = new File( context.getOutputDirectory(), book.getId() + ".xml" );
+
+        Writer fileWriter;
+        try
+        {
+            fileWriter = WriterFactory.newXmlWriter( bookFile );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while opening file.", e );
+        }
+
+        // ----------------------------------------------------------------------
+        // Create the Dockbook File
+        // ----------------------------------------------------------------------
+
+        // TODO: Write out TOC?
+
+        DocBookBookSink sink = new DocBookBookSink( fileWriter );
+
+        try
+        {
+            sink.book();
+
+            // TODO: symmetrize bookHead?
+
+            if ( StringUtils.isNotEmpty( book.getTitle() ) )
+            {
+                sink.bookTitle();
+                sink.text( book.getTitle() );
+                sink.bookTitle_();
+            }
+
+            if ( StringUtils.isNotEmpty( book.getDate() ) )
+            {
+                sink.bookDate();
+                sink.text( book.getDate() );
+                sink.bookDate_();
+            }
+
+            if ( StringUtils.isNotEmpty( book.getAuthor() ) )
+            {
+                sink.bookAuthor();
+                sink.text( book.getAuthor() );
+                sink.bookAuthor_();
+            }
+
+            sink.bookHead_();
+
+            for ( Iterator it = book.getChapters().iterator(); it.hasNext(); )
+            {
+                Chapter chapter = (Chapter) it.next();
+
+                sink.chapter();
+
+                if ( StringUtils.isNotEmpty( chapter.getTitle() ) )
+                {
+                    sink.chapterTitle();
+                    sink.text( chapter.getTitle() );
+                    sink.chapterTitle_();
+                }
+
+                renderChapter( chapter, context, sink );
+
+                sink.chapter_();
+            }
+
+            sink.book_();
+        }
+        finally
+        {
+            sink.flush();
+
+            sink.close();
+
+            IOUtil.close( fileWriter );
+        }
+    }
+
+    /**
+     * Write a chapter.
+     *
+     * @param writer the writer.
+     * @param chapter the Chapter.
+     * @param context the BookContext.
+     * @param sink a Sink.
+     * @throws BookDoxiaException if the chapter cannot be written.
+     */
+    private void renderChapter( Chapter chapter, BookContext context, Sink sink )
+        throws BookDoxiaException
+    {
+        for ( Iterator it = chapter.getSections().iterator(); it.hasNext(); )
+        {
+            Section section = (Section) it.next();
+
+            renderSection( section, context, sink );
+        }
+    }
+
+    /**
+     * Write a section.
+     *
+     * @param writer the writer.
+     * @param section the Section.
+     * @param context the BookContext.
+     * @param sink a Sink.
+     * @throws BookDoxiaException if the section cannot be written.
+     */
+    private void renderSection( Section section, BookContext context, Sink sink )
+        throws BookDoxiaException
+    {
+        BookContext.BookFile bookFile = (BookContext.BookFile) context.getFiles().get( section.getId() );
+
+        if ( bookFile == null )
+        {
+            throw new BookDoxiaException( "No document that matches section with id=" + section.getId() + "." );
+        }
+
+        Reader reader = null;
+        try
+        {
+            reader = ReaderFactory.newReader( bookFile.getFile(), context.getInputEncoding() );
+            doxia.parse( reader, bookFile.getParserId(), sink );
+        }
+        catch ( ParserNotFoundException e )
+        {
+            throw new BookDoxiaException( "Parser not found: " + bookFile.getParserId() + ".", e );
+        }
+        catch ( ParseException e )
+        {
+            throw new BookDoxiaException(
+                                          "Error while parsing document: " + bookFile.getFile().getAbsolutePath() + ".",
+                                          e );
+        }
+        catch ( FileNotFoundException e )
+        {
+            throw new BookDoxiaException( "Could not find document: " + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while rendering book: "
+                      + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/LatexBookRenderer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/LatexBookRenderer.java
new file mode 100644
index 0000000..0e16eac
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/LatexBookRenderer.java
@@ -0,0 +1,255 @@
+package org.apache.maven.doxia.book.services.renderer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.services.renderer.latex.LatexBookSink;
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.model.Chapter;
+import org.apache.maven.doxia.book.model.Section;
+import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.Doxia;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.WriterFactory;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.FileNotFoundException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * <p>LatexBookRenderer class.</p>
+ *
+ * @plexus.component role-hint="latex"
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: LatexBookRenderer.java 1003021 2010-09-30 11:51:11Z ltheussl $
+ */
+public class LatexBookRenderer
+    implements BookRenderer
+{
+    /**
+     * @plexus.requirement
+     */
+    private Doxia doxia;
+
+    // ----------------------------------------------------------------------
+    // BookRenderer Implementatino
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void renderBook( BookContext context )
+        throws BookDoxiaException
+    {
+        BookModel book = context.getBook();
+
+        if ( !context.getOutputDirectory().exists() )
+        {
+            if ( !context.getOutputDirectory().mkdirs() )
+            {
+                throw new BookDoxiaException(
+                    "Could not make directory: " + context.getOutputDirectory().getAbsolutePath() + "." );
+            }
+        }
+
+        File bookFile = new File( context.getOutputDirectory(), book.getId() + ".tex" );
+
+        FileWriter fileWriter = null;
+
+        try
+        {
+            fileWriter = new FileWriter( bookFile );
+
+            PrintWriter writer = new PrintWriter( fileWriter );
+
+            writeBook( book, context, writer );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while opening file.", e );
+        }
+        finally
+        {
+            IOUtil.close( fileWriter );
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Private
+    // ----------------------------------------------------------------------
+
+    /** SectionInfo: id and title. */
+    static class SectionInfo
+    {
+        /** id. */
+        String id;
+
+        /** title. */
+        String title;
+    }
+
+    /**
+     * Write a book.
+     *
+     * @param book the BookModel to write.
+     * @param context the BookContext.
+     * @param writer the writer to use.
+     * @throws IOException if any.
+     * @throws BookDoxiaException if the section cannot be written.
+     */
+    private void writeBook( BookModel book, BookContext context, PrintWriter writer )
+        throws IOException, BookDoxiaException
+    {
+        // ----------------------------------------------------------------------
+        // Process all the section documents and collect their names
+        // ----------------------------------------------------------------------
+
+        Map sectionInfos = new HashMap();
+
+        for ( Iterator it = book.getChapters().iterator(); it.hasNext(); )
+        {
+            Chapter chapter = (Chapter) it.next();
+
+            for ( Iterator j = chapter.getSections().iterator(); j.hasNext(); )
+            {
+                Section section = (Section) j.next();
+
+                SectionInfo info = writeSection( section, context );
+
+                sectionInfos.put( info.id, info );
+            }
+        }
+
+        // ----------------------------------------------------------------------
+        // Write the main .tex file
+        // ----------------------------------------------------------------------
+
+        writer.println( "\\documentclass{book}" );
+        writer.println( "\\title{" + book.getTitle() + "}" );
+
+        if ( StringUtils.isNotEmpty( book.getAuthor() ) )
+        {
+            writer.println( "\\author{" + book.getAuthor() + "}" );
+        }
+
+        if ( StringUtils.isNotEmpty( book.getDate() ) )
+        {
+            writer.println( "\\date{" + book.getDate() + "}" );
+        }
+
+        LatexBookSink sink = new LatexBookSink( writer );
+        sink.defaultBookPreamble();
+
+        writer.println( "\\begin{document}" );
+        writer.println( "\\maketitle" );
+        writer.println( "\\tableofcontents" );
+//        writer.println( "\\listoffigures" );
+
+        for ( Iterator it = book.getChapters().iterator(); it.hasNext(); )
+        {
+            Chapter chapter = (Chapter) it.next();
+
+            writer.println( "\\chapter{" + chapter.getTitle() + "}" );
+
+            for ( Iterator j = chapter.getSections().iterator(); j.hasNext(); )
+            {
+                Section section = (Section) j.next();
+
+                SectionInfo info = (SectionInfo) sectionInfos.get( section.getId() );
+
+                writer.println( "\\input{" + info.id + "}" );
+            }
+        }
+
+        writer.println( "\\end{document}" );
+    }
+
+    /**
+     * Write a section.
+     *
+     * @param section the Section to write.
+     * @param context the BookContext.
+     * @return SectionInfo
+     * @throws IOException if any.
+     * @throws BookDoxiaException if the section cannot be written.
+     */
+    private SectionInfo writeSection( Section section, BookContext context )
+        throws IOException, BookDoxiaException
+    {
+        File file = new File( context.getOutputDirectory(), ( section.getId() + ".tex" ) );
+
+        Writer writer = WriterFactory.newWriter( file, context.getOutputEncoding() );
+
+        LatexBookSink sink = new LatexBookSink( writer );
+
+        BookContext.BookFile bookFile = (BookContext.BookFile) context.getFiles().get( section.getId() );
+
+        if ( bookFile == null )
+        {
+            throw new BookDoxiaException( "No document that matches section with id="
+                        + section.getId() + "." );
+        }
+
+        Reader reader = null;
+        try
+        {
+            reader = ReaderFactory.newReader( bookFile.getFile(), context.getInputEncoding() );
+            doxia.parse( reader, bookFile.getParserId(), sink );
+        }
+        catch ( ParserNotFoundException e )
+        {
+            throw new BookDoxiaException( "Parser not found: "
+                        + bookFile.getParserId() + ".", e );
+        }
+        catch ( ParseException e )
+        {
+            throw new BookDoxiaException( "Error while parsing document: "
+                        + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        catch ( FileNotFoundException e )
+        {
+            throw new BookDoxiaException( "Could not find document: "
+                        + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        finally
+        {
+            sink.flush();
+            sink.close();
+
+            IOUtil.close( reader );
+            IOUtil.close( writer );
+        }
+
+        SectionInfo info = new SectionInfo();
+        info.id = section.getId();
+        info.title = sink.getTitle();
+
+        return info;
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/PdfBookRenderer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/PdfBookRenderer.java
new file mode 100644
index 0000000..c4db019
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/PdfBookRenderer.java
@@ -0,0 +1,52 @@
+package org.apache.maven.doxia.book.services.renderer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.maven.doxia.module.itext.ITextUtil;
+
+/**
+ * PDF book renderer with the <code>iText</code> framework.
+ *
+ * @plexus.component role-hint="pdf"
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: PdfBookRenderer.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+public class PdfBookRenderer
+    extends AbstractITextBookRenderer
+{
+    /** {@inheritDoc} */
+    public String getOutputExtension()
+    {
+        return "pdf";
+    }
+
+    /** {@inheritDoc} */
+    public void renderXML( File iTextFile, File iTextOutput )
+        throws IOException
+    {
+        ITextUtil.writePdf( new FileInputStream( iTextFile ), new FileOutputStream( iTextOutput ) );
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/RtfBookRenderer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/RtfBookRenderer.java
new file mode 100644
index 0000000..05c669e
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/RtfBookRenderer.java
@@ -0,0 +1,52 @@
+package org.apache.maven.doxia.book.services.renderer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.maven.doxia.module.itext.ITextUtil;
+
+/**
+ * RTF book renderer with the <code>iText</code> framework.
+ *
+ * @plexus.component role-hint="rtf"
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: RtfBookRenderer.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+public class RtfBookRenderer
+    extends AbstractITextBookRenderer
+{
+    /** {@inheritDoc} */
+    public String getOutputExtension()
+    {
+        return "rtf";
+    }
+
+    /** {@inheritDoc} */
+    public void renderXML( File iTextFile, File iTextOutput )
+        throws IOException
+    {
+        ITextUtil.writeRtf( new FileInputStream( iTextFile ), new FileOutputStream( iTextOutput ) );
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/XHtmlBookRenderer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/XHtmlBookRenderer.java
new file mode 100644
index 0000000..c32f479
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/XHtmlBookRenderer.java
@@ -0,0 +1,213 @@
+package org.apache.maven.doxia.book.services.renderer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.Doxia;
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.model.Chapter;
+import org.apache.maven.doxia.book.model.Section;
+import org.apache.maven.doxia.book.services.renderer.xhtml.XhtmlBookSink;
+import org.apache.maven.doxia.sink.render.RenderingContext;
+import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
+import org.apache.maven.doxia.parser.ParseException;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Iterator;
+
+/**
+ * <p>XHtmlBookRenderer class.</p>
+ *
+ * @plexus.component role-hint="xhtml"
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: XHtmlBookRenderer.java 784534 2009-06-14 10:05:31Z vsiveton $
+ */
+public class XHtmlBookRenderer
+    extends AbstractLogEnabled
+    implements BookRenderer
+{
+    /**
+     * @plexus.requirement
+     */
+    private Doxia doxia;
+
+    // ----------------------------------------------------------------------
+    // BookRenderer Implementation
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void renderBook( BookContext context )
+        throws BookDoxiaException
+    {
+        BookModel book = context.getBook();
+
+        if ( !context.getOutputDirectory().exists() )
+        {
+            if ( !context.getOutputDirectory().mkdirs() )
+            {
+                throw new BookDoxiaException( "Could not make directory: "
+                            + context.getOutputDirectory().getAbsolutePath() + "." );
+            }
+        }
+
+        File bookFile = new File( context.getOutputDirectory(), book.getId() + ".xhtml" );
+
+        Writer fileWriter;
+
+        try
+        {
+            fileWriter = new FileWriter( bookFile );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while opening file.", e );
+        }
+
+        XhtmlBookSink sink = new XhtmlBookSink( fileWriter,
+              new RenderingContext( context.getOutputDirectory(), bookFile.getAbsolutePath() ) );
+
+        try
+        {
+            sink.bookHead();
+            sink.bookTitle();
+            sink.text( context.getBook().getTitle() );
+            sink.bookTitle_();
+            sink.bookAuthor();
+            sink.text( context.getBook().getAuthor() );
+            sink.bookAuthor_();
+            sink.bookDate();
+            sink.text( context.getBook().getDate() );
+            sink.bookDate_();
+            sink.bookHead_();
+            sink.bookBody();
+
+            int chapterNumber = 1;
+
+            for ( Iterator it = book.getChapters().iterator(); it.hasNext(); )
+            {
+                Chapter chapter = (Chapter) it.next();
+
+                sink.sectionTitle();
+                sink.text( Integer.toString( chapterNumber ) + ". " + chapter.getTitle() );
+                sink.sectionTitle_();
+
+                renderChapter( sink, chapter, context );
+
+                chapterNumber++;
+            }
+
+            sink.bookBody_();
+        }
+        finally
+        {
+            sink.flush();
+
+            sink.close();
+
+            IOUtil.close( fileWriter );
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Private
+    // ----------------------------------------------------------------------
+
+    /**
+     * Write a chapter.
+     *
+     * @param sink the XhtmlBookSink.
+     * @param chapter the Chapter.
+     * @param context the BookContext.
+     * @throws BookDoxiaException if the chapter cannot be written.
+     */
+    private void renderChapter( XhtmlBookSink sink, Chapter chapter, BookContext context )
+        throws BookDoxiaException
+    {
+        for ( Iterator it = chapter.getSections().iterator(); it.hasNext(); )
+        {
+            Section section = (Section) it.next();
+
+            renderSection( sink, section, context );
+        }
+    }
+
+    /**
+     * Write a section.
+     *
+     * @param sink the XhtmlBookSink.
+     * @param section the Section.
+     * @param context the BookContext.
+     * @throws BookDoxiaException if the section cannot be written.
+     */
+    private void renderSection( XhtmlBookSink sink, Section section, BookContext context )
+        throws BookDoxiaException
+    {
+        sink.section2();
+
+        BookContext.BookFile bookFile = (BookContext.BookFile) context.getFiles().get( section.getId() );
+
+        if ( bookFile == null )
+        {
+            throw new BookDoxiaException( "No document that matches section with id=" + section.getId() + "." );
+        }
+
+        Reader reader = null;
+        try
+        {
+            reader = ReaderFactory.newReader( bookFile.getFile(), context.getInputEncoding() );
+            doxia.parse( reader, bookFile.getParserId(), sink );
+        }
+        catch ( ParserNotFoundException e )
+        {
+            throw new BookDoxiaException( "Parser not found: "
+                      + bookFile.getParserId() + ".", e );
+        }
+        catch ( ParseException e )
+        {
+            throw new BookDoxiaException( "Error while parsing document: "
+                      + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        catch ( FileNotFoundException e )
+        {
+            throw new BookDoxiaException( "Could not find document: "
+                      + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while rendering book: "
+                      + bookFile.getFile().getAbsolutePath() + ".", e );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+
+        sink.section2_();
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/XdocBookRenderer.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/XdocBookRenderer.java
new file mode 100644
index 0000000..f13c86b
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/XdocBookRenderer.java
@@ -0,0 +1,452 @@
+package org.apache.maven.doxia.book.services.renderer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.Locale;
+
+import org.apache.maven.doxia.Doxia;
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.model.Chapter;
+import org.apache.maven.doxia.book.model.Section;
+import org.apache.maven.doxia.book.services.renderer.xdoc.ChapterXdocBookSink;
+import org.apache.maven.doxia.book.services.renderer.xdoc.IndexXdocBookSink;
+import org.apache.maven.doxia.book.services.renderer.xdoc.SectionXdocBookSink;
+import org.apache.maven.doxia.index.IndexEntry;
+import org.apache.maven.doxia.module.xdoc.XdocSink;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
+import org.apache.maven.doxia.util.HtmlTools;
+import org.codehaus.plexus.i18n.I18N;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.WriterFactory;
+
+/**
+ * An implementation of <code>BookRenderer</code> for Xdoc
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XdocBookRenderer.java 1003021 2010-09-30 11:51:11Z ltheussl $
+ * @plexus.component role-hint="xdoc"
+ */
+public class XdocBookRenderer
+    extends AbstractLogEnabled
+    implements BookRenderer
+{
+    /**
+     * @plexus.requirement
+     */
+    private Doxia doxia;
+
+    /**
+     * @plexus.requirement
+     */
+    private I18N i18n;
+
+    // ----------------------------------------------------------------------
+    // BookRenderer Implementation
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void renderBook( BookContext context )
+        throws BookDoxiaException
+    {
+        BookModel book = context.getBook();
+
+        if ( !context.getOutputDirectory().exists() )
+        {
+            if ( !context.getOutputDirectory().mkdirs() )
+            {
+                throw new BookDoxiaException( "Could not make directory: "
+                    + context.getOutputDirectory().getAbsolutePath() + "." );
+            }
+        }
+
+        renderBook( book, context );
+    }
+
+    // -----------------------------------------------------------------------
+    // Protected
+    // -----------------------------------------------------------------------
+
+    /**
+     * Gets a trimmed String for the given key from the resource bundle defined by Plexus.
+     *
+     * @param locale the locale used
+     * @param key the key for the desired string
+     * @return the string for the given key and the given locale
+     */
+    protected String getString( Locale locale, String key )
+    {
+        if ( StringUtils.isEmpty( key ) )
+        {
+            throw new IllegalArgumentException( "The key cannot be empty" );
+        }
+
+        return i18n.getString( "book-renderer", locale, key ).trim();
+    }
+
+    // -----------------------------------------------------------------------
+    // Private
+    // -----------------------------------------------------------------------
+
+    /**
+     * Render the book, ie the book index and all chapter index and pages
+     *
+     * @param book the BookModel.
+     * @param context the BookContext.
+     * @throws BookDoxiaException if any
+     */
+    private void renderBook( BookModel book, BookContext context )
+        throws BookDoxiaException
+    {
+        // -----------------------------------------------------------------------
+        // Render the book index.xml page
+        // -----------------------------------------------------------------------
+
+        File index = new File( context.getOutputDirectory(), "index.xml" );
+
+        try
+        {
+            writeBookIndex( index, book, context );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while rendering index page to: '"
+                        + index.getAbsolutePath() + "'.", e );
+        }
+
+        // -----------------------------------------------------------------------
+        // Render all the chapter pages
+        // -----------------------------------------------------------------------
+
+        Iterator ii = context.getIndex().getChildEntries().iterator();
+
+        for ( Iterator it = book.getChapters().iterator(); it.hasNext(); )
+        {
+            Chapter chapter = (Chapter) it.next();
+
+            renderChapter( chapter, context, (IndexEntry) ii.next() );
+        }
+    }
+
+    /**
+     * Write the book index, ie a TOC.
+     *
+     * @param index the File.
+     * @param book the BookModel.
+     * @param context the BookContext.
+     * @throws IOException if any
+     */
+    private void writeBookIndex( File index, BookModel book, BookContext context )
+        throws IOException
+    {
+        Writer writer = WriterFactory.newXmlWriter( index );
+
+        XdocSink sink = new IndexXdocBookSink( writer, context.getIndex().getFirstEntry(), i18n, context.getLocale() );
+
+        try
+        {
+            // -----------------------------------------------------------------------
+            // Head
+            // -----------------------------------------------------------------------
+
+            sink.head();
+
+            sink.title();
+            sink.text( book.getTitle() + " - " + getString( context.getLocale(), "toc" ) );
+            sink.title_();
+
+            sink.head_();
+
+            // -----------------------------------------------------------------------
+            // Body
+            // -----------------------------------------------------------------------
+
+            sink.body();
+
+            sink.section1();
+            sink.sectionTitle1();
+            sink.text( book.getTitle() + " - " + getString( context.getLocale(), "toc" ) );
+            sink.sectionTitle1_();
+
+            sink.list();
+            for ( Iterator it = context.getIndex().getChildEntries().iterator(); it.hasNext(); )
+            {
+                writeChapterIndexForBookIndex( sink, (IndexEntry) it.next() );
+            }
+            sink.list_();
+
+            sink.section1_();
+
+            sink.body_();
+        }
+        finally
+        {
+            sink.flush();
+
+            sink.close();
+
+            IOUtil.close( writer );
+        }
+    }
+
+    /**
+     * Write the chapter index for the book index.
+     *
+     * @param sink the XdocSink.
+     * @param chapterEntry the chapter IndexEntry.
+     */
+    private void writeChapterIndexForBookIndex( XdocSink sink, IndexEntry chapterEntry )
+    {
+        sink.listItem();
+        sink.link( chapterEntry.getId() + ".html" );
+        sink.text( chapterEntry.getTitle() );
+        sink.link_();
+
+        sink.list();
+        for ( Iterator it = chapterEntry.getChildEntries().iterator(); it.hasNext(); )
+        {
+            IndexEntry sectionIndex = (IndexEntry) it.next();
+            writeSectionIndexForBookIndex( sink, sectionIndex );
+        }
+        sink.list_();
+
+        sink.listItem_();
+    }
+
+    /**
+     * Write the section index for the book index.
+     *
+     * @param sink the XdocSink.
+     * @param sectionIndex the section IndexEntry.
+     */
+    private void writeSectionIndexForBookIndex( XdocSink sink, IndexEntry sectionIndex )
+    {
+        sink.listItem();
+        sink.link( sectionIndex.getId() + ".html" );
+        sink.text( sectionIndex.getTitle() );
+        sink.link_();
+
+        sink.list();
+        for ( Iterator it = sectionIndex.getChildEntries().iterator(); it.hasNext(); )
+        {
+            IndexEntry subsectionIndex = (IndexEntry) it.next();
+            writeSubsectionIndexForBookIndex( sink, sectionIndex, subsectionIndex );
+        }
+        sink.list_();
+
+        sink.listItem_();
+    }
+
+    /**
+     * Write subsection index for the book index.
+     *
+     * @param sink the XdocSink.
+     * @param sectionIndex the section IndexEntry.
+     * @param subsectionIndex the subsection IndexEntry.
+     */
+    private void writeSubsectionIndexForBookIndex( XdocSink sink, IndexEntry sectionIndex, IndexEntry subsectionIndex )
+    {
+        sink.listItem();
+        sink.link( sectionIndex.getId() + ".html#" + HtmlTools.encodeId( subsectionIndex.getId() ) );
+        sink.text( subsectionIndex.getTitle() );
+        sink.link_();
+        sink.listItem_();
+    }
+
+    // -----------------------------------------------------------------------
+    // Rendering
+    // -----------------------------------------------------------------------
+
+    /**
+     * Render the chapter index and all section pages.
+     *
+     * @param chapter the Chapter.
+     * @param context the BookContext.
+     * @param chapterIndex the IndexEntry.
+     * @throws BookDoxiaException if any
+     */
+    private void renderChapter( Chapter chapter, BookContext context, IndexEntry chapterIndex )
+        throws BookDoxiaException
+    {
+        // -----------------------------------------------------------------------
+        // Render the chapter index page
+        // -----------------------------------------------------------------------
+
+        File index = new File( context.getOutputDirectory(), chapter.getId() + ".xml" );
+
+        try
+        {
+            writeChapterIndex( index, chapter, chapterIndex, context );
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while rendering index page to: '"
+                        + index.getAbsolutePath() + "'.", e );
+        }
+
+        // -----------------------------------------------------------------------
+        // Render all section pages
+        // -----------------------------------------------------------------------
+
+        Iterator ii = chapterIndex.getChildEntries().iterator();
+
+        for ( Iterator it = chapter.getSections().iterator(); it.hasNext(); )
+        {
+            Section section = (Section) it.next();
+
+            renderSection( context, section, (IndexEntry) ii.next() );
+        }
+    }
+
+    /**
+     * Write a chapter index
+     *
+     * @param index the File.
+     * @param context the context.
+     * @param chapter the Chapter.
+     * @param chapterIndex the IndexEntry.
+     * @throws IOException if any.
+     */
+    private void writeChapterIndex( File index, Chapter chapter, IndexEntry chapterIndex, BookContext context )
+        throws IOException
+    {
+        Writer writer = WriterFactory.newXmlWriter( index );
+
+        ChapterXdocBookSink sink = new ChapterXdocBookSink( writer, chapterIndex, i18n, context.getLocale() );
+
+        try
+        {
+            // -----------------------------------------------------------------------
+            // Head
+            // -----------------------------------------------------------------------
+
+            sink.head();
+
+            sink.title();
+            sink.text( chapter.getTitle() );
+            sink.title_();
+
+            sink.head_();
+
+            // -----------------------------------------------------------------------
+            // Body
+            // -----------------------------------------------------------------------
+
+            sink.body();
+
+            sink.section1();
+            sink.sectionTitle1();
+            sink.text( chapter.getTitle() );
+            sink.sectionTitle1_();
+
+            sink.list();
+            for ( Iterator it = chapterIndex.getChildEntries().iterator(); it.hasNext(); )
+            {
+                IndexEntry sectionIndex = (IndexEntry) it.next();
+                writeSectionIndexForBookIndex( sink, sectionIndex );
+            }
+            sink.list_();
+
+            sink.section1_();
+
+            sink.body_();
+        }
+        finally
+        {
+            sink.flush();
+
+            sink.close();
+
+            IOUtil.close( writer );
+        }
+    }
+
+    /**
+     * Render all section pages.
+     *
+     * @param context the BookContext.
+     * @param section the Section.
+     * @param sectionIndex the IndexEntry.
+     * @throws BookDoxiaException if any.
+     */
+    private void renderSection( BookContext context, Section section, IndexEntry sectionIndex )
+        throws BookDoxiaException
+    {
+        try
+        {
+            Writer writer = WriterFactory.newXmlWriter( new File( context.getOutputDirectory()
+                    + "/" + section.getId() + ".xml" ) );
+
+            SectionXdocBookSink sink = new SectionXdocBookSink( writer, sectionIndex, i18n, context.getLocale() );
+
+            BookContext.BookFile bookFile = (BookContext.BookFile) context.getFiles().get( section.getId() );
+
+            if ( bookFile == null )
+            {
+                throw new BookDoxiaException( "No document that matches section with id=" + section.getId() + "." );
+            }
+
+            Reader reader = null;
+            try
+            {
+                reader = ReaderFactory.newReader( bookFile.getFile(), context.getInputEncoding() );
+                doxia.parse( reader, bookFile.getParserId(), sink );
+            }
+            catch ( ParserNotFoundException e )
+            {
+                throw new BookDoxiaException( "Parser not found: " + bookFile.getParserId() + ".", e );
+            }
+            catch ( ParseException e )
+            {
+                throw new BookDoxiaException( "Error while parsing document: " + bookFile.getFile().getAbsolutePath()
+                    + ".", e );
+            }
+            catch ( FileNotFoundException e )
+            {
+                throw new BookDoxiaException( "Could not find document: " + bookFile.getFile().getAbsolutePath() + ".",
+                                              e );
+            }
+            finally
+            {
+                sink.flush();
+                sink.close();
+
+                IOUtil.close( reader );
+                IOUtil.close( writer );
+            }
+        }
+        catch ( IOException e )
+        {
+            throw new BookDoxiaException( "Error while rendering book.", e );
+        }
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSink.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSink.java
new file mode 100644
index 0000000..dfb9fe4
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSink.java
@@ -0,0 +1,270 @@
+package org.apache.maven.doxia.book.services.renderer.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.module.docbook.DocBookSink;
+
+/**
+ * An Docbook Sink that doesn't write out head or body elements for every section of a book, and has some convenience
+ * methods relating to the construction of a Doxia Book.
+ *
+ * @author Dave Syer
+ * @version $Id: DocBookBookSink.java 808201 2009-08-26 22:04:43Z vsiveton $
+ * @since 1.1
+ */
+public class DocBookBookSink
+    extends DocBookSink
+{
+    /** Indicates if we're inside a head. */
+    private boolean hasHead = false;
+
+    /**
+     * Construct a new DocBookSink.
+     *
+     * @param out the writer for the sink.
+     */
+    public DocBookBookSink( Writer out )
+    {
+        super( out );
+
+        setSystemId( "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" );
+        setPublicId( "-//OASIS//DTD DocBook V4.4//EN" );
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing because we don't want the header from each document to crop up in the middle of a book.
+     */
+    public void head()
+    {
+        // noop
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing because we don't want the header from each document to crop up in the middle of a book.
+     */
+    public void head_()
+    {
+        // noop
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Marks the skip flag to true so that this element's text is not emitted by the base class.
+     */
+    public void title()
+    {
+        setSkip( true );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Marks the skip flag to false so that rendering can resume.
+     */
+    public void title_()
+    {
+        setSkip( false );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Marks the skip flag to true so that this element's text is not emitted by the base class.
+     */
+    public void author()
+    {
+        setSkip( true );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Marks the skip flag to false so that rendering can resume.
+     */
+    public void author_()
+    {
+        setSkip( false );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing because we don't want the header from each document to crop up in the middle of a book.
+     */
+    public void body()
+    {
+        // noop
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing because we don't want the header from each document to crop up in the middle of a book.
+     */
+    public void body_()
+    {
+        // noop
+    }
+
+    /**
+     * Emit the start tag for the book.
+     *
+     * @see org.apache.maven.doxia.module.docbook.DocbookMarkup#BOOK_TAG
+     */
+    public void book()
+    {
+        init();
+
+        MutableAttributeSet att = writeXmlHeader( "book" );
+
+        writeStartTag( BOOK_TAG, att );
+
+    }
+
+    /**
+     * Emit the end tag for the book.
+     *
+     * @see org.apache.maven.doxia.module.docbook.DocbookMarkup#BOOK_TAG
+     */
+    public void book_()
+    {
+        writeEndTag( BOOK_TAG );
+        flush();
+    }
+
+    /** If no header matter has yet been encountered emit the book info start tag. */
+    private void bookHead()
+    {
+        if ( !hasHead )
+        {
+            writeStartTag( BOOKINFO_TAG );
+            hasHead = true;
+        }
+    }
+
+    /**
+     * If some header matter has been encountered emit the book info end tag.
+     */
+    public void bookHead_()
+    {
+        if ( hasHead )
+        {
+            writeEndTag( BOOKINFO_TAG );
+            hasHead = false;
+        }
+    }
+
+    /**
+     * Emit the title start tag for the whole book.
+     */
+    public void bookTitle()
+    {
+        bookHead();
+        writeStartTag( Tag.TITLE );
+    }
+
+    /**
+     * Emit the title end tag for the whole book.
+     */
+    public void bookTitle_()
+    {
+        super.title_();
+    }
+
+    /**
+     * Emit the author start tag for the whole book.
+     */
+    public void bookAuthor()
+    {
+        bookHead();
+        super.author();
+    }
+
+   /**
+    * Emit the author end tag for the whole book.
+    */
+   public void bookAuthor_()
+    {
+        super.author_();
+    }
+
+   /**
+    * Emit the date start tag for the whole book.
+    */
+   public void bookDate()
+    {
+        bookHead();
+        super.date();
+    }
+
+    /**
+     * Emit the date end tag for the whole book.
+     */
+    public void bookDate_()
+    {
+        super.date_();
+    }
+
+    /**
+     * Emit the chapter start tag.
+     */
+    public void chapter()
+    {
+        writeStartTag( CHAPTER_TAG );
+    }
+
+    /**
+     * Emit the chapter end tag.
+     */
+    public void chapter_()
+    {
+        writeEndTag( CHAPTER_TAG );
+    }
+
+    /**
+     * Emit the chapter title start tag.
+     */
+    public void chapterTitle()
+    {
+        writeStartTag( Tag.TITLE );
+    }
+
+    /**
+     * Emit the chapter title end tag.
+     */
+    public void chapterTitle_()
+    {
+        writeEndTag( Tag.TITLE );
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/latex/LatexBookSink.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/latex/LatexBookSink.java
new file mode 100644
index 0000000..7d27be2
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/latex/LatexBookSink.java
@@ -0,0 +1,124 @@
+package org.apache.maven.doxia.book.services.renderer.latex;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.latex.LatexSink;
+
+import java.io.Writer;
+
+/**
+ * <p>LatexBookSink class.</p>
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: LatexBookSink.java 769433 2009-04-28 15:26:55Z ltheussl $
+ */
+public class LatexBookSink
+    extends LatexSink
+{
+    /** text. */
+    private String txt;
+
+    /** title. */
+    private String title;
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * Construct a new LatexBookSink which is a LatexSink with the given writer,
+     * null sinkCommands, null preamble and fragmentDocument = true.
+     *
+     * @param out the writer for the sink.
+     */
+    public LatexBookSink( Writer out )
+    {
+        super( out, null, null, true );
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    protected String getDocumentStart()
+    {
+        return "";
+//        return "\\documentclass{book}";
+    }
+
+    /** {@inheritDoc} */
+    protected String getDocumentBegin()
+    {
+        return null;
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        this.txt = text;
+
+        super.text( text );
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        super.title_();
+
+        this.title = txt;
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * Return the title.
+     *
+     * @return String.
+     */
+    public String getTitle()
+    {
+        return title;
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * Writes the default LaTeX commands and preamble in the main book file.
+     *
+     * @since 1.1
+     */
+    public void defaultBookPreamble()
+    {
+        markup( defaultSinkCommands() );
+        markup( defaultPreamble() );
+        flush();
+    }
+
+
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/AbstractXdocBookSink.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/AbstractXdocBookSink.java
new file mode 100644
index 0000000..65d3d06
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/AbstractXdocBookSink.java
@@ -0,0 +1,126 @@
+package org.apache.maven.doxia.book.services.renderer.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+import java.util.Locale;
+
+import org.apache.maven.doxia.module.xdoc.XdocSink;
+
+import org.codehaus.plexus.i18n.I18N;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Abstract <code>XdocSink</code> implementation for book.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractXdocBookSink.java 808201 2009-08-26 22:04:43Z vsiveton $
+ */
+public abstract class AbstractXdocBookSink
+    extends XdocSink
+{
+    /** I18N for localized messages. */
+    private final I18N i18n;
+
+    /** The wanted locale */
+    private final Locale locale;
+
+    /**
+     * Default constructor.
+     *
+     * @param out a Writer.
+     * @param i18n I18N.
+     * @param locale the wanted locale.
+     */
+    public AbstractXdocBookSink( Writer out, I18N i18n, Locale locale )
+    {
+        super( out );
+
+        this.i18n = i18n;
+        this.locale = locale;
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        writeStartTag( BODY );
+
+        write( "<section name=\"\">" );
+
+        navigationPanel();
+        horizontalRule();
+
+        write( "</section>" );
+    }
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        write( "<section name=\"\">" );
+
+        horizontalRule();
+
+        navigationPanel();
+
+        write( "</section>" );
+
+        writeEndTag( BODY );
+
+        writeEndTag( DOCUMENT_TAG );
+
+        flush();
+
+        close();
+
+        init();
+    }
+
+    // -----------------------------------------------------------------------
+    // Protected
+    // -----------------------------------------------------------------------
+
+    /**
+     * Gets a trimmed String for the given key from the resource bundle defined by Plexus.
+     *
+     * @param key the key for the desired string
+     * @return the string for the given key
+     */
+    protected String getString( String key )
+    {
+        if ( StringUtils.isEmpty( key ) )
+        {
+            throw new IllegalArgumentException( "The key cannot be empty" );
+        }
+
+        return i18n.getString( "book-renderer", locale, key ).trim();
+    }
+
+    /**
+     * Add a navigation panel.
+     */
+    protected abstract void navigationPanel();
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/ChapterXdocBookSink.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/ChapterXdocBookSink.java
new file mode 100644
index 0000000..f3cc936
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/ChapterXdocBookSink.java
@@ -0,0 +1,151 @@
+package org.apache.maven.doxia.book.services.renderer.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+import java.util.Locale;
+
+import org.apache.maven.doxia.index.IndexEntry;
+import org.codehaus.plexus.i18n.I18N;
+
+/**
+ * A <code>XdocSink</code> implementation for chapter in a book.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ChapterXdocBookSink.java 782330 2009-06-07 05:55:26Z ltheussl $
+ */
+public class ChapterXdocBookSink
+    extends AbstractXdocBookSink
+{
+   /** the chapter IndexEntry. */
+    private final IndexEntry chapterIndex;
+
+    /**
+     * Default constructor.
+     *
+     * @param out the Writer.
+     * @param chapterIndex the chapter IndexEntry.
+     * @param i18n I18N.
+     * @param locale wanted locale.
+     */
+    public ChapterXdocBookSink( Writer out, IndexEntry chapterIndex, I18N i18n, Locale locale )
+    {
+        super( out, i18n, locale );
+
+        this.chapterIndex = chapterIndex;
+    }
+
+    /** {@inheritDoc} */
+    protected void navigationPanel()
+    {
+        write( "<!--Navigation Panel-->" );
+
+        write( "<table width=\"100%\" align=\"center\">" );
+        write( "<tr>" );
+
+        // -----------------------------------------------------------------------
+        // Prev
+        // -----------------------------------------------------------------------
+
+        IndexEntry prevChapter = chapterIndex.getPrevEntry();
+
+        write( "<td><div align=\"left\">" );
+
+        previous( prevChapter );
+
+        write( "</div></td>" );
+
+        // -----------------------------------------------------------------------
+        // Parent
+        // -----------------------------------------------------------------------
+
+        write( "<td><div align=\"center\">" );
+        up();
+        write( "</div></td>" );
+
+        // -----------------------------------------------------------------------
+        // Next
+        // -----------------------------------------------------------------------
+
+        write( "<td><div align=\"right\">" );
+
+        next();
+
+        write( "</div></td>" );
+
+        write( "</tr>" );
+        write( "</table>" );
+
+        write( "<!--End of Navigation Panel-->" );
+    }
+
+    /**
+     * Add previous link.
+     *
+     * @param prevChapter the previous IndexEntry.
+     */
+    protected void previous( IndexEntry prevChapter )
+    {
+        if ( prevChapter != null )
+        {
+            IndexEntry lastEntry = prevChapter.getLastEntry();
+            if ( lastEntry == null )
+            {
+                write( "<i>Start of book</i>" );
+            }
+            else
+            {
+                write( getString( "previous" ) + ": <a href=\"" + lastEntry.getId() + ".html\">" );
+                content( lastEntry.getTitle() );
+                write( "</a>" );
+            }
+        }
+        else
+        {
+            write( getString( "previous" ) + ":<a href=\"index.html\">" + getString( "toc" ) + "</a>" );
+        }
+    }
+
+    /**
+     * Add parent/up link.
+     */
+    protected void up()
+    {
+        write( getString( "up" ) + ": <a href=\"index.html\">" + getString( "toc" ) + "</a>" );
+    }
+
+    /**
+     * Add next link
+     */
+    protected void next()
+    {
+        IndexEntry firstEntry = chapterIndex.getFirstEntry();
+        if ( firstEntry == null )
+        {
+            write( "<i>End of book</i>" );
+        }
+        else
+        {
+            write( getString( "next" ) + ": <a href=\"" + firstEntry.getId() + ".html\">" );
+            content( firstEntry.getTitle() );
+            write( "</a>" );
+        }
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/IndexXdocBookSink.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/IndexXdocBookSink.java
new file mode 100644
index 0000000..366b706
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/IndexXdocBookSink.java
@@ -0,0 +1,84 @@
+package org.apache.maven.doxia.book.services.renderer.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+import java.util.Locale;
+
+import org.apache.maven.doxia.index.IndexEntry;
+import org.codehaus.plexus.i18n.I18N;
+
+/**
+ * A <code>XdocSink</code> implementation for index book.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: IndexXdocBookSink.java 782330 2009-06-07 05:55:26Z ltheussl $
+ */
+public class IndexXdocBookSink
+    extends AbstractXdocBookSink
+{
+    /** the first IndexEntry. */
+    private final IndexEntry firstEntry;
+
+    /**
+     * Default constructor.
+     *
+     * @param out the Writer.
+     * @param firstEntry the first IndexEntry.
+     * @param i18n I18N.
+     * @param locale wanted locale.
+     */
+    public IndexXdocBookSink( Writer out, IndexEntry firstEntry, I18N i18n, Locale locale )
+    {
+        super( out, i18n, locale );
+
+        this.firstEntry = firstEntry;
+    }
+
+    /** {@inheritDoc} */
+    protected void navigationPanel()
+    {
+        write( "<!--Navigation Panel-->" );
+
+        write( "<table width=\"100%\" align=\"center\">" );
+        write( "<tr>" );
+
+        // -----------------------------------------------------------------------
+        // Next
+        // -----------------------------------------------------------------------
+
+        if ( firstEntry != null )
+        {
+            write( "<td><div align=\"right\">" );
+
+            write( getString( "next" ) + ": <a href=\"" + firstEntry.getId() + ".html\">" );
+            content( firstEntry.getTitle() );
+            write( "</a>" );
+
+            write( "</div></td>" );
+        }
+
+        write( "</tr>" );
+        write( "</table>" );
+
+        write( "<!--End of Navigation Panel-->" );
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/SectionXdocBookSink.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/SectionXdocBookSink.java
new file mode 100644
index 0000000..8ec8ed6
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/SectionXdocBookSink.java
@@ -0,0 +1,166 @@
+package org.apache.maven.doxia.book.services.renderer.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+import java.util.Locale;
+
+import org.apache.maven.doxia.index.IndexEntry;
+import org.codehaus.plexus.i18n.I18N;
+
+/**
+ * A <code>XdocSink</code> implementation for section in a book
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: SectionXdocBookSink.java 782330 2009-06-07 05:55:26Z ltheussl $
+ */
+public class SectionXdocBookSink
+    extends AbstractXdocBookSink
+{
+    /** indexEntry. */
+    private final IndexEntry indexEntry;
+
+    /**
+     * Default constructor.
+     *
+     * @param out the Writer to use.
+     * @param indexEntry the IndexEntry.
+     * @param i18n the I18N.
+     * @param locale wanted locale.
+     */
+    public SectionXdocBookSink( Writer out, IndexEntry indexEntry, I18N i18n, Locale locale )
+    {
+        super( out, i18n, locale );
+
+        this.indexEntry = indexEntry;
+    }
+
+    /** {@inheritDoc} */
+    protected void navigationPanel()
+    {
+        write( "<!--Navigation Panel-->" );
+
+        write( "<table width=\"100%\" align=\"center\" border=\"0\">" );
+        write( "<tr>" );
+
+        IndexEntry parent = indexEntry.getParent();
+
+        // -----------------------------------------------------------------------
+        // Prev
+        // -----------------------------------------------------------------------
+
+        IndexEntry prevEntry = indexEntry.getPrevEntry();
+
+        write( "<td align=\"left\">" );
+
+        previous( parent, prevEntry );
+
+        write( "</td>" );
+
+        // -----------------------------------------------------------------------
+        // Parent
+        // -----------------------------------------------------------------------
+
+        write( "<td align=\"center\">" );
+        up( parent );
+        write( "</td>" );
+
+        // -----------------------------------------------------------------------
+        // Next
+        // -----------------------------------------------------------------------
+
+        IndexEntry nextEntry = indexEntry.getNextEntry();
+
+        write( "<td align=\"right\">" );
+
+        next( parent, nextEntry );
+
+        write( "</td>" );
+
+        write( "</tr>" );
+        write( "</table>" );
+
+        write( "<!--End of Navigation Panel-->" );
+    }
+
+    /**
+     * Add previous link.
+     *
+     * @param parent the parent IndexEntry.
+     * @param prevEntry the previous IndexEntry.
+     */
+    protected void previous( IndexEntry parent, IndexEntry prevEntry )
+    {
+        if ( prevEntry != null )
+        {
+            write( getString( "previous" ) + ": <a href=\"" + prevEntry.getId() + ".html\">" );
+            content( prevEntry.getTitle() );
+            write( "</a>" );
+        }
+        else
+        {
+            write( getString( "previous" ) + ": <a href=\"" + parent.getId() + ".html\">" );
+            content( parent.getTitle() );
+            write( "</a>" );
+        }
+    }
+
+    /**
+     * Add parent/up link.
+     *
+     * @param parent the parent IndexEntry.
+     * @see org.apache.maven.doxia.book.services.renderer.xdoc.ChapterXdocBookSink#up()
+     */
+    protected void up( IndexEntry parent )
+    {
+        write( getString( "up" ) + ": <a href=\"" + parent.getId() + ".html\">" + parent.getTitle() + "</a>" );
+    }
+
+    /**
+     * Add next link.
+     *
+     * @param parent the parent IndexEntry.
+     * @param nextEntry the next IndexEntry.
+     */
+    protected void next( IndexEntry parent, IndexEntry nextEntry )
+    {
+        if ( nextEntry != null )
+        {
+            write( getString( "next" ) + ": <a href=\"" + nextEntry.getId() + ".html\">" );
+            content( nextEntry.getTitle() );
+            write( "</a>" );
+        }
+        else
+        {
+            IndexEntry nextChapter = parent.getNextEntry();
+
+            if ( nextChapter == null )
+            {
+                write( "<i>End of book</i>" );
+            }
+            else
+            {
+                write( getString( "next" ) + ": <a href=\"" + nextChapter.getId() + ".html\">" );
+                content( nextChapter.getTitle() );
+                write( "</a>" );
+            }
+        }
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xhtml/XhtmlBookSink.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xhtml/XhtmlBookSink.java
new file mode 100644
index 0000000..f670176
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/renderer/xhtml/XhtmlBookSink.java
@@ -0,0 +1,248 @@
+package org.apache.maven.doxia.book.services.renderer.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.xhtml.XhtmlSink;
+import org.apache.maven.doxia.sink.render.RenderingContext;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.io.Writer;
+
+/**
+ * An Xhtml Sink that doesn't write out head or body elements.
+ *
+ * @author ltheussl
+ * @version $Id: XhtmlBookSink.java 808201 2009-08-26 22:04:43Z vsiveton $
+ */
+public class XhtmlBookSink
+    extends XhtmlSink
+{
+    private RenderingContext renderingContext;
+
+    /**
+     * Construct a new XhtmlBookSink.
+     *
+     * @param out the writer for the sink.
+     * @param context the RenderingContext.
+     */
+    public XhtmlBookSink( Writer out, RenderingContext context )
+    {
+        super( out );
+        this.renderingContext = context;
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing.
+     */
+    public void head()
+    {
+        init();
+
+        setHeadFlag( true );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing.
+     */
+    public void head_()
+    {
+        setHeadFlag( false );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing.
+     */
+    public void title()
+    {
+        // noop
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing.
+     */
+    public void title_()
+    {
+        resetTextBuffer();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing.
+     */
+    public void author_()
+    {
+        resetTextBuffer();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing.
+     */
+    public void date_()
+    {
+        resetTextBuffer();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing.
+     */
+    public void body()
+    {
+        // noop
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Does nothing.
+     */
+    public void body_()
+    {
+        // noop
+    }
+
+    /**
+     * Calls super.head().
+     */
+    public void bookHead()
+    {
+        super.head();
+    }
+
+    /**
+     * Calls super.head_().
+     */
+    public void bookHead_()
+    {
+        super.head_();
+    }
+
+    /**
+     * Calls super.title().
+     */
+    public void bookTitle()
+    {
+        super.title();
+    }
+
+    /**
+     * Calls super.title_().
+     */
+    public void bookTitle_()
+    {
+        super.title_();
+    }
+
+    /**
+     * Calls super.author().
+     */
+    public void bookAuthor()
+    {
+        super.author();
+    }
+
+    /**
+     * Calls super.author_().
+     */
+    public void bookAuthor_()
+    {
+        super.author_();
+    }
+
+    /**
+     * Calls super.date().
+     */
+    public void bookDate()
+    {
+        super.date();
+    }
+
+    /**
+     * Calls super.date_().
+     */
+    public void bookDate_()
+    {
+        super.date_();
+    }
+
+    /**
+     * Calls super.body().
+     */
+    public void bookBody()
+    {
+        super.body();
+    }
+
+    /**
+     * Calls super.body_().
+     */
+    public void bookBody_()
+    {
+        super.body_();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle()
+    {
+        writeStartTag( H1 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_()
+    {
+        writeEndTag( H1 );
+    }
+
+    /** {@inheritDoc} */
+    protected void write( String text )
+    {
+        if ( renderingContext != null )
+        {
+            String relativePathToBasedir = renderingContext.getRelativePath();
+
+            if ( relativePathToBasedir == null )
+            {
+                text = StringUtils.replace( text, "$relativePath", "." );
+            }
+            else
+            {
+                text = StringUtils.replace( text, "$relativePath", relativePathToBasedir );
+            }
+        }
+
+        super.write( text );
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/BookValidator.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/BookValidator.java
new file mode 100644
index 0000000..f9193dc
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/BookValidator.java
@@ -0,0 +1,42 @@
+package org.apache.maven.doxia.book.services.validation;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.model.BookModel;
+
+/**
+ * Ensure a BookModel is valid.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: BookValidator.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+public interface BookValidator
+{
+    /** The plexus lookup role. */
+    String ROLE = BookValidator.class.getName();
+
+    /**
+     * Validate a BookModel.
+     *
+     * @param book the BookModel to validate.
+     * @return ValidationResult
+     */
+    ValidationResult validateBook( BookModel book );
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/DefaultBookValidator.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/DefaultBookValidator.java
new file mode 100644
index 0000000..1026cd1
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/DefaultBookValidator.java
@@ -0,0 +1,108 @@
+package org.apache.maven.doxia.book.services.validation;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.codehaus.plexus.util.StringUtils;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.model.Chapter;
+
+import java.util.Iterator;
+
+/**
+ * Default implementation of BookValidator.
+ *
+ * @plexus.component
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: DefaultBookValidator.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+public class DefaultBookValidator
+    extends AbstractLogEnabled
+    implements BookValidator
+{
+    // ----------------------------------------------------------------------
+    // BookValidator Implementation
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public ValidationResult validateBook( BookModel book )
+    {
+        ValidationResult result = new ValidationResult();
+
+        if ( StringUtils.isEmpty( book.getId() ) )
+        {
+            result.getErrors().add( "Book is missing id." );
+        }
+
+        if ( StringUtils.isEmpty( book.getTitle() ) )
+        {
+            result.getErrors().add( "Book is missing title." );
+        }
+
+        if ( book.getChapters().size() == 0 )
+        {
+            result.getErrors().add( "The book must have at least one chaper" );
+        }
+        else
+        {
+            for ( Iterator it = book.getChapters().iterator(); it.hasNext(); )
+            {
+                Chapter chapter = (Chapter) it.next();
+
+                validateChapter( result, chapter );
+
+                // TODO: Validate the chapter id
+            }
+        }
+
+        return result;
+    }
+
+    // ----------------------------------------------------------------------
+    // Private
+    // ----------------------------------------------------------------------
+
+    /**
+     * Validate a Chapter.
+     *
+     * @param result the ValidationResult to receive the results.
+     * @param chapter the chapter to validate.
+     */
+    private void validateChapter( ValidationResult result, Chapter chapter )
+    {
+        if ( StringUtils.isEmpty( chapter.getId() ) )
+        {
+            result.getErrors().add( "Each chapter has to have an id." );
+
+            return;
+        }
+
+        if ( StringUtils.isEmpty( chapter.getTitle() ) )
+        {
+            result.getErrors().add( "Missing title. Chapter id: " + chapter.getId() );
+        }
+
+        if ( chapter.getSections().size() == 0 )
+        {
+            result.getErrors().add( "Chapter doesn't have any sections. Chapter id: " + chapter.getId() );
+        }
+    }
+}
diff --git a/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/ValidationResult.java b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/ValidationResult.java
new file mode 100644
index 0000000..e86649a
--- /dev/null
+++ b/doxia-book/src/main/java/org/apache/maven/doxia/book/services/validation/ValidationResult.java
@@ -0,0 +1,81 @@
+package org.apache.maven.doxia.book.services.validation;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Encapsulates the result of a validation.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: ValidationResult.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+public class ValidationResult
+{
+    /** If all is OK. */
+    private boolean allOk;
+
+    /** List of errors. */
+    private List errors;
+
+    /** List of warnings. */
+    private List warnings;
+
+    /**
+     * Checks if there were any errors or warnings.
+     *
+     * @return True if there were no errors or warnings.
+     */
+    public boolean isAllOk()
+    {
+        return getErrors().size() == 0 && getWarnings().size() == 0;
+    }
+
+    /**
+     * Return the list of errors.
+     *
+     * @return List. A new ArrayList is constructed if the current List is null.
+     */
+    public List getErrors()
+    {
+        if ( errors == null )
+        {
+            errors = new ArrayList();
+        }
+
+        return errors;
+    }
+
+    /**
+     * Return the list of warnings.
+     *
+     * @return List. A new ArrayList is constructed if the current List is null.
+     */
+    public List getWarnings()
+    {
+        if ( warnings == null )
+        {
+            warnings = new ArrayList();
+        }
+
+        return warnings;
+    }
+}
diff --git a/doxia-book/src/main/modello/book.mdo b/doxia-book/src/main/modello/book.mdo
new file mode 100644
index 0000000..13068f6
--- /dev/null
+++ b/doxia-book/src/main/modello/book.mdo
@@ -0,0 +1,537 @@
+<?xml version="1.0"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<model xmlns="http://modello.codehaus.org/MODELLO/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://modello.codehaus.org/MODELLO/1.0.0 http://modello.codehaus.org/xsd/modello-1.0.0.xsd"
+  xml.namespace="http://maven.apache.org/BOOK/${version}">
+  <id>book</id>
+  <name>BookModel</name>
+  <description>
+    <![CDATA[
+    <p>
+      This descriptor specifies the metadatas and the content for a book.
+    </p>
+    <p>
+      A book is defined by a collection of chapters, a chapter by a
+      collection of sections, a section by a file.
+    </p>
+    <p>An XSD is available at:</p>
+    <ul>
+      <li><a href="http://maven.apache.org/xsd/book-1.0.0.xsd">http://maven.apache.org/xsd/book-1.0.0.xsd</a>.</li>
+    </ul>
+    ]]>
+  </description>
+  <defaults>
+    <default>
+      <key>package</key>
+      <value>org.apache.maven.doxia.book.model</value>
+    </default>
+  </defaults>
+  <classes>
+    <class rootElement="true" xml.tagName="book">
+      <name>BookModel</name>
+      <description>
+        Describes the book layout and packaging.
+      </description>
+      <version>1.0.0</version>
+      <fields>
+        <field>
+          <name>id</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+          <description>
+            Specifies the id of this book. This is a symbolic name for a
+            particular book from this project.
+          </description>
+        </field>
+        <field>
+          <name>title</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <description>
+            Specifies the title of this book.
+          </description>
+        </field>
+        <field>
+          <name>author</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <description>
+            Specifies the author of this book.
+          </description>
+        </field>
+        <field>
+          <name>date</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <description>
+            Specifies the date of this book.
+          </description>
+        </field>
+        <field>
+          <name>chapters</name>
+          <version>1.0.0</version>
+          <description>
+            Specifies a collection of chapters.
+          </description>
+          <association>
+            <type>Chapter</type>
+            <multiplicity>*</multiplicity>
+          </association>
+        </field>
+<!--
+        <field>
+          <name>bannerLeft</name>
+          <description>Banner logo on the masthead of the site to the left.</description>
+          <version>1.0.0</version>
+          <association>
+            <type>Banner</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>bannerRight</name>
+          <description>Banner logo on the masthead of the site to the right.</description>
+          <version>1.0.0</version>
+          <association>
+            <type>Banner</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>publishDate</name>
+          <description>Modify the date published display properties.</description>
+          <version>1.0.0</version>
+          <association>
+            <type>PublishDate</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>version</name>
+          <description>Modify the version display properties.</description>
+          <version>1.0.0</version>
+          <association>
+            <type>Version</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>poweredBy</name>
+          <description>Powered by logos.</description>
+          <version>1.0.0</version>
+          <association xml.tagName="logo">
+            <type>Logo</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>skin</name>
+          <description>The artifact containing the skin for the site</description>
+          <version>1.0.0</version>
+          <association>
+            <type>Skin</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>body</name>
+          <description>The main site content decoration.</description>
+          <version>1.0.0</version>
+          <association>
+            <type>Body</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>custom</name>
+          <description>Custom configuration for use with customised velocity templates.</description>
+          <version>1.0.0</version>
+          <type>DOM</type>
+          <identifier>true</identifier>
+        </field>
+-->
+      </fields>
+    </class>
+    <class>
+      <name>Chapter</name>
+      <version>1.0.0</version>
+      <fields>
+        <field>
+          <name>id</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+          <description>
+            Specifies the id of this chapter. This is a symbolic name for a
+            particular chapter.
+          </description>
+        </field>
+        <field>
+          <name>title</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <description>
+            Specifies the title of this chapter.
+          </description>
+        </field>
+        <field>
+          <name>sections</name>
+          <version>1.0.0</version>
+          <description>
+            Specifies a collection of sections.
+          </description>
+          <association>
+            <type>Section</type>
+            <multiplicity>*</multiplicity>
+          </association>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>Section</name>
+      <version>1.0.0</version>
+      <fields>
+        <field>
+          <name>id</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+          <description>
+            Specifies the id of this section. This is a symbolic name for a
+            particular section.
+          </description>
+        </field>
+        <field>
+          <name>title</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <description>
+            Specifies the title of this section.
+          </description>
+        </field>
+        <field>
+          <name>file</name>
+          <version>1.0.0</version>
+          <type>String</type>
+          <description>
+            Specifies the file of this section.
+          </description>
+        </field>
+      </fields>
+    </class>
+<!--
+    <class>
+      <name>Banner</name>
+      <description>A banner logo on the masthead of the site.</description>
+      <version>1.0.0</version>
+      <fields>
+        <field>
+          <name>name</name>
+          <version>1.0.0</version>
+          <description>Description of the banner.</description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>src</name>
+          <version>1.0.0</version>
+          <description>The href of an image for the banner</description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>alt</name>
+          <version>1.0.0</version>
+          <description>Alt description for the banner image.</description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>href</name>
+          <version>1.0.0</version>
+          <description>The href of a link to use for the banner</description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>PublishDate</name>
+      <description>Modify display properties for date published.</description>
+      <version>1.0.0</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>position</name>
+          <description>Where to place the date published (left, right, navigation-top, navigation-bottom,
+            bottom).</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>format</name>
+          <description>Date format to use. The default is MM/dd/yyyy.</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>Version</name>
+      <description>Modify display properties for version.</description>
+      <version>1.0.0</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>position</name>
+          <description>Where to place the date published (left, right, navigation-top, navigation-bottom,
+            bottom).</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>Logo</name>
+      <description>Logo.</description>
+      <version>1.0.0</version>
+      <superClass>LinkItem</superClass>
+      <fields>
+        <field xml.attribute="true">
+          <name>img</name>
+          <description>Logo image href.</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>Body</name>
+      <description>The main content decoration.</description>
+      <version>1.0.0</version>
+      <fields>
+        <field>
+          <name>head</name>
+          <description>Additional content to include in the HEAD block of the generated pages.</description>
+          <version>1.0.0</version>
+          <type>DOM</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>links</name>
+          <description>A list of links to display in the navigation.</description>
+          <version>1.0.0</version>
+          <association xml.tagName="item">
+            <type>LinkItem</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>breadcrumbs</name>
+          <description>A list of breadcrumbs to display in the navigation.</description>
+          <version>1.0.0</version>
+          <association xml.tagName="item">
+            <type>LinkItem</type>
+            <multiplicity>*</multiplicity>
+            <identifier>true</identifier>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>menus</name>
+          <description>Menus to include in the navigation.</description>
+          <version>1.0.0</version>
+          <association xml.itemsStyle="flat">
+            <type>Menu</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>LinkItem</name>
+      <description>A link in the navigation.</description>
+      <version>1.0.0</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>name</name>
+          <description>The name to use for the link.</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>href</name>
+          <description>The href to use for the link.</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>Menu</name>
+      <description>A menu in the navigation.</description>
+      <version>1.0.0</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>name</name>
+          <description>The name to use for the menu.</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>inherit</name>
+          <description>The way in which the menu is inherited.</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>inheritAsRef</name>
+          <description>If this is a reference, setting inheritAsRef means that it will be populated in the project,
+            whereas if it is false it is populated in the parent and then inherited.</description>
+          <version>1.0.0</version>
+          <type>boolean</type>
+        </field>
+        <field xml.attribute="true">
+          <name>ref</name>
+          <description><![CDATA[A reference to a pre-defined menu, such as a <code>reports</code>, <code>modules</code>
+          or <code>parentProject</code>.]]></description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>img</name>
+          <description>Image href.</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>items</name>
+          <description>Menu item.</description>
+          <version>1.0.0</version>
+          <association xml.itemsStyle="flat">
+            <type>MenuItem</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>MenuItem</name>
+      <description>A menu item.</description>
+      <version>1.0.0</version>
+      <superClass>LinkItem</superClass>
+      <fields>
+        <field>
+          <name>description</name>
+          <description>A description of the menu item. This is used on any summary pages for a menu.</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>collapse</name>
+          <description>Whether the item is collapsed by default when it has children elements.</description>
+          <version>1.0.0</version>
+          <type>boolean</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>ref</name>
+          <description><![CDATA[A reference to a pre-defined menu item, such as a report (specified by the report goal
+          name). Any elements explicitly given override those from the pre-defined reference.]]></description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>items</name>
+          <description>Menu item.</description>
+          <version>1.0.0</version>
+          <association xml.itemsStyle="flat">
+            <type>MenuItem</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class>
+      <name>Skin</name>
+      <description>An skin artifact declaration</description>
+      <version>1.0.0</version>
+      <fields>
+        <field>
+          <name>groupId</name>
+          <description>The group ID</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <required>true</required>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>artifactId</name>
+          <description>The artifact ID</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <required>true</required>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>version</name>
+          <description>The version</description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+      <codeSegments>
+        <codeSegment>
+          <version>1.0.0</version>
+          <code>
+            <![CDATA[
+    public static Skin getDefaultSkin()
+    {
+        Skin skin = new Skin();
+        skin.setGroupId( "org.apache.maven.skins" );
+        skin.setArtifactId( "maven-default-skin" );
+        return skin;
+    }
+            ]]>
+          </code>
+        </codeSegment>
+      </codeSegments>
+    </class>
+-->
+  </classes>
+</model>
diff --git a/doxia-book/src/main/resources/book-renderer.properties b/doxia-book/src/main/resources/book-renderer.properties
new file mode 100644
index 0000000..9369897
--- /dev/null
+++ b/doxia-book/src/main/resources/book-renderer.properties
@@ -0,0 +1,21 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+toc=Table Of Content
+previous=Previous
+up=Up
+next=Next
diff --git a/doxia-book/src/main/resources/book-renderer_en.properties b/doxia-book/src/main/resources/book-renderer_en.properties
new file mode 100644
index 0000000..0c479bc
--- /dev/null
+++ b/doxia-book/src/main/resources/book-renderer_en.properties
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# NOTE:
+# This bundle is intentionally empty because English strings are provided by the base bundle via the parent chain. It
+# must be provided nevertheless such that a request for locale "en" will not errorneously pick up the bundle for the
+# JVM's default locale (which need not be "en"). See the method javadoc about
+#   ResourceBundle.getBundle(String, Locale, ClassLoader)
+# for a full description of the lookup strategy.
diff --git a/doxia-book/src/main/resources/book-renderer_fr.properties b/doxia-book/src/main/resources/book-renderer_fr.properties
new file mode 100644
index 0000000..385696e
--- /dev/null
+++ b/doxia-book/src/main/resources/book-renderer_fr.properties
@@ -0,0 +1,21 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+toc=Table des mati�res
+previous=Pr�c�dent
+up=Haut
+next=Suivant
diff --git a/doxia-book/src/site/apt/index.apt b/doxia-book/src/site/apt/index.apt
new file mode 100644
index 0000000..968e955
--- /dev/null
+++ b/doxia-book/src/site/apt/index.apt
@@ -0,0 +1,20 @@
+ -----
+ Writing Books in Doxia
+ -----
+ Lukas Theussl
+ -----
+
+Introduction
+
+ Doxia allows you to write books like user manuals and guides in any format supported by Doxia. Combined with the
+ Doxia Book Maven you are able to include the manuals directly in your generated site with links to the off-line
+ friendly formats like XDoc, PDF, RTF and LaTeX.
+
+* How It Works
+
+ The only thing you need in addition to the content files itself is a simple book descriptor which is used to specify
+ the ordering of the sections and the names for the chapters.
+
+ See {{{./book.html}The Book Descriptor Reference}} for a reference to the descriptor. A sample is given on the
+ main Doxia {{{http://maven.apache.org/doxia/book/index.html}site}}.
+
diff --git a/doxia-book/src/site/apt/usage.apt b/doxia-book/src/site/apt/usage.apt
new file mode 100644
index 0000000..e90d983
--- /dev/null
+++ b/doxia-book/src/site/apt/usage.apt
@@ -0,0 +1,31 @@
+ -----
+ Usage
+ -----
+ Lukas Theussl
+ -----
+
+Usage
+
+ Below is a simple example to illustrate how to use it.
+
++------------------------------------------------------
+        BookDoxia doxia = (BookDoxia) lookup( BookDoxia.ROLE );
+
+        // load the book descriptor
+        File book1 = new File( "book-1.xml" );
+
+        BookModel book = doxia.loadBook( book1 );
+
+        // files to include
+        List files = FileUtils.getFiles( new File( "src/resources/book/" ), "**/*.apt, **/*.xml", "" );
+
+        // render books in different formats
+        doxia.renderBook( book, "pdf", files, new File( "target/itext/" ) );
+        doxia.renderBook( book, "xhtml", files, new File( "target/xhtml/" ) );
+        doxia.renderBook( book, "xdoc", files, new File( "target/xdoc/" ) );
+        doxia.renderBook( book, "latex", files, new File( "target/latex/" ) );
+        doxia.renderBook( book, "doc-book", files, new File( "target/doc-book/" ) );
+        doxia.renderBook( book, "rtf", files, new File( "target/rtf/" ) );
++------------------------------------------------------
+
+ See the {{{./apidocs/index.html}Javadocs}} for more details.
diff --git a/doxia-book/src/site/apt/using-book-xsd.apt b/doxia-book/src/site/apt/using-book-xsd.apt
new file mode 100644
index 0000000..1c3fdc9
--- /dev/null
+++ b/doxia-book/src/site/apt/using-book-xsd.apt
@@ -0,0 +1,41 @@
+ -----
+ Using Schema Book 1.0
+ -----
+ Vincent Siveton
+ ------
+ 2009-01-28
+ ------
+
+~~ Licensed to the Apache Software Foundation (ASF) under one
+~~ or more contributor license agreements.  See the NOTICE file
+~~ distributed with this work for additional information
+~~ regarding copyright ownership.  The ASF licenses this file
+~~ to you under the Apache License, Version 2.0 (the
+~~ "License"); you may not use this file except in compliance
+~~ with the License.  You may obtain a copy of the License at
+~~
+~~   http://www.apache.org/licenses/LICENSE-2.0
+~~
+~~ Unless required by applicable law or agreed to in writing,
+~~ software distributed under the License is distributed on an
+~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+~~ KIND, either express or implied.  See the License for the
+~~ specific language governing permissions and limitations
+~~ under the License.
+
+~~ NOTE: For help with the syntax of this file, see:
+~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Using Schema Book 1.0
+
+  The Decoration XSD is located {{{http://maven.apache.org/xsd/book-1.0.0.xsd}here}}.
+
+  Your favorite IDE probably supports XSD schema's for .xml files. You need to specify the following:
+
++-----+
+<book xmlns="http://maven.apache.org/BOOK/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/BOOK/1.0.0 http://maven.apache.org/xsd/book-1.0.0.xsd">
+...
+</book>
++-----+
diff --git a/doxia-book/src/site/site.xml b/doxia-book/src/site/site.xml
new file mode 100644
index 0000000..1ef2dd2
--- /dev/null
+++ b/doxia-book/src/site/site.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 file:../../../../doxia-sitetools/doxia-decoration-model/target/generated-site/xsd/decoration-1.0.0.xsd">
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu name="About Doxia :: Book Components">
+      <item name="Introduction" href="index.html"/>
+      <item name="Usage" href="usage.html"/>
+      <!--<item name="FAQ" href="faq.html"/>-->
+    </menu>
+
+    <menu name="Schema Book 1.0">
+      <item name="Reference of Schema Book" href="book.html"/>
+      <item name="Using Schema Book" href="using-book-xsd.html"/>
+    </menu>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
diff --git a/doxia-book/src/test/java/org/apache/maven/doxia/book/BookRendererTest.java b/doxia-book/src/test/java/org/apache/maven/doxia/book/BookRendererTest.java
new file mode 100644
index 0000000..7913f9b
--- /dev/null
+++ b/doxia-book/src/test/java/org/apache/maven/doxia/book/BookRendererTest.java
@@ -0,0 +1,75 @@
+package org.apache.maven.doxia.book;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.codehaus.plexus.PlexusTestCase;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.StringUtils;
+import org.apache.maven.doxia.book.model.BookModel;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: BookRendererTest.java 707844 2008-10-25 13:41:29Z vsiveton $
+ */
+public class BookRendererTest
+    extends PlexusTestCase
+{
+    public void testBasic()
+        throws Exception
+    {
+        BookDoxia doxia = (BookDoxia) lookup( BookDoxia.ROLE );
+
+        File book1 = getTestFile( "src/test/resources/book-1.xml" );
+
+        List files = FileUtils.getFiles( getTestFile( "src/test/resources/book-1" ), "**/*.apt, **/*.xml", "" );
+
+        BookModel book = doxia.loadBook( book1 );
+
+        doxia.renderBook( book, "pdf", files, getTestFile( "target/test-output/itext" ) );
+        doxia.renderBook( book, "xhtml", files, getTestFile( "target/test-output/xhtml" ) );
+        doxia.renderBook( book, "xdoc", files, getTestFile( "target/test-output/xdoc" ) );
+        doxia.renderBook( book, "latex", files, getTestFile( "target/test-output/latex" ) );
+        doxia.renderBook( book, "doc-book", files, getTestFile( "target/test-output/doc-book" ) );
+        doxia.renderBook( book, "rtf", files, getTestFile( "target/test-output/rtf" ) );
+
+        assertCorrectDocbook();
+    }
+
+    /**
+     * Regression test for the docbook output.
+     */
+    private void assertCorrectDocbook()
+        throws Exception
+    {
+        String expected =
+            FileUtils.fileRead( getTestFile( "src/test/resources/expected/doc-book/plexus-user-guide.xml" ) );
+        expected = StringUtils.deleteWhitespace( expected );
+
+        String actual =
+            FileUtils.fileRead( getTestFile( "target/test-output/doc-book/plexus-user-guide.xml" ) );
+        actual = StringUtils.deleteWhitespace( actual );
+
+        assertEquals( "Wrong docbook output!",
+            StringUtils.replace( expected, "\r", "" ), StringUtils.replace( actual, "\r", "" ) );
+    }
+}
diff --git a/doxia-book/src/test/java/org/apache/maven/doxia/book/services/indexer/BookIndexerTest.java b/doxia-book/src/test/java/org/apache/maven/doxia/book/services/indexer/BookIndexerTest.java
new file mode 100644
index 0000000..72b2ef5
--- /dev/null
+++ b/doxia-book/src/test/java/org/apache/maven/doxia/book/services/indexer/BookIndexerTest.java
@@ -0,0 +1,118 @@
+package org.apache.maven.doxia.book.services.indexer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.context.BookContext;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.services.io.BookIo;
+import org.apache.maven.doxia.index.IndexEntry;
+import org.apache.maven.doxia.util.HtmlTools;
+import org.codehaus.plexus.PlexusTestCase;
+import org.codehaus.plexus.util.FileUtils;
+
+/**
+ * @author <a href="mailto:trygve.laugstol at objectware.no">Trygve Laugstøl</a>
+ * @version $Id: BookIndexerTest.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+public class BookIndexerTest
+    extends PlexusTestCase
+{
+    public void testBasic()
+        throws Exception
+    {
+        BookIo io = (BookIo) lookup( BookIo.ROLE );
+
+        BookIndexer indexer = (BookIndexer) lookup( BookIndexer.ROLE );
+
+        BookModel book = io.readBook( getTestFile( "src/test/resources/book-1.xml" ) );
+
+        BookContext context = new BookContext();
+
+        io.loadFiles( context, FileUtils.getFiles( getTestFile( "src/test/resources/book-1" ), "*.apt", "" ) );
+
+        indexer.indexBook( book, context );
+
+        IndexEntry root = context.getIndex();
+
+        assertNotNull( root );
+
+        assertEquals( 2, root.getChildEntries().size() );
+
+        IndexEntry c1 = assertIndexEntry( root, 0, "Chapter 1", "chapter-1", 2 );
+
+        // -----------------------------------------------------------------------
+        // Section 1
+        // -----------------------------------------------------------------------
+
+        IndexEntry s1 = assertIndexEntry( c1, 0, "Section 1", "section-1", 5 );
+
+        IndexEntry ss1 = assertIndexEntry( s1, 0, "Subsection 1", "Subsection_1", 1 );
+
+        assertIndexEntry( ss1, 0, "Subsubsection 1", "Subsubsection_1", 0 );
+
+        assertIndexEntry( s1, 1, "Subsection 2", "Subsection_2", 0 );
+
+        assertIndexEntry( s1, 2, "Subsection 3", "Subsection_3", 0 );
+
+        assertIndexEntry( s1, 3, "Subsection 4", "Subsection_4", 0 );
+
+        // -----------------------------------------------------------------------
+        // Section 2
+        // -----------------------------------------------------------------------
+
+        IndexEntry s2 = assertIndexEntry( c1, 1, "Section 2", "section-2", 1 );
+
+        assertIndexEntry( s2, 0, "Section 1.10.32 of \"de Finibus Bonorum et Malorum\", written by Cicero in 45 BC",
+                          "Section_1.10.32_of_\"de_Finibus_Bonorum_et_Malorum\",_written_by_Cicero_in_45_BC", 0 );
+
+        // -----------------------------------------------------------------------
+        // Chapter 2
+        // -----------------------------------------------------------------------
+
+        IndexEntry c2 = assertIndexEntry( root, 1, "Chapter 2", "chapter-2", 2 );
+
+        IndexEntry s3 = assertIndexEntry( c2, 0, "Section 3", "section-3", 1 );
+
+        assertIndexEntry( s3, 0, "1914 translation by H. Rackham", "1914_translation_by_H._Rackham", 0 );
+
+        IndexEntry s4 = assertIndexEntry( c2, 1, "Section 4", "section-4", 1 );
+
+        assertIndexEntry( s4, 0, "Section 1.10.33 of \"de Finibus Bonorum et Malorum\", written by Cicero in 45 BC",
+                          "Section_1.10.33_of_\"de_Finibus_Bonorum_et_Malorum\",_written_by_Cicero_in_45_BC", 0 );
+    }
+
+    private IndexEntry assertIndexEntry( IndexEntry parent, int childIndex, String title, String id, int childCount )
+    {
+        assertTrue( "parent: " + parent.getId() +  ", " +
+            "required count: " + childCount + ", " +
+            "actual count: " + parent.getChildEntries().size(),
+                    childIndex < parent.getChildEntries().size() );
+
+        IndexEntry indexEntry = (IndexEntry) parent.getChildEntries().get( childIndex );
+
+        assertEquals( title, indexEntry.getTitle() );
+
+        assertEquals( HtmlTools.encodeId( id ), indexEntry.getId() );
+
+        assertEquals( childCount, indexEntry.getChildEntries().size() );
+
+        return indexEntry;
+    }
+}
diff --git a/doxia-book/src/test/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSinkTest.java b/doxia-book/src/test/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSinkTest.java
new file mode 100644
index 0000000..6a4de45
--- /dev/null
+++ b/doxia-book/src/test/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSinkTest.java
@@ -0,0 +1,490 @@
+package org.apache.maven.doxia.book.services.renderer.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import java.util.Locale;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+import org.apache.maven.doxia.module.docbook.DocBookSink;
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkUtils;
+
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.codehaus.plexus.util.FileUtils;
+
+/**
+ * Test the book path of the DockBook sink
+ * @author Dave Syer
+ */
+public class DocBookBookSinkTest extends AbstractSinkTest
+{
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "docbook";
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new DocBookBookSink( writer );
+    }
+
+      //
+     // DocBookBookSink specific
+    //
+
+    /**
+     * Checks that the sequence <code>[bookTitle(), text( title ), bookTitle_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getBookTitleBlock getBookTitleBlock}( title ).
+     * NewLines are ignored.
+     */
+    public void testBookTitle()
+    {
+        DocBookBookSink sink = (DocBookBookSink) getSink();
+
+        String title = "Grodek";
+        sink.bookTitle();
+        sink.text( title );
+        sink.bookTitle_();
+        sink.flush();
+
+        String actual = getSinkContent();
+        String expected = getBookTitleBlock( title );
+
+        assertEquals( "Wrong book title!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[bookAuthor(), text( author ), bookAuthor_()]
+     * </code>, invoked on the current sink, produces the same result as
+     * {@link #getBookAuthorBlock getBookAuthorBlock}( author ).
+     * NewLines are ignored.
+     */
+    public void testBookAuthor()
+    {
+        DocBookBookSink sink = (DocBookBookSink) getSink();
+
+        String author = "Georg Trakl";
+        sink.bookAuthor();
+        sink.text( author );
+        sink.bookAuthor_();
+        sink.flush();
+
+        String actual = getSinkContent();
+        String expected = getBookAuthorBlock( author );
+
+        assertEquals( "Wrong book author!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[bookDate(), text( date ), bookDate_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getBookDateBlock getBookDateBlock}( date ). NewLines are ignored.
+     */
+    public void testBookDate()
+    {
+        DocBookBookSink sink = (DocBookBookSink) getSink();
+
+        String date = "1914";
+        sink.bookDate();
+        sink.text( date );
+        sink.bookDate_();
+        sink.flush();
+
+        String actual = getSinkContent();
+        String expected = getBookDateBlock( date );
+
+        assertEquals( "Wrong book date!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[bookHead(), bookHead_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getBookHeadBlock getBookHeadBlock()}. NewLines are ignored.
+     */
+    public void testBookHead()
+    {
+        DocBookBookSink sink = (DocBookBookSink) getSink();
+
+        //sink.bookHead();
+        sink.bookHead_();
+        sink.flush();
+
+        String actual = getSinkContent();
+        String expected = getBookHeadBlock();
+
+        assertEquals( "Wrong book head!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[book(), book_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getBookBlock getBookBlock()}. NewLines are ignored.
+     */
+    public void testBook()
+    {
+        DocBookBookSink sink = (DocBookBookSink) getSink();
+
+        sink.book();
+        sink.book_();
+        sink.flush();
+
+        String actual = getSinkContent();
+        String expected = getBookBlock();
+
+        assertEquals( "Wrong book body!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[chapterTitle(), text( title ),
+     * chapterTitle_()]</code>, invoked on the current sink, produces
+     * the same result as
+     * {@link #getChapterTitleBlock getChapterTitleBlock}( title ).
+     * NewLines are ignored.
+     */
+    public void testChapterTitle()
+    {
+        DocBookBookSink sink = (DocBookBookSink) getSink();
+
+        String title = "Title";
+        sink.chapterTitle();
+        sink.text( title );
+        sink.chapterTitle_();
+        sink.flush();
+
+        String actual = getSinkContent();
+        String expected = getChapterTitleBlock( title );
+
+        assertEquals( "Wrong chapterTitle!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[chapter(), chapter_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getChapterBlock getChapterBlock}().
+     * NewLines are ignored.
+     */
+    public void testChapter()
+    {
+        DocBookBookSink sink = (DocBookBookSink) getSink();
+
+        sink.chapter();
+        sink.chapter_();
+        sink.flush();
+
+        String actual = getSinkContent();
+        String expected = getChapterBlock();
+
+        assertEquals( "Wrong chapter block!", expected, actual );
+    }
+
+    /**
+     * Returns a title block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a title block on the current sink.
+     * @see #testBookTitle()
+     */
+    protected String getBookTitleBlock( String title )
+    {
+        return "<bookinfo><title>" + title + "</title>";
+    }
+
+    /**
+     * Returns an author block generated by this sink.
+     * @param author The author to use.
+     * @return The result of invoking an author block on the current sink.
+     * @see #testBookAuthor()
+     */
+    protected String getBookAuthorBlock( String author )
+    {
+        return "<bookinfo><corpauthor>" + author + "</corpauthor>";
+    }
+
+    /**
+     * Returns a date block generated by this sink.
+     * @param date The date to use.
+     * @return The result of invoking a date block on the current sink.
+     * @see #testBookDate()
+     */
+    protected String getBookDateBlock( String date )
+    {
+        return "<bookinfo><date>" + date + "</date>";
+    }
+
+    /**
+     * Returns a head block generated by this sink.
+     * @return The result of invoking a head block on the current sink.
+     * @see #testBookHead()
+     */
+    protected String getBookHeadBlock()
+    {
+        return "";
+    }
+
+    /**
+     * Returns a body block generated by this sink.
+     * @return The result of invoking a body block on the current sink.
+     * @see #testBook()
+     */
+    protected String getBookBlock()
+    {
+        return "<?xml version=\"1.0\"?><!DOCTYPE book PUBLIC \"" + DocBookSink.DEFAULT_XML_PUBLIC_ID
+            + "\" \"" + DocBookSink.DEFAULT_XML_SYSTEM_ID + "\"><book></book>";
+    }
+
+    /**
+     * Returns a SectionTitle block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a SectionTitle block on the current sink.
+     * @see #testChapterTitle()
+     */
+    protected String getChapterTitleBlock( String title )
+    {
+        return "<title>" + title + "</title>";
+    }
+
+    /**
+     * Returns a Section1 block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a Section1 block on the current sink.
+     * @see #testChapter()
+     */
+    protected String getChapterBlock()
+    {
+        return "<chapter></chapter>";
+    }
+
+
+      //
+     // from DocBookSink
+    //
+
+
+    /** {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        return "<date>" + date + "</date>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return "<title>" + title + "</title>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return "<section><title>" + title + "</title></section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return "<section><title>" + title + "</title></section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        return "<section><title>" + title + "</title></section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        return "<section><title>" + title + "</title></section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        return "<section><title>" + title + "</title></section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return "<itemizedlist><listitem><para>" + item  + "</para></listitem></itemizedlist>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return "<orderedlist numeration=\"lowerroman\"><listitem><para>"
+            + item  + "</para></listitem></orderedlist>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return "<variablelist><varlistentry><term>" + definum
+            + "</term><listitem><para>" + definition
+            + "</para></listitem></varlistentry></variablelist>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        String format = FileUtils.extension( source ).toUpperCase( Locale.ENGLISH );
+
+        return "<mediaobject><imageobject>"
+                + "<imagedata fileref=\"" + source + "\" format=\"" + format + "\" />"
+                + "</imageobject><caption><para>" + caption + "</para></caption></mediaobject>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        // Using the same set ordering than the JVM
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( "frame", "none" );
+        att.addAttribute( "rowsep", "0" );
+        att.addAttribute( "colsep", "0" );
+
+        return "<table" + SinkUtils.getAttributeString( att ) + "><title>" + caption
+            + "</title><tgroup cols=\"1\"><colspec align=\"center\" /><tbody><row><entry>"
+            + cell  + "</entry></row></tbody></tgroup></table>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        return "<para>" + text + "</para>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        return "<programlisting>" + text + "</programlisting>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return "<!-- HR -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return "<!-- PB -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        return "<anchor id=\"" + anchor + "\" />" + anchor + "<!-- anchor_end -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        String linkend = DoxiaUtils.isInternalLink( link ) ? link.substring( 1 ) : link;
+        return "<link linkend=\"" + linkend + "\">" + text + "</link>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        return "<emphasis>" + text + "</emphasis>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        return "<emphasis role=\"bold\">" + text + "</emphasis>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        return "<literal>" + text + "</literal>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return "<!-- LB -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return "&#x00A0;";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        // TODO: retreive those from the sink
+        return "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        // TODO
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "<!-- Simple comment with - - - - -->";
+    }
+}
diff --git a/doxia-book/src/test/resources/book-1.xml b/doxia-book/src/test/resources/book-1.xml
new file mode 100644
index 0000000..7364904
--- /dev/null
+++ b/doxia-book/src/test/resources/book-1.xml
@@ -0,0 +1,51 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<book xmlns="http://maven.apache.org/BOOK/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/BOOK/1.0.0 ../../../target/generated-site/xsd/book-1.0.0.xsd">
+  <id>plexus-user-guide</id>
+  <title>Test Book</title>
+  <chapters>
+    <chapter>
+      <id>chapter-1</id>
+      <title>Chapter 1</title>
+      <sections>
+        <section>
+          <id>section-1</id>
+        </section>
+        <section>
+          <id>section-2</id>
+        </section>
+      </sections>
+    </chapter>
+    <chapter>
+      <id>chapter-2</id>
+      <title>Chapter 2</title>
+      <sections>
+        <section>
+          <id>section-3</id>
+        </section>
+        <section>
+          <id>section-4</id>
+        </section>
+      </sections>
+    </chapter>
+  </chapters>
+</book>
diff --git a/doxia-book/src/test/resources/book-1/section-1.apt b/doxia-book/src/test/resources/book-1/section-1.apt
new file mode 100644
index 0000000..35d204f
--- /dev/null
+++ b/doxia-book/src/test/resources/book-1/section-1.apt
@@ -0,0 +1,61 @@
+ -----
+ Section 1
+ -----
+ Trygve
+ -----
+
+Subsection 1
+
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec sagittis malesuada nisi. Aliquam orci eros, vestibulum
+ eu, placerat et, pretium sed, nisi. Proin consequat. Praesent faucibus sem id arcu hendrerit nonummy. Aliquam blandit
+ accumsan pede. Vivamus aliquet lacinia nunc. Praesent dapibus orci eu magna. Cras nonummy, pede nec facilisis semper,
+ sem nibh vestibulum massa, sed ornare tortor elit non lectus. Nullam mauris turpis, luctus et, vulputate vitae, commodo
+ sit amet, purus. Fusce erat. Proin ullamcorper imperdiet est. Morbi sit amet dui. Fusce bibendum auctor augue. Sed leo
+ sapien, vehicula ac, nonummy id, cursus at, nulla. Ut sed purus.
+
+* Subsubsection 1
+
+ Suspendisse sagittis metus nec leo. Suspendisse velit. Phasellus ipsum dolor, porttitor ut1, varius id, scelerisque
+ vel, ligula. Aliquam tempor sem in pede tincidunt nonummy. Vestibulum et nulla. Nunc et dolor a risus porttitor tem
+ pus. Sed felis arcu, consectetuer non, imperdiet sollicitudin, ullamcorper vitae, nulla. Vestibulum ante ipsum primis
+ in faucibus.
+
+Subsection 2
+
+ Duis eget libero aliquet quam ultrices malesuada. Donec molestie dignissim nunc. Curabitur turpis. Suspendisse a nibh
+ ut elit vulputate ultrices. Etiam nulla erat, nonummy vel, fringilla at, scelerisque non, ante. Suspendisse adipiscing
+ rhoncus purus. Nulla in augue. Ut ac nisi eu nisi cursus elementum. Pellentesque habitant morbi tristique senectus et
+ netus et malesuada fames ac turpis egestas. Donec et turpis. Donec nec mi. Mauris malesuada congue sem. Maecenas et
+ urna in nisi sagittis facilisis. Cras nibh. Aliquam purus. Donec convallis congue libero. Nulla feugiat. Nulla massa
+ libero, consectetuer ac, aliquet ac, consequat eu, purus. Pellentesque eleifend pretium augue.
+
+Subsection 3
+
+ Integer auctor, nisi ut convallis imperdiet, ligula diam sollicitudin dolor, porttitor mattis urna sapien at velit.
+ Fusce vestibulum, neque nec malesuada tempor, tortor nisi accumsan purus, quis faucibus metus elit ac urna. Aliquam
+ commodo velit vel ipsum. Donec blandit diam blandit eros. Aliquam pretium fermentum neque. Sed nec tellus eu orci
+ ullamcorper facilisis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+ Nulla sed leo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce
+ suscipit. Sed sit amet diam ac ante tincidunt ornare. Sed sodales vestibulum quam. Fusce accumsan. Ut ut mi.
+
+Subsection 4
+
+ Maecenas tincidunt lobortis nunc. Phasellus euismod diam sit amet felis. Donec lorem metus, vulputate vitae, ornare
+ vel, molestie sit amet, pede. In erat velit, adipiscing sed, varius in, interdum cursus, enim. Quisque dolor ante,
+ tincidunt vel, congue eget, consectetuer id, nunc. Suspendisse hendrerit. Proin egestas, massa eget egestas
+ ullamcorper, nisl elit gravida magna, vitae dignissim odio velit ut tortor. Fusce lobortis consequat nulla.
+ Vestibulum pretium justo at metus. Sed lorem velit, elementum eget, pellentesque ac, ornare id, mi. Pellentesque vel
+ ligula et erat dictum commodo. Integer malesuada lacus nec metus. Aliquam id purus ac neque mattis venenatis. Aenean
+ lobortis accumsan massa. Donec dui ante, facilisis vel, hendrerit ut, vehicula in, eros. Suspendisse potenti. Sed
+ fringilla. Suspendisse vel nibh. Sed sit amet lacus quis massa tincidunt elementum. Ut ut augue vitae ligula dapibus
+ aliquam.
+
+Subsection 5
+
+ Fusce non eros non lectus venenatis bibendum. Nullam pharetra. Nunc commodo pede et metus. Pellentesque habitant morbi
+ tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum imperdiet nisl nec nulla. Morbi congue
+ dictum pede. Aliquam ligula. In pede nulla, varius a, blandit ut, pulvinar vitae, mauris. Suspendisse sit amet magna.
+ Curabitur cursus placerat justo. Vivamus imperdiet magna commodo mi. Vestibulum eget metus quis sem sollicitudin
+ consectetuer. Morbi metus augue, elementum rutrum, luctus quis, porttitor a, est. Phasellus quis sapien et augue
+ adipiscing fermentum. Sed fermentum tristique dui. Vivamus aliquam, tortor at ultricies commodo, urna ipsum fringilla
+ neque, sit amet congue purus enim a justo.
diff --git a/doxia-book/src/test/resources/book-1/section-2.apt b/doxia-book/src/test/resources/book-1/section-2.apt
new file mode 100644
index 0000000..64c1f5f
--- /dev/null
+++ b/doxia-book/src/test/resources/book-1/section-2.apt
@@ -0,0 +1,16 @@
+ -----
+ Section 2
+ -----
+ Trygve
+ -----
+
+Section 1.10.32 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
+
+ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam,
+ eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam
+ voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione
+ voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim
+ ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi
+ consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur,
+ vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
diff --git a/doxia-book/src/test/resources/book-1/section-3.apt b/doxia-book/src/test/resources/book-1/section-3.apt
new file mode 100644
index 0000000..f5d07ec
--- /dev/null
+++ b/doxia-book/src/test/resources/book-1/section-3.apt
@@ -0,0 +1,17 @@
+ -----
+ Section 3
+ -----
+ Trygve
+ -----
+
+1914 translation by H. Rackham
+
+ But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will
+ give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the
+ master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but
+ because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful.
+ Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because
+ occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial
+ example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who
+ has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who
+ avoids a pain that produces no resultant pleasure?
diff --git a/doxia-book/src/test/resources/book-1/section-4.apt b/doxia-book/src/test/resources/book-1/section-4.apt
new file mode 100644
index 0000000..a148762
--- /dev/null
+++ b/doxia-book/src/test/resources/book-1/section-4.apt
@@ -0,0 +1,16 @@
+ -----
+ Section 4
+ -----
+ Trygve
+ -----
+
+Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
+
+ At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque
+ corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa
+ qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita
+ distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime
+ placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut
+ officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae.
+ Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut
+ perferendis doloribus asperiores repellat.
diff --git a/doxia-book/src/test/resources/expected/doc-book/plexus-user-guide.xml b/doxia-book/src/test/resources/expected/doc-book/plexus-user-guide.xml
new file mode 100644
index 0000000..37ed826
--- /dev/null
+++ b/doxia-book/src/test/resources/expected/doc-book/plexus-user-guide.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0"?><!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<book><bookinfo><title>Test Book</title>
+</bookinfo>
+<chapter><title>Chapter 1</title>
+<section><title>Subsection 1</title>
+<para>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec sagittis
+malesuada nisi. Aliquam orci eros, vestibulum eu, placerat et, pretium sed,
+nisi. Proin consequat. Praesent faucibus sem id arcu hendrerit nonummy.
+Aliquam blandit accumsan pede. Vivamus aliquet lacinia nunc. Praesent dapibus
+orci eu magna. Cras nonummy, pede nec facilisis semper, sem nibh vestibulum
+massa, sed ornare tortor elit non lectus. Nullam mauris turpis, luctus et,
+vulputate vitae, commodo sit amet, purus. Fusce erat. Proin ullamcorper
+imperdiet est. Morbi sit amet dui. Fusce bibendum auctor augue. Sed leo
+sapien, vehicula ac, nonummy id, cursus at, nulla. Ut sed purus.</para>
+<section><title>Subsubsection 1</title>
+<para>Suspendisse sagittis metus nec leo. Suspendisse velit. Phasellus ipsum
+dolor, porttitor ut1, varius id, scelerisque vel, ligula. Aliquam tempor sem
+in pede tincidunt nonummy. Vestibulum et nulla. Nunc et dolor a risus
+porttitor tem pus. Sed felis arcu, consectetuer non, imperdiet sollicitudin,
+ullamcorper vitae, nulla. Vestibulum ante ipsum primis in faucibus.</para>
+</section>
+</section>
+<section><title>Subsection 2</title>
+<para>Duis eget libero aliquet quam ultrices malesuada. Donec molestie
+dignissim nunc. Curabitur turpis. Suspendisse a nibh ut elit vulputate
+ultrices. Etiam nulla erat, nonummy vel, fringilla at, scelerisque non, ante.
+Suspendisse adipiscing rhoncus purus. Nulla in augue. Ut ac nisi eu nisi
+cursus elementum. Pellentesque habitant morbi tristique senectus et netus et
+malesuada fames ac turpis egestas. Donec et turpis. Donec nec mi. Mauris
+malesuada congue sem. Maecenas et urna in nisi sagittis facilisis. Cras nibh.
+Aliquam purus. Donec convallis congue libero. Nulla feugiat. Nulla massa
+libero, consectetuer ac, aliquet ac, consequat eu, purus. Pellentesque
+eleifend pretium augue.</para>
+</section>
+<section><title>Subsection 3</title>
+<para>Integer auctor, nisi ut convallis imperdiet, ligula diam sollicitudin
+dolor, porttitor mattis urna sapien at velit. Fusce vestibulum, neque nec
+malesuada tempor, tortor nisi accumsan purus, quis faucibus metus elit ac
+urna. Aliquam commodo velit vel ipsum. Donec blandit diam blandit eros.
+Aliquam pretium fermentum neque. Sed nec tellus eu orci ullamcorper facilisis.
+Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac
+turpis egestas. Nulla sed leo. Class aptent taciti sociosqu ad litora torquent
+per conubia nostra, per inceptos hymenaeos. Fusce suscipit. Sed sit amet diam
+ac ante tincidunt ornare. Sed sodales vestibulum quam. Fusce accumsan. Ut ut
+mi.</para>
+</section>
+<section><title>Subsection 4</title>
+<para>Maecenas tincidunt lobortis nunc. Phasellus euismod diam sit amet felis.
+Donec lorem metus, vulputate vitae, ornare vel, molestie sit amet, pede. In
+erat velit, adipiscing sed, varius in, interdum cursus, enim. Quisque dolor
+ante, tincidunt vel, congue eget, consectetuer id, nunc. Suspendisse
+hendrerit. Proin egestas, massa eget egestas ullamcorper, nisl elit gravida
+magna, vitae dignissim odio velit ut tortor. Fusce lobortis consequat nulla.
+Vestibulum pretium justo at metus. Sed lorem velit, elementum eget,
+pellentesque ac, ornare id, mi. Pellentesque vel ligula et erat dictum
+commodo. Integer malesuada lacus nec metus. Aliquam id purus ac neque mattis
+venenatis. Aenean lobortis accumsan massa. Donec dui ante, facilisis vel,
+hendrerit ut, vehicula in, eros. Suspendisse potenti. Sed fringilla.
+Suspendisse vel nibh. Sed sit amet lacus quis massa tincidunt elementum. Ut ut
+augue vitae ligula dapibus aliquam.</para>
+</section>
+<section><title>Subsection 5</title>
+<para>Fusce non eros non lectus venenatis bibendum. Nullam pharetra. Nunc
+commodo pede et metus. Pellentesque habitant morbi tristique senectus et netus
+et malesuada fames ac turpis egestas. Vestibulum imperdiet nisl nec nulla.
+Morbi congue dictum pede. Aliquam ligula. In pede nulla, varius a, blandit ut,
+pulvinar vitae, mauris. Suspendisse sit amet magna. Curabitur cursus placerat
+justo. Vivamus imperdiet magna commodo mi. Vestibulum eget metus quis sem
+sollicitudin consectetuer. Morbi metus augue, elementum rutrum, luctus quis,
+porttitor a, est. Phasellus quis sapien et augue adipiscing fermentum. Sed
+fermentum tristique dui. Vivamus aliquam, tortor at ultricies commodo, urna
+ipsum fringilla neque, sit amet congue purus enim a justo.</para>
+</section>
+<section><title>Section 1.10.32 of "de Finibus Bonorum et Malorum",
+written by Cicero in 45 BC</title>
+<para>Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo
+inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
+Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
+sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
+Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur,
+adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et
+dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis
+nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex
+ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea
+voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem
+eum fugiat quo voluptas nulla pariatur?</para>
+</section>
+</chapter>
+<chapter><title>Chapter 2</title>
+<section><title>1914 translation by H. Rackham</title>
+<para>But I must explain to you how all this mistaken idea of denouncing
+pleasure and praising pain was born and I will give you a complete account of
+the system, and expound the actual teachings of the great explorer of the
+truth, the master-builder of human happiness. No one rejects, dislikes, or
+avoids pleasure itself, because it is pleasure, but because those who do not
+know how to pursue pleasure rationally encounter consequences that are
+extremely painful. Nor again is there anyone who loves or pursues or desires
+to obtain pain of itself, because it is pain, but because occasionally
+circumstances occur in which toil and pain can procure him some great
+pleasure. To take a trivial example, which of us ever undertakes laborious
+physical exercise, except to obtain some advantage from it? But who has any
+right to find fault with a man who chooses to enjoy a pleasure that has no
+annoying consequences, or one who avoids a pain that produces no resultant
+pleasure?</para>
+</section>
+<section><title>Section 1.10.33 of "de Finibus Bonorum et Malorum",
+written by Cicero in 45 BC</title>
+<para>At vero eos et accusamus et iusto odio dignissimos ducimus qui
+blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas
+molestias excepturi sint occaecati cupiditate non provident, similique sunt in
+culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et
+harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum
+soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime
+placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum
+necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae
+non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut
+reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus
+asperiores repellat.</para>
+</section>
+</chapter>
+</book>
diff --git a/doxia-core/pom.xml b/doxia-core/pom.xml
new file mode 100644
index 0000000..9a5f5c3
--- /dev/null
+++ b/doxia-core/pom.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.doxia</groupId>
+    <artifactId>doxia</artifactId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-core</artifactId>
+  <name>Doxia :: Core</name>
+  <description>Doxia core classes and interfaces.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-sink-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-logging-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-container-default</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>xerces</groupId>
+      <artifactId>xercesImpl</artifactId>
+      <version>2.9.1</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>2.4</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <version>4.0.2</version>
+    </dependency>
+
+    <!-- test -->
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.modello</groupId>
+        <artifactId>modello-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>descriptor</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>java</goal>
+              <goal>xpp3-reader</goal>
+              <goal>xpp3-writer</goal>
+              <goal>xsd</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>docs</id>
+            <phase>pre-site</phase>
+            <goals>
+              <goal>xdoc</goal>
+              <goal>xsd</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <version>1.0.1</version>
+          <models>
+            <model>src/main/mdo/document.mdo</model>
+          </models>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/DefaultDoxia.java b/doxia-core/src/main/java/org/apache/maven/doxia/DefaultDoxia.java
new file mode 100644
index 0000000..226dbad
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/DefaultDoxia.java
@@ -0,0 +1,72 @@
+package org.apache.maven.doxia;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.logging.PlexusLoggerWrapper;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.parser.manager.ParserManager;
+import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
+import org.apache.maven.doxia.sink.Sink;
+
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+import java.io.Reader;
+
+/**
+ * Simple implementation of the Doxia interface:
+ * uses a ParserManager to lookup a parser.
+ *
+ * @author Jason van Zyl
+ * @version $Id: DefaultDoxia.java 628059 2008-02-15 13:22:30Z vsiveton $
+ * @since 1.0
+ * @plexus.component
+ */
+public class DefaultDoxia
+    extends AbstractLogEnabled
+    implements Doxia
+{
+    /** @plexus.requirement */
+    private ParserManager parserManager;
+
+    // ----------------------------------------------------------------------
+    // This remains because the sinks are not threadsafe which they probably
+    // should be. In some places a constructor is used to initialize a sink
+    // which can probably be done away with.
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void parse( Reader source, String parserId, Sink sink )
+        throws ParserNotFoundException, ParseException
+    {
+        Parser parser = parserManager.getParser( parserId );
+
+        parser.enableLogging( new PlexusLoggerWrapper( getLogger() ) );
+
+        parser.parse( source, sink );
+    }
+
+    /** {@inheritDoc} */
+    public Parser getParser( String parserId )
+        throws ParserNotFoundException
+    {
+        return parserManager.getParser( parserId );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/Doxia.java b/doxia-core/src/main/java/org/apache/maven/doxia/Doxia.java
new file mode 100644
index 0000000..2638539
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/Doxia.java
@@ -0,0 +1,66 @@
+package org.apache.maven.doxia;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
+import org.apache.maven.doxia.sink.Sink;
+
+import java.io.Reader;
+
+/**
+ * Basic interface of the Doxia framework.
+ *
+ * @author Jason van Zyl
+ * @version $Id: Doxia.java 708880 2008-10-29 11:29:48Z vsiveton $
+ * @since 1.0
+ */
+public interface Doxia
+{
+    /** The Plexus lookup role. */
+    String ROLE = Doxia.class.getName();
+
+    /**
+     * Parses the given source model using a parser with given id,
+     * and emits Doxia events into the given sink.
+     *
+     * @param source not null reader that provides the source document.
+     * You could use <code>newReader</code> methods from {@link org.codehaus.plexus.util.ReaderFactory}.
+     * @param parserId Identifier for the parser to use.
+     * @param sink A sink that consumes the Doxia events.
+     * @throws org.apache.maven.doxia.parser.manager.ParserNotFoundException
+     * if no parser could be found for the given id.
+     * @throws org.apache.maven.doxia.parser.ParseException if the model could not be parsed.
+     */
+    void parse( Reader source, String parserId, Sink sink )
+        throws ParserNotFoundException, ParseException;
+
+    /**
+     * Return a parser for the given <code>parserId</code>.
+     *
+     * @param parserId Identifier for the parser to use.
+     * @return the parser defining by parserId.
+     * @throws org.apache.maven.doxia.parser.manager.ParserNotFoundException
+     * if no parser could be found for the given id.
+     */
+    Parser getParser( String parserId )
+        throws ParserNotFoundException;
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/index/IndexEntry.java b/doxia-core/src/main/java/org/apache/maven/doxia/index/IndexEntry.java
new file mode 100644
index 0000000..99d5960
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/index/IndexEntry.java
@@ -0,0 +1,312 @@
+package org.apache.maven.doxia.index;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collections;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * <p>IndexEntry class.</p>
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: IndexEntry.java 807625 2009-08-25 13:43:52Z vsiveton $
+ */
+public class IndexEntry
+{
+    /** The parent entry. */
+    private final IndexEntry parent;
+
+    /** The id of the entry. */
+    private String id;
+
+    /** The entry title. */
+    private String title;
+
+    /** The child entries. */
+    private List childEntries = new ArrayList();
+
+    /** System-dependent EOL. */
+    private static final String EOL = System.getProperty( "line.separator" );
+
+    /**
+     * Constructor.
+     *
+     * @param newId The id. May be null.
+     */
+    public IndexEntry( String newId )
+    {
+        this( null, newId );
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param newParent The parent. May be null.
+     * @param newId The id. May be null.
+     */
+    public IndexEntry( IndexEntry newParent, String newId )
+    {
+        this.parent = newParent;
+        this.id = newId;
+
+        if ( parent != null )
+        {
+            parent.childEntries.add( this );
+        }
+    }
+
+    /**
+     * Returns the parent entry.
+     *
+     * @return the parent entry.
+     */
+    public IndexEntry getParent()
+    {
+        return parent;
+    }
+
+    /**
+     * Returns the id.
+     *
+     * @return the id.
+     */
+    public String getId()
+    {
+        return id;
+    }
+
+    /**
+     * Set the id.
+     *
+     * @since 1.1.2
+     */
+    protected void setId( String id )
+    {
+        this.id = id;
+    }
+
+    /**
+     * Returns the title.
+     *
+     * @return the title.
+     */
+    public String getTitle()
+    {
+        return title;
+    }
+
+    /**
+     * Sets the title.
+     *
+     * @param newTitle the title.
+     */
+    public void setTitle( String newTitle )
+    {
+        this.title = newTitle;
+    }
+
+    /**
+     * Returns an unmodifiableList of the child entries.
+     *
+     * @return child entries.
+     */
+    public List getChildEntries()
+    {
+        return Collections.unmodifiableList( childEntries );
+    }
+
+    /**
+     * Sets the child entries or creates a new ArrayList if entries == null.
+     *
+     * @param entries the entries.
+     */
+    public void setChildEntries( List entries )
+    {
+        if ( entries == null )
+        {
+            childEntries = new ArrayList();
+        }
+
+        this.childEntries = entries;
+    }
+
+    // -----------------------------------------------------------------------
+    // Utils
+    // -----------------------------------------------------------------------
+
+    /**
+     * Returns the next entry.
+     *
+     * @return the next entry, or null if there is none.
+     */
+    public IndexEntry getNextEntry()
+    {
+        if ( parent == null )
+        {
+            return null;
+        }
+
+        List entries = parent.getChildEntries();
+
+        int index = entries.indexOf( this );
+
+        if ( index + 1 >= entries.size() )
+        {
+            return null;
+        }
+
+        return (IndexEntry) entries.get( index + 1 );
+    }
+
+    /**
+     * Returns the previous entry.
+     *
+     * @return the previous entry, or null if there is none.
+     */
+    public IndexEntry getPrevEntry()
+    {
+        if ( parent == null )
+        {
+            return null;
+        }
+
+        List entries = parent.getChildEntries();
+
+        int index = entries.indexOf( this );
+
+        if ( index == 0 )
+        {
+            return null;
+        }
+
+        return (IndexEntry) entries.get( index - 1 );
+    }
+
+    /**
+     * Returns the first entry.
+     *
+     * @return the first entry, or null if there is none.
+     */
+    public IndexEntry getFirstEntry()
+    {
+        List entries = getChildEntries();
+
+        if ( entries.size() == 0 )
+        {
+            return null;
+        }
+
+        return (IndexEntry) entries.get( 0 );
+    }
+
+    /**
+     * Returns the last entry.
+     *
+     * @return the last entry, or null if there is none.
+     */
+    public IndexEntry getLastEntry()
+    {
+        List entries = getChildEntries();
+
+        if ( entries.size() == 0 )
+        {
+            return null;
+        }
+
+        return (IndexEntry) entries.get( entries.size() - 1 );
+    }
+
+    /**
+     * Returns the root entry.
+     *
+     * @return the root entry, or null if there is none.
+     */
+    public IndexEntry getRootEntry()
+    {
+        List entries = getChildEntries();
+
+        if ( entries.size() == 0 )
+        {
+            return null;
+        }
+        else if ( entries.size() > 1 )
+        {
+            throw new RuntimeException( "This index has more than one root entry" );
+        }
+        else
+        {
+            return (IndexEntry) entries.get( 0 );
+        }
+    }
+
+    // -----------------------------------------------------------------------
+    // Object Overrides
+    // -----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     *
+     * Returns a string representation of the object.
+     */
+    public String toString()
+    {
+        return toString( 0 );
+    }
+
+    /**
+     * Returns a string representation of all objects to the given depth.
+     *
+     * @param depth The depth to descent to.
+     * @return A string.
+     */
+    public String toString( int depth )
+    {
+        StringBuffer message = new StringBuffer();
+
+        message.append( "Id: " ).append( id );
+
+        if ( StringUtils.isNotEmpty( title ) )
+        {
+            message.append( ", title: " ).append( title );
+        }
+
+        message.append( EOL );
+
+        String indent = "";
+
+        for ( int i = 0; i < depth; i++ )
+        {
+            indent += " ";
+        }
+
+        for ( Iterator it = getChildEntries().iterator(); it.hasNext(); )
+        {
+            IndexEntry entry = (IndexEntry) it.next();
+
+            message.append( indent ).append( entry.toString( depth + 1 ) );
+        }
+
+        return message.toString();
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/index/IndexingSink.java b/doxia-core/src/main/java/org/apache/maven/doxia/index/IndexingSink.java
new file mode 100644
index 0000000..2ad23c2
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/index/IndexingSink.java
@@ -0,0 +1,309 @@
+package org.apache.maven.doxia.index;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Stack;
+
+import org.apache.maven.doxia.util.HtmlTools;
+import org.apache.maven.doxia.sink.SinkAdapter;
+
+/**
+ * A sink implementation for index.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: IndexingSink.java 808200 2009-08-26 22:04:08Z vsiveton $
+ */
+public class IndexingSink
+    extends SinkAdapter
+{
+    /** Section 1. */
+    private static final int TYPE_SECTION_1 = 1;
+
+    /** Section 2. */
+    private static final int TYPE_SECTION_2 = 2;
+
+    /** Section 3. */
+    private static final int TYPE_SECTION_3 = 3;
+
+    /** Section 4. */
+    private static final int TYPE_SECTION_4 = 4;
+
+    /** Section 5. */
+    private static final int TYPE_SECTION_5 = 5;
+
+    /** Defined term. */
+    private static final int TYPE_DEFINED_TERM = 6;
+
+    /** Figure. */
+    private static final int TYPE_FIGURE = 7;
+
+    /** Table. */
+    private static final int TYPE_TABLE = 8;
+
+    /** Title. */
+    private static final int TITLE = 9;
+
+    /** The current type. */
+    private int type;
+
+    /** The current title. */
+    private String title;
+
+    /** The stack. */
+    private final Stack stack;
+
+    /** The current type. */
+    private IndexEntry currentEntry;
+
+    /**
+     * Default constructor.
+     *
+     * @param sectionEntry The first index entry.
+     */
+    public IndexingSink( IndexEntry sectionEntry )
+    {
+        stack = new Stack();
+        stack.push( sectionEntry );
+
+        init();
+    }
+
+    /**
+     * <p>Getter for the field <code>title</code>.</p>
+     *
+     * @return the title
+     */
+    public String getTitle()
+    {
+        return title;
+    }
+
+    // ----------------------------------------------------------------------
+    // Sink Overrides
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        this.type = TITLE;
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        this.currentEntry = null;
+        this.type = TYPE_SECTION_1;
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        this.type = 0;
+    }
+
+    public void sectionTitle1_()
+    {
+        this.type = 0;
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        pop();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        this.currentEntry = null;
+        this.type = TYPE_SECTION_2;
+    }
+
+    public void sectionTitle2_()
+    {
+        this.type = 0;
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        pop();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        this.currentEntry = null;
+        this.type = TYPE_SECTION_3;
+    }
+
+    public void sectionTitle3_()
+    {
+        this.type = 0;
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        pop();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        this.currentEntry = null;
+        this.type = TYPE_SECTION_4;
+    }
+
+    public void sectionTitle4_()
+    {
+        this.type = 0;
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        pop();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        this.currentEntry = null;
+        this.type = TYPE_SECTION_5;
+    }
+
+    public void sectionTitle5_()
+    {
+        this.type = 0;
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        pop();
+    }
+
+    // public void definedTerm()
+    // {
+    // type = TYPE_DEFINED_TERM;
+    // }
+    //
+    // public void figureCaption()
+    // {
+    // type = TYPE_FIGURE;
+    // }
+    //
+    // public void tableCaption()
+    // {
+    // type = TYPE_TABLE;
+    // }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        switch ( this.type )
+        {
+            case TITLE:
+                this.title = text;
+                break;
+            case TYPE_SECTION_1:
+            case TYPE_SECTION_2:
+            case TYPE_SECTION_3:
+            case TYPE_SECTION_4:
+            case TYPE_SECTION_5:
+                // -----------------------------------------------------------------------
+                // Sanitize the id. The most important step is to remove any blanks
+                // -----------------------------------------------------------------------
+
+                if ( this.currentEntry == null )
+                {
+                    this.currentEntry = new IndexEntry( peek(), HtmlTools.encodeId( text ) );
+
+                    this.currentEntry.setTitle( text );
+
+                    push( currentEntry );
+                }
+                else
+                {
+                    IndexEntry entry = (IndexEntry) stack.lastElement();
+
+                    String title = currentEntry.getTitle() + text;
+                    title = title.replaceAll( "[\\r\\n]+", "" );
+
+                    entry.setId( HtmlTools.encodeId( title ) );
+
+                    entry.setTitle( title );
+                }
+
+                break;
+            // Dunno how to handle these yet
+            case TYPE_DEFINED_TERM:
+            case TYPE_FIGURE:
+            case TYPE_TABLE:
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Pushes an IndexEntry onto the top of this stack.
+     *
+     * @param entry to put.
+     */
+    public void push( IndexEntry entry )
+    {
+        stack.push( entry );
+    }
+
+    /**
+     * Removes the IndexEntry at the top of this stack.
+     */
+    public void pop()
+    {
+        stack.pop();
+    }
+
+    /**
+     * <p>peek.</p>
+     *
+     * @return Looks at the IndexEntry at the top of this stack.
+     */
+    public IndexEntry peek()
+    {
+        return (IndexEntry) stack.peek();
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        super.close();
+
+        init();
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        this.type = 0;
+        this.title = null;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/AbstractMacro.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/AbstractMacro.java
new file mode 100644
index 0000000..9a6086a
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/AbstractMacro.java
@@ -0,0 +1,118 @@
+package org.apache.maven.doxia.macro;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.maven.doxia.logging.Log;
+import org.apache.maven.doxia.logging.SystemStreamLog;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Abstract base class to execute <code>Macro</code>.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: AbstractMacro.java 773896 2009-05-12 13:46:00Z ltheussl $
+ * @since 1.0
+ */
+public abstract class AbstractMacro
+    implements Macro
+{
+    /** Log instance. */
+    private Log logger;
+
+    /** {@inheritDoc} */
+    public void enableLogging( Log log )
+    {
+        this.logger = log;
+    }
+
+    /**
+     * Returns a logger for this macro.
+     * If no logger has been configured, a new SystemStreamLog is returned.
+     *
+     * @return Log
+     * @since 1.1
+     */
+    protected Log getLog()
+    {
+        if ( logger == null )
+        {
+            logger = new SystemStreamLog();
+        }
+
+        return logger;
+    }
+
+    /**
+     * Check if the given parameter is required. Throws an
+     * IllegalArgumentException if paramValue is null or empty.
+     *
+     * @param paramName The name of the parameter to check.
+     * @param paramValue The parameter value.
+     * @since 1.1
+     */
+    protected void required( String paramName, String paramValue )
+    {
+        if ( StringUtils.isEmpty( paramValue ) )
+        {
+            throw new IllegalArgumentException( paramName + " is a required parameter!" );
+        }
+    }
+
+    /**
+     * Convert the Map of macro parameters to an AttributeSet.
+     * No check of validity is done, all parameters are added.
+     *
+     * @param parameters the macro parameters.
+     * @return a SinkEventAttributeSet containing the same parameters,
+     *  or null if parameters is null.
+     *
+     * @since 1.1.1.
+     */
+    protected static SinkEventAttributes getAttributesFromMap( Map parameters )
+    {
+        if ( parameters == null )
+        {
+            return null;
+        }
+
+        final int count = parameters.size();
+
+        if ( count <= 0 )
+        {
+            return null;
+        }
+
+        final SinkEventAttributeSet atts = new SinkEventAttributeSet( count );
+
+        for ( Iterator it = parameters.keySet().iterator(); it.hasNext(); )
+        {
+            final Object key = it.next();
+            atts.addAttribute( key, parameters.get( key ) );
+        }
+
+        return atts;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/EchoMacro.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/EchoMacro.java
new file mode 100644
index 0000000..2bcf8b1
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/EchoMacro.java
@@ -0,0 +1,58 @@
+package org.apache.maven.doxia.macro;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.Iterator;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+/**
+ * A simple macro that prints out the key and value of some supplied parameters.
+ *
+ * @plexus.component role-hint="echo"
+ * @version $Id: EchoMacro.java 746978 2009-02-23 12:20:33Z vsiveton $
+ */
+public class EchoMacro
+    extends AbstractMacro
+{
+    /** {@inheritDoc} */
+    public void execute( Sink sink, MacroRequest request )
+    {
+        sink.verbatim( SinkEventAttributeSet.BOXED );
+
+        sink.text( "echo" + EOL );
+
+        for ( Iterator i = request.getParameters().keySet().iterator(); i.hasNext(); )
+        {
+            String key = (String) i.next();
+
+            // TODO: DOXIA-242: separate or define internal params
+            if ( "parser".equals( key ) || "sourceContent".equals( key ) )
+            {
+                continue;
+            }
+
+            sink.text( key + " ---> " + request.getParameter( key ) + EOL );
+        }
+
+        sink.verbatim_();
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/Macro.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/Macro.java
new file mode 100644
index 0000000..aca750f
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/Macro.java
@@ -0,0 +1,51 @@
+package org.apache.maven.doxia.macro;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.logging.LogEnabled;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Base interface of a macro.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: Macro.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.0
+ */
+public interface Macro
+    extends LogEnabled
+{
+    /** The Plexus lookup role. */
+    String ROLE = Macro.class.getName();
+
+    /** The vm line separator */
+    String EOL = System.getProperty( "line.separator" );
+
+    /**
+     * Execute the current macro using the given MacroRequest,
+     * and emit events into the given sink.
+     *
+     * @param sink The sink to receive the events.
+     * @param request The corresponding MacroRequest.
+     * @throws org.apache.maven.doxia.macro.MacroExecutionException if an error occurred during execution.
+     */
+    void execute( Sink sink, MacroRequest request )
+        throws MacroExecutionException;
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/MacroExecutionException.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/MacroExecutionException.java
new file mode 100644
index 0000000..b4845f6
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/MacroExecutionException.java
@@ -0,0 +1,60 @@
+package org.apache.maven.doxia.macro;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Wrap an exception that occurs during the execution of a Doxia macro.
+ *
+ * @author <a href="mailto:brett at apache.org">Brett Porter</a>
+ * @version $Id: MacroExecutionException.java 636300 2008-03-12 12:46:19Z vsiveton $
+ * @since 1.0
+ */
+public class MacroExecutionException
+    extends Exception
+{
+    /** serialVersionUID */
+    static final long serialVersionUID = -6314856898570018814L;
+
+    /**
+     * Construct a new <code>MacroExecutionException</code> with the specified detail message.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     */
+    public MacroExecutionException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Construct a new <code>MacroExecutionException</code> with the specified
+     * detail message and cause.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     * @param cause the cause. This can be retrieved later by the
+     * <code>Throwable.getCause()</code> method. (A null value is permitted, and indicates
+     * that the cause is nonexistent or unknown.)
+     */
+    public MacroExecutionException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/MacroRequest.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/MacroRequest.java
new file mode 100644
index 0000000..b24c57b
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/MacroRequest.java
@@ -0,0 +1,93 @@
+package org.apache.maven.doxia.macro;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Map;
+import java.io.File;
+
+/**
+ * <p>MacroRequest class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: MacroRequest.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.0
+ */
+public class MacroRequest
+{
+    /** The current base directory. */
+    private File basedir;
+
+    /** A map of parameters. */
+    private Map parameters;
+
+    /**
+     * Constructor.
+     *
+     * @param param A map of parameters.
+     * @param base The current base directory.
+     */
+    public MacroRequest( Map param, File base )
+    {
+        this.parameters = param;
+        this.basedir = base;
+    }
+
+    /**
+     * Returns the current base directory.
+     *
+     * @return The base dir.
+     */
+    public File getBasedir()
+    {
+        return basedir;
+    }
+
+    /**
+     * Sets the current base directory.
+     *
+     * @param base The current base directory.
+     */
+    public void setBasedir( File base )
+    {
+        this.basedir = base;
+    }
+
+    /**
+     * Returns the map of parameters.
+     *
+     * @return The map of parameters.
+     */
+    public Map getParameters()
+    {
+        return parameters;
+    }
+
+    /**
+     * Returns on object from the map of parameters
+     * that corresponds to the given key.
+     *
+     * @param key The key to lookup the object.
+     * @return The value object.
+     */
+    public Object getParameter( String key )
+    {
+        return parameters.get( key );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/SwfMacro.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/SwfMacro.java
new file mode 100644
index 0000000..a561adb
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/SwfMacro.java
@@ -0,0 +1,182 @@
+package org.apache.maven.doxia.macro;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Iterator;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Macro for embedding Flash (SWF) within Maven documentation.
+ *
+ * @plexus.component role="org.apache.maven.doxia.macro.Macro"
+ * role-hint="swf"
+ *
+ * @author <a href="mailto:steve.motola at gmail.com">Steve Motola</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: SwfMacro.java 759580 2009-03-28 20:36:40Z ltheussl $
+ */
+public class SwfMacro
+    extends AbstractMacro
+{
+    /** {@inheritDoc} */
+    public void execute( Sink sink, MacroRequest request )
+        throws MacroExecutionException
+    {
+        // parameter defaults
+        String src = "";
+        String id = "swf";
+        String width = "400";
+        String height = "400";
+        String quality = "high";
+        String menu = "false";
+        String loop = "0";
+        String play = "true";
+        String version = "9,0,45,0";
+        String allowScript = "sameDomain";
+
+        // assign parameters
+        for ( Iterator i = request.getParameters().keySet().iterator(); i.hasNext(); )
+        {
+            String str = "";
+            String key = (String) i.next();
+            if ( key.equals( "src" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    src = str;
+                }
+            }
+            if ( key.equals( "id" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    id = str;
+                }
+            }
+            if ( key.equals( "width" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    width = str;
+                }
+            }
+            if ( key.equals( "height" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    height = str;
+                }
+            }
+            if ( key.equals( "quality" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    quality = str;
+                }
+            }
+            if ( key.equals( "menu" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    menu = str;
+                }
+            }
+            if ( key.equals( "loop" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    loop = str;
+                }
+            }
+            if ( key.equals( "play" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    play = str;
+                }
+            }
+            if ( key.equals( "version" ) )
+            {
+                str = (String) request.getParameter( key );
+                // enable version shorthand
+                // TODO: put in other shorthand versions
+                if ( str.equals( "6" ) )
+                {
+                    version = "6,0,29,0";
+                }
+                else
+                {
+                    if ( str.equals( "9" ) )
+                    {
+                        version = "9,0,45,0";
+                    }
+                    else
+                    {
+                        if ( StringUtils.isNotEmpty( str ) )
+                        {
+                            version = str;
+                        }
+                    }
+                }
+            }
+            if ( key.equals( "allowScript" ) )
+            {
+                str = (String) request.getParameter( key );
+                if ( StringUtils.isNotEmpty( str ) )
+                {
+                    allowScript = str;
+                }
+            }
+        }
+
+        StringBuffer content = new StringBuffer();
+        content.append( "<center>" ).append( EOL );
+        content.append( "<object classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" " )
+            .append( "codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=" )
+            .append( version ).append( "\" width=\"" ).append( width ).append( "\" height=\"" ).append( height )
+            .append( "\" id=\"" ).append( id ).append( "\">" ).append( EOL );
+        content.append( "<param name=\"movie\" value=\"" ).append( src ).append( "\">" ).append( EOL );
+        content.append( "<param name=\"quality\" value=\"" ).append( quality ).append( "\">" ).append( EOL );
+        content.append( "<param name=\"menu\" value=\"" ).append( menu ).append( "\">" ).append( EOL );
+        content.append( "<param name=\"loop\" value=\"" ).append( loop ).append( "\">" ).append( EOL );
+        content.append( "<param name=\"play\" value=\"" ).append( play ).append( "\">" ).append( EOL );
+        content.append( "<param name=\"allowScriptAccess\" value=\"" ).append( allowScript ).append( "\">" );
+        content.append( "<embed src=\"" ).append( src ).append( "\" width=\"" ).append( width ).append( "\" height=\"" )
+            .append( height ).append( "\" loop=\"" ).append( loop ).append( "\" play=\"" ).append( play )
+            .append( "\" quality=\"" ).append( quality ).append( "\" allowScriptAccess=\"" ).append( allowScript )
+            .append( "\" " ).append( "pluginspage=\"http://www.macromedia.com/go/getflashplayer\" " )
+            .append( "type=\"application/x-shockwave-flash\" menu=\"" ).append( menu ).append( "\">" ).append( EOL );
+        content.append( "</embed>" ).append( EOL );
+        content.append( "</object>" ).append( EOL );
+        content.append( "</center>" ).append( EOL );
+
+        sink.rawText( content.toString() );
+    }
+}
\ No newline at end of file
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/DefaultMacroManager.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/DefaultMacroManager.java
new file mode 100644
index 0000000..789fcc2
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/DefaultMacroManager.java
@@ -0,0 +1,53 @@
+package org.apache.maven.doxia.macro.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.macro.Macro;
+
+import java.util.Map;
+
+/**
+ * Default implementation of <code>MacroManager</code>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: DefaultMacroManager.java 567311 2007-08-18 18:30:54Z vsiveton $
+ * @since 1.0
+ * @plexus.component
+ */
+public class DefaultMacroManager
+    implements MacroManager
+{
+    /** @plexus.requirement role="org.apache.maven.doxia.macro.Macro" */
+    private Map macros;
+
+    /** {@inheritDoc} */
+    public Macro getMacro( String id )
+        throws MacroNotFoundException
+    {
+        Macro macro = (Macro) macros.get( id );
+
+        if ( macro == null )
+        {
+            throw new MacroNotFoundException( "Cannot find macro with id = " + id );
+        }
+
+        return macro;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/MacroManager.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/MacroManager.java
new file mode 100644
index 0000000..174b836
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/MacroManager.java
@@ -0,0 +1,47 @@
+package org.apache.maven.doxia.macro.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.macro.Macro;
+
+/**
+ * Handles MacroManager lookups.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: MacroManager.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.0
+ */
+public interface MacroManager
+{
+    /** The Plexus lookup role. */
+    String ROLE = MacroManager.class.getName();
+
+    /**
+     * Returns the MacroManager that corresponds to the given id.
+     *
+     * @param id The identifier.
+     * @return The corresponding MacroManager.
+     * @throws org.apache.maven.doxia.macro.manager.MacroNotFoundException if no MacroManager could be found
+     * for the given id.
+     */
+    Macro getMacro( String id )
+        throws MacroNotFoundException;
+
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/MacroNotFoundException.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/MacroNotFoundException.java
new file mode 100644
index 0000000..d4f3d7e
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/manager/MacroNotFoundException.java
@@ -0,0 +1,74 @@
+package org.apache.maven.doxia.macro.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Encapsulate an exception that indicates that a Macro
+ * does not exist or could not be found.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: MacroNotFoundException.java 662767 2008-06-03 12:25:07Z ltheussl $
+ * @since 1.0
+ */
+public class MacroNotFoundException
+    extends Exception
+{
+    /** serialVersionUID */
+    static final long serialVersionUID = 295967936746221567L;
+
+    /**
+     * Construct a new MacroNotFoundException with the specified detail message.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the Throwable.getMessage() method.
+     */
+    public MacroNotFoundException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Constructs a new MacroNotFoundException with the specified cause.
+     * The error message is (cause == null ? null : cause.toString() ).
+     *
+     * @param cause the cause. This can be retrieved later by the
+     * Throwable.getCause() method. (A null value is permitted, and indicates
+     * that the cause is nonexistent or unknown.)
+     */
+    public MacroNotFoundException( Throwable cause )
+    {
+        super( cause );
+    }
+
+    /**
+     * Construct a new MacroNotFoundException with the specified
+     * detail message and cause.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the Throwable.getMessage() method.
+     * @param cause the cause. This can be retrieved later by the
+     * Throwable.getCause() method. (A null value is permitted, and indicates
+     * that the cause is nonexistent or unknown.)
+     */
+    public MacroNotFoundException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/snippet/SnippetMacro.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/snippet/SnippetMacro.java
new file mode 100644
index 0000000..50df273
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/snippet/SnippetMacro.java
@@ -0,0 +1,289 @@
+package org.apache.maven.doxia.macro.snippet;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.macro.AbstractMacro;
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.io.IOException;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+/**
+ * A macro that prints out the content of a file or a URL.
+ *
+ * @plexus.component role-hint="snippet"
+ * @version $Id: SnippetMacro.java 747735 2009-02-25 10:43:09Z ltheussl $
+ */
+public class SnippetMacro
+    extends AbstractMacro
+{
+    /** Holds the cache. */
+    private static Map cache = new HashMap();
+
+    private static final int HOUR = 60;
+
+    /** One hour default cache. */
+    private long timeout = HOUR * HOUR * 1000;
+
+    /** Holds the time cache. */
+    private static Map timeCached = new HashMap();
+
+    /** Debug. */
+    private boolean debug = false;
+
+    /** {@inheritDoc} */
+    public void execute( Sink sink, MacroRequest request )
+        throws MacroExecutionException
+    {
+        String id = (String) request.getParameter( "id" );
+
+        String urlParam = (String) request.getParameter( "url" );
+
+        String fileParam = (String) request.getParameter( "file" );
+
+        boolean verbatim = true;
+
+        String verbatimParam = (String) request.getParameter( "verbatim" );
+
+        if ( verbatimParam != null && !"".equals( verbatimParam ) )
+        {
+            verbatim = Boolean.valueOf( verbatimParam ).booleanValue();
+        }
+
+        URL url;
+
+        if ( !StringUtils.isEmpty( urlParam ) )
+        {
+            try
+            {
+                url = new URL( urlParam );
+            }
+            catch ( MalformedURLException e )
+            {
+                throw new IllegalArgumentException( urlParam + " is a malformed URL" );
+            }
+        }
+        else if ( !StringUtils.isEmpty( fileParam ) )
+        {
+            File f = new File( fileParam );
+
+            if ( !f.isAbsolute() )
+            {
+                f = new File( request.getBasedir(), fileParam );
+            }
+
+            try
+            {
+                url = f.toURI().toURL();
+            }
+            catch ( MalformedURLException e )
+            {
+                throw new IllegalArgumentException( fileParam + " is a malformed URL" );
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException( "Either the 'url' or the 'file' param has to be given." );
+        }
+
+        StringBuffer snippet;
+
+        try
+        {
+            snippet = getSnippet( url, id );
+        }
+        catch ( IOException e )
+        {
+            throw new MacroExecutionException( "Error reading snippet", e );
+        }
+
+        if ( verbatim )
+        {
+            sink.verbatim( SinkEventAttributeSet.BOXED );
+
+            sink.text( snippet.toString() );
+
+            sink.verbatim_();
+        }
+        else
+        {
+            sink.rawText( snippet.toString() );
+        }
+    }
+
+    /**
+     * Return a snippet of the given url.
+     *
+     * @param url The URL to parse.
+     * @param id The id of the snippet.
+     * @return The snippet.
+     * @throws IOException if something goes wrong.
+     */
+    private StringBuffer getSnippet( URL url, String id )
+        throws IOException
+    {
+        StringBuffer result;
+
+        String cachedSnippet = (String) getCachedSnippet( url, id );
+
+        if ( cachedSnippet != null )
+        {
+            result = new StringBuffer( cachedSnippet );
+
+            if ( debug )
+            {
+                result.append( "(Served from cache)" );
+            }
+        }
+        else
+        {
+            result = new SnippetReader( url ).readSnippet( id );
+
+            cacheSnippet( url, id, result.toString() );
+
+            if ( debug )
+            {
+                result.append( "(Fetched from url, cache content " ).append( cache ).append( ")" );
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Return a snippet from the cache.
+     *
+     * @param url The URL to parse.
+     * @param id The id of the snippet.
+     * @return The snippet.
+     */
+    private Object getCachedSnippet( URL url, String id )
+    {
+        if ( isCacheTimedout( url, id ) )
+        {
+            removeFromCache( url, id );
+        }
+        return cache.get( globalSnippetId( url, id ) );
+    }
+
+    /**
+     * Return true if the snippet has been cached longer than
+     * the current timeout.
+     *
+     * @param url The URL to parse.
+     * @param id The id of the snippet.
+     * @return True if timeout exceeded.
+     */
+    boolean isCacheTimedout( URL url, String id )
+    {
+        return timeInCache( url, id ) >= timeout;
+    }
+
+    /**
+     * Return the time the snippet has been cached.
+     *
+     * @param url The URL to parse.
+     * @param id The id of the snippet.
+     * @return The cache time.
+     */
+    long timeInCache( URL url, String id )
+    {
+        return System.currentTimeMillis() - getTimeCached( url, id );
+    }
+
+    /**
+     * Return the absolute value of when the snippet has been cached.
+     *
+     * @param url The URL to parse.
+     * @param id The id of the snippet.
+     * @return The cache time.
+     */
+    long getTimeCached( URL url, String id )
+    {
+        String globalId = globalSnippetId( url, id );
+
+        return timeCached.containsKey( globalId ) ? ( (Long) timeCached.get( globalId ) ).longValue() : 0;
+    }
+
+    /**
+     * Removes the snippet from the cache.
+     *
+     * @param url The URL to parse.
+     * @param id The id of the snippet.
+     */
+    private void removeFromCache( URL url, String id )
+    {
+        String globalId = globalSnippetId( url, id );
+
+        timeCached.remove( globalId );
+
+        cache.remove( globalId );
+    }
+
+    /**
+     * Return a global identifier for the snippet.
+     *
+     * @param url The URL to parse.
+     * @param id The id of the snippet.
+     * @return An identifier, concatenated url and id,
+     *  or just url.toString() if id is empty or null.
+     */
+    private String globalSnippetId( URL url, String id )
+    {
+        if ( StringUtils.isEmpty( id ) )
+        {
+            return url.toString();
+        }
+
+        return url + " " + id;
+    }
+
+    /**
+     * Puts the given snippet into the cache.
+     *
+     * @param url The URL to parse.
+     * @param id The id of the snippet.
+     * @param content The content of the snippet.
+     */
+    public void cacheSnippet( URL url, String id, String content )
+    {
+        cache.put( globalSnippetId( url, id ), content );
+
+        timeCached.put( globalSnippetId( url, id ), new Long( System.currentTimeMillis() ) );
+    }
+
+    /**
+     * Set the cache timeout.
+     *
+     * @param time The timeout to set.
+     */
+    public void setCacheTimeout( int time )
+    {
+        this.timeout = time;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/snippet/SnippetReader.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/snippet/SnippetReader.java
new file mode 100644
index 0000000..b53e936
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/snippet/SnippetReader.java
@@ -0,0 +1,201 @@
+package org.apache.maven.doxia.macro.snippet;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * Utility class for reading snippets.
+ *
+ * @version $Id: SnippetReader.java 947273 2010-05-22 09:16:28Z ltheussl $
+ */
+public class SnippetReader
+{
+    /** System-dependent EOL. */
+    private static final String EOL = System.getProperty( "line.separator" );
+
+    /** The source. */
+    private URL source;
+
+    /**
+     * Constructor.
+     *
+     * @param src The source.
+     */
+    public SnippetReader( URL src )
+    {
+        this.source = src;
+    }
+
+    /**
+     * Reads the snippet with given id.
+     *
+     * @param snippetId The id of the snippet.
+     * @return The snippet.
+     * @throws java.io.IOException if something goes wrong.
+     */
+    public StringBuffer readSnippet( String snippetId )
+        throws IOException
+    {
+        List lines = readLines( snippetId );
+        int minIndent = minIndent( lines );
+        StringBuffer result = new StringBuffer();
+        for ( Iterator iterator = lines.iterator(); iterator.hasNext(); )
+        {
+            String line = (String) iterator.next();
+            result.append( line.substring( minIndent ) );
+            result.append( EOL );
+        }
+        return result;
+    }
+
+    /**
+     * Returns the minimal indent of all the lines in the given List.
+     *
+     * @param lines A List of lines.
+     * @return the minimal indent.
+     */
+    int minIndent( List lines )
+    {
+        int minIndent = Integer.MAX_VALUE;
+        for ( Iterator iterator = lines.iterator(); iterator.hasNext(); )
+        {
+            String line = (String) iterator.next();
+            minIndent = Math.min( minIndent, indent( line ) );
+        }
+        return minIndent;
+    }
+
+    /**
+     * Returns the indent of the given line.
+     *
+     * @param line A line.
+     * @return the indent.
+     */
+    int indent( String line )
+    {
+        char[] chars = line.toCharArray();
+        int indent = 0;
+        for ( ; indent < chars.length; indent++ )
+        {
+            if ( chars[indent] != ' ' )
+            {
+                break;
+            }
+        }
+        return indent;
+    }
+
+    /**
+     * Reads the snippet and returns the lines in a List.
+     *
+     * @param snippetId The id of the snippet.
+     * @return A List of lines.
+     * @throws IOException if something goes wrong.
+     */
+    private List readLines( String snippetId )
+        throws IOException
+    {
+        // TODO: DOXIA-386, use InputStreamReader(InputStream in, Charset cs)
+        BufferedReader reader = new BufferedReader( new InputStreamReader( source.openStream() ) );
+        List lines = new ArrayList();
+        try
+        {
+            boolean capture = false;
+            String line;
+            while ( ( line = reader.readLine() ) != null )
+            {
+                if ( snippetId == null || "".equals( snippetId.trim() ) )
+                {
+                    lines.add( line );
+                }
+                else
+                {
+                    if ( isStart( snippetId, line ) )
+                    {
+                        capture = true;
+                    }
+                    else if ( isEnd( snippetId, line ) )
+                    {
+                        break;
+                    }
+                    else if ( capture )
+                    {
+                        lines.add( line );
+                    }
+                }
+            }
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+        return lines;
+    }
+
+    /**
+     * Determines if the given line is a start demarcator.
+     *
+     * @param snippetId the id of the snippet.
+     * @param line the line.
+     * @return True, if the line is a start demarcator.
+     */
+    protected boolean isStart( String snippetId, String line )
+    {
+        return isDemarcator( snippetId, "START", line );
+    }
+
+    /**
+     * Determines if the given line is a demarcator.
+     *
+     * @param snippetId the id of the snippet.
+     * @param what Identifier for the demarcator.
+     * @param line the line.
+     * @return True, if the line is a start demarcator.
+     */
+    protected boolean isDemarcator( String snippetId, String what, String line )
+    {
+        String upper = line.toUpperCase( Locale.ENGLISH );
+        return upper.indexOf( what.toUpperCase( Locale.ENGLISH ) ) != -1
+            && upper.indexOf( "SNIPPET" ) != -1
+            && line.indexOf( snippetId ) != -1;
+    }
+
+    /**
+     * Determines if the given line is an end demarcator.
+     *
+     * @param snippetId the id of the snippet.
+     * @param line the line.
+     * @return True, if the line is an end demarcator.
+     */
+    protected boolean isEnd( String snippetId, String line )
+    {
+        return isDemarcator( snippetId, "END", line );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/macro/toc/TocMacro.java b/doxia-core/src/main/java/org/apache/maven/doxia/macro/toc/TocMacro.java
new file mode 100644
index 0000000..0b34c3a
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/macro/toc/TocMacro.java
@@ -0,0 +1,237 @@
+package org.apache.maven.doxia.macro.toc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringReader;
+
+import java.util.Iterator;
+
+import org.apache.maven.doxia.index.IndexEntry;
+import org.apache.maven.doxia.index.IndexingSink;
+import org.apache.maven.doxia.macro.AbstractMacro;
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.util.HtmlTools;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.Sink;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Macro to display a <code>Table Of Content</code> in a given <code>Sink</code>.
+ * The input parameters for this macro are:
+ * <dl>
+ * <dt>section</dt>
+ * <dd>Display a TOC for the specified section only, or all sections if 0.<br/>
+ * Positive int, not mandatory, 0 by default.</dd>
+ * <dt>fromDepth</dt>
+ * <dd>Minimal depth of entries to display in the TOC.
+ * Sections are depth 1, sub-sections depth 2, etc.<br/>
+ * Positive int, not mandatory, 0 by default.</dd>
+ * <dt>toDepth</dt>
+ * <dd>Maximum depth of entries to display in the TOC.<br/>
+ * Positive int, not mandatory, 5 by default.</dd>
+ * </dl>
+ * For instance, in an APT file, you could write:
+ * <dl>
+ * <dt>%{toc|section=2|fromDepth=2|toDepth=3}</dt>
+ * <dd>Display a TOC for the second section in the document, including all
+ * subsections (depth 2) and  sub-subsections (depth 3).</dd>
+ * <dt>%{toc}</dt>
+ * <dd>display a TOC with all section and subsections
+ * (similar to %{toc|section=0} )</dd>
+ * </dl>
+ * Moreover, you need to write APT link for section to allow anchor,
+ * for instance:
+ * <pre>
+ * * {SubSection 1}
+ * </pre>
+ *
+ * Similarly, in an XDOC file, you could write:
+ * <pre>
+ * <macro name="toc">
+ *   <param name="section" value="1" />
+ *   <param name="fromDepth" value="1" />
+ *   <param name="toDepth" value="2" />
+ * </macro>
+ * </pre>
+ *
+ * @plexus.component role-hint="toc"
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: TocMacro.java 774013 2009-05-12 18:36:51Z ltheussl $
+ */
+public class TocMacro
+    extends AbstractMacro
+{
+    /** The section to display. */
+    private int section;
+
+    /** Start depth. */
+    private int fromDepth;
+
+    /** End depth. */
+    private int toDepth;
+
+    /** The default end depth. */
+    private static final int DEFAULT_DEPTH = 5;
+
+    /** {@inheritDoc} */
+    public void execute( Sink sink, MacroRequest request )
+        throws MacroExecutionException
+    {
+        String source = (String) request.getParameter( "sourceContent" );
+        Parser parser = (Parser) request.getParameter( "parser" );
+
+        section = getInt( request, "section", 0 );
+        fromDepth = getInt( request, "fromDepth", 0 );
+        toDepth = getInt( request, "toDepth", DEFAULT_DEPTH );
+
+        if ( fromDepth > toDepth )
+        {
+            return;
+        }
+
+        IndexEntry index = new IndexEntry( "index" );
+        IndexingSink tocSink = new IndexingSink( index );
+
+        try
+        {
+            parser.parse( new StringReader( source ), tocSink );
+        }
+        catch ( ParseException e )
+        {
+            throw new MacroExecutionException( "ParseException: " + e.getMessage(), e );
+        }
+
+        if ( index.getChildEntries().size() > 0 )
+        {
+            sink.list( getAttributesFromMap( request.getParameters() ) );
+
+            int i = 1;
+
+            for ( Iterator it = index.getChildEntries().iterator(); it.hasNext(); )
+            {
+                IndexEntry sectionIndex = (IndexEntry) it.next();
+
+                if ( ( i == section ) || ( section == 0 ) )
+                {
+                    writeSubSectionN( sink, sectionIndex, 1 );
+                }
+
+                i++;
+            }
+
+            sink.list_();
+        }
+    }
+
+    /**
+     * @param sink The sink to write to.
+     * @param sectionIndex The section index.
+     * @param n The toc depth.
+     */
+    private void writeSubSectionN( Sink sink, IndexEntry sectionIndex, int n )
+    {
+        if ( fromDepth <= n )
+        {
+            sink.listItem();
+            sink.link( "#" + HtmlTools.encodeId( sectionIndex.getId() ) );
+            sink.text( sectionIndex.getTitle() );
+            sink.link_();
+        }
+
+        if ( toDepth > n )
+        {
+            if ( sectionIndex.getChildEntries().size() > 0 )
+            {
+                if ( fromDepth <= n )
+                {
+                    sink.list();
+                }
+
+                for ( Iterator it = sectionIndex.getChildEntries().iterator(); it.hasNext(); )
+                {
+                    IndexEntry subsectionIndex = (IndexEntry) it.next();
+
+                    if ( n == toDepth - 1 )
+                    {
+                        sink.listItem();
+                        sink.link( "#" + HtmlTools.encodeId( subsectionIndex.getId() ) );
+                        sink.text( subsectionIndex.getTitle() );
+                        sink.link_();
+                        sink.listItem_();
+                    }
+                    else
+                    {
+                        writeSubSectionN( sink, subsectionIndex, n + 1 );
+                    }
+                }
+
+                if ( fromDepth <= n )
+                {
+                    sink.list_();
+                }
+            }
+        }
+
+        if ( fromDepth <= n )
+        {
+            sink.listItem_();
+        }
+    }
+
+    /**
+     * @param request The MacroRequest.
+     * @param parameter The parameter.
+     * @param defaultValue the default value.
+     * @return the int value of a parameter in the request.
+     * @throws MacroExecutionException if something goes wrong.
+     */
+    private static int getInt( MacroRequest request, String parameter, int defaultValue )
+        throws MacroExecutionException
+    {
+        String value = (String) request.getParameter( parameter );
+
+        if ( StringUtils.isEmpty( value ) )
+        {
+            return defaultValue;
+        }
+
+        int i;
+
+        try
+        {
+            i = Integer.parseInt( value );
+        }
+        catch ( NumberFormatException e )
+        {
+            return defaultValue;
+        }
+
+        if ( i < 0 )
+        {
+            throw new MacroExecutionException( "The " + parameter + "=" + i + " should be positive." );
+        }
+
+        return i;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/markup/HtmlMarkup.java b/doxia-core/src/main/java/org/apache/maven/doxia/markup/HtmlMarkup.java
new file mode 100644
index 0000000..333a76c
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/markup/HtmlMarkup.java
@@ -0,0 +1,467 @@
+package org.apache.maven.doxia.markup;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+
+/**
+ * List of <code>Html</code> tags.
+ * <p>
+ *   This should contain all valid XHTML 1.0 tags, comprising the tags in
+ *   {@link javax.swing.text.html.HTML.Tag} plus several others.
+ * </p>
+ *
+ * @see <a href="http://www.w3.org/TR/html401/index/elements.html">http://www.w3.org/TR/html401/index/elements.html</a>
+ *
+ * @author ltheussl
+ * @version $Id: HtmlMarkup.java 779585 2009-05-28 12:43:33Z ltheussl $
+ * @since 1.0
+ */
+public interface HtmlMarkup
+    extends XmlMarkup
+{
+
+    /** A simple HTML tag. Eg <code><br/></code>. */
+    int TAG_TYPE_SIMPLE = 1;
+
+    /** A start HTML tag. Eg <code><p></code>. */
+    int TAG_TYPE_START = 2;
+
+    /** An end HTML tag. Eg <code></p></code>. */
+    int TAG_TYPE_END = 3;
+
+    /**
+     * An HTML entity. Eg <code>&lt;</code>.
+     *
+     * @since 1.1.1.
+     */
+    int ENTITY_TYPE = 4;
+
+    /**
+     * A CDATA type event.
+     *
+     * @since 1.1.1.
+     */
+    int CDATA_TYPE = 5;
+
+    // ----------------------------------------------------------------------
+    // All XHTML 1.0 tags
+    // ----------------------------------------------------------------------
+
+    /** Xhtml tag for <code>a</code>. */
+    Tag A = Tag.A;
+
+    /** Xhtml tag for <code>abbr</code>. */
+    Tag ABBR = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "abbr";
+        }
+    };
+
+    /** Xhtml tag for <code>acronym</code>. */
+    Tag ACRONYM = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "acronym";
+        }
+    };
+
+    /** Xhtml tag for <code>address</code>. */
+    Tag ADDRESS = Tag.ADDRESS;
+
+    /** Xhtml tag for <code>applet</code>. */
+    Tag APPLET = Tag.APPLET;
+
+    /** Xhtml tag for <code>area</code>. */
+    Tag AREA = Tag.AREA;
+
+    /** Xhtml tag for <code>b</code>. */
+    Tag B = Tag.B;
+
+    /** Xhtml tag for <code>base</code>. */
+    Tag BASE = Tag.BASE;
+
+    /** Xhtml tag for <code>basefont</code>. */
+    Tag BASEFONT = Tag.BASEFONT;
+
+    /** Xhtml tag for <code>bdo</code>. */
+    Tag BDO = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bdo";
+        }
+    };
+
+    /** Xhtml tag for <code>big</code>. */
+    Tag BIG = Tag.BIG;
+
+    /** Xhtml tag for <code>blockquote</code>. */
+    Tag BLOCKQUOTE = Tag.BLOCKQUOTE;
+
+    /** Xhtml tag for <code>body</code>. */
+    Tag BODY = Tag.BODY;
+
+    /** Xhtml tag for <code>br</code>. */
+    Tag BR = Tag.BR;
+
+    /** Xhtml tag for <code>button</code>. */
+    Tag BUTTON = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "button";
+        }
+    };
+
+    /** Xhtml tag for <code>caption</code>. */
+    Tag CAPTION = Tag.CAPTION;
+
+    /** Xhtml tag for <code>center</code>. */
+    Tag CENTER = Tag.CENTER;
+
+    /** Xhtml tag for <code>cite</code>. */
+    Tag CITE = Tag.CITE;
+
+    /** Xhtml tag for <code>code</code>. */
+    Tag CODE = Tag.CODE;
+
+    /** Xhtml tag for <code>col</code>. */
+    Tag COL = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "col";
+        }
+    };
+
+    /** Xhtml tag for <code>colgroup</code>. */
+    Tag COLGROUP = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "colgroup";
+        }
+    };
+
+    /** Xhtml tag for <code>dd</code>. */
+    Tag DD = Tag.DD;
+
+    /** Xhtml tag for <code>del</code>. */
+    Tag DEL = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "del";
+        }
+    };
+
+    /** Xhtml tag for <code>dfn</code>. */
+    Tag DFN = Tag.DFN;
+
+    /** Xhtml tag for <code>dir</code>. */
+    Tag DIR = Tag.DIR;
+
+    /** Xhtml tag for <code>div</code>. */
+    Tag DIV = Tag.DIV;
+
+    /** Xhtml tag for <code>dl</code>. */
+    Tag DL = Tag.DL;
+
+    /** Xhtml tag for <code>dt</code>. */
+    Tag DT = Tag.DT;
+
+    /** Xhtml tag for <code>em</code>. */
+    Tag EM = Tag.EM;
+
+    /** Xhtml tag for <code>fieldset</code>. */
+    Tag FIELDSET = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "fieldset";
+        }
+    };
+
+    /** Xhtml tag for <code>font</code>. */
+    Tag FONT = Tag.FONT;
+
+    /** Xhtml tag for <code>form</code>. */
+    Tag FORM = Tag.FORM;
+
+    /** Xhtml tag for <code>frame</code>. */
+    Tag FRAME = Tag.FRAME;
+
+    /** Xhtml tag for <code>frameset</code>. */
+    Tag FRAMESET = Tag.FRAMESET;
+
+    /** Xhtml tag for <code>h1</code>. */
+    Tag H1 = Tag.H1;
+
+    /** Xhtml tag for <code>h2</code>. */
+    Tag H2 = Tag.H2 ;
+
+    /** Xhtml tag for <code>h3</code>. */
+    Tag H3 = Tag.H3;
+
+    /** Xhtml tag for <code>h4</code>. */
+    Tag H4 = Tag.H4;
+
+    /** Xhtml tag for <code>h5</code>. */
+    Tag H5 = Tag.H5;
+
+    /** Xhtml tag for <code>h6</code>. */
+    Tag H6 = Tag.H6;
+
+    /** Xhtml tag for <code>head</code>. */
+    Tag HEAD = Tag.HEAD;
+
+    /** Xhtml tag for <code>hr</code>. */
+    Tag HR = Tag.HR;
+
+    /** Xhtml tag for <code>html</code>. */
+    Tag HTML = Tag.HTML;
+
+    /** Xhtml tag for <code>i</code>. */
+    Tag I = Tag.I;
+
+    /** Xhtml tag for <code>iframe</code>. */
+    Tag IFRAME = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "iframe";
+        }
+    };
+
+    /** Xhtml tag for <code>img</code>. */
+    Tag IMG = Tag.IMG;
+
+    /** Xhtml tag for <code>input</code>. */
+    Tag INPUT = Tag.INPUT;
+
+    /** Xhtml tag for <code>ins</code>. */
+    Tag INS = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "ins";
+        }
+    };
+
+    /** Xhtml tag for <code>isindex</code>. */
+    Tag ISINDEX = Tag.ISINDEX;
+
+    /** Xhtml tag for <code>kbd</code>. */
+    Tag KBD = Tag.KBD;
+
+    /** Xhtml tag for <code>label</code>. */
+    Tag LABEL = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "label";
+        }
+    };
+
+    /** Xhtml tag for <code>legend</code>. */
+    Tag LEGEND = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "legend";
+        }
+    };
+
+    /** Xhtml tag for <code>li</code>. */
+    Tag LI = Tag.LI;
+
+    /** Xhtml tag for <code>link</code>. */
+    Tag LINK = Tag.LINK;
+
+    /** Xhtml tag for <code>map</code>. */
+    Tag MAP = Tag.MAP;
+
+    /** Xhtml tag for <code>menu</code>. */
+    Tag MENU = Tag.MENU;
+
+    /** Xhtml tag for <code>meta</code>. */
+    Tag META = Tag.META;
+
+    /** Xhtml tag for <code>noframes</code>. */
+    Tag NOFRAMES = Tag.NOFRAMES;
+
+    /** Xhtml tag for <code>noscript</code>. */
+    Tag NOSCRIPT = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "noscript";
+        }
+    };
+
+    /** Xhtml tag for <code>object</code>. */
+    Tag OBJECT = Tag.OBJECT;
+
+    /** Xhtml tag for <code>ol</code>. */
+    Tag OL = Tag.OL;
+
+    /** Xhtml tag for <code>optgroup</code>. */
+    Tag OPTGROUP = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "optgroup";
+        }
+    };
+
+    /** Xhtml tag for <code>option</code>. */
+    Tag OPTION = Tag.OPTION;
+
+    /** Xhtml tag for <code>p</code>. */
+    Tag P = Tag.P;
+
+    /** Xhtml tag for <code>param</code>. */
+    Tag PARAM = Tag.PARAM;
+
+    /** Xhtml tag for <code>pre</code>. */
+    Tag PRE = Tag.PRE;
+
+    /** Xhtml tag for <code>q</code>. */
+    Tag Q = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "q";
+        }
+    };
+
+    /** Xhtml tag for <code>s</code>. */
+    Tag S = Tag.S;
+
+    /** Xhtml tag for <code>samp</code>. */
+    Tag SAMP = Tag.SAMP;
+
+    /** Xhtml tag for <code>script</code>. */
+    Tag SCRIPT = Tag.SCRIPT;
+
+    /** Xhtml tag for <code>select</code>. */
+    Tag SELECT = Tag.SELECT;
+
+    /** Xhtml tag for <code>small</code>. */
+    Tag SMALL = Tag.SMALL;
+
+    /** Xhtml tag for <code>span</code>. */
+    Tag SPAN = Tag.SPAN;
+
+    /** Xhtml tag for <code>strike</code>. */
+    Tag STRIKE = Tag.STRIKE;
+
+    /** Xhtml tag for <code>strong</code>. */
+    Tag STRONG = Tag.STRONG;
+
+    /** Xhtml tag for <code>style</code>. */
+    Tag STYLE = Tag.STYLE;
+
+    /** Xhtml tag for <code>sub</code>. */
+    Tag SUB = Tag.SUB;
+
+    /** Xhtml tag for <code>sup</code>. */
+    Tag SUP = Tag.SUP;
+
+    /** Xhtml tag for <code>table</code>. */
+    Tag TABLE = Tag.TABLE;
+
+    /** Xhtml tag for <code>tbody</code>. */
+    Tag TBODY = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tbody";
+        }
+    };
+
+    /** Xhtml tag for <code>td</code>. */
+    Tag TD = Tag.TD;
+
+    /** Xhtml tag for <code>textarea</code>. */
+    Tag TEXTAREA = Tag.TEXTAREA;
+
+    /** Xhtml tag for <code>tfoot</code>. */
+    Tag TFOOT = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tfoot";
+        }
+    };
+
+    /** Xhtml tag for <code>th</code>. */
+    Tag TH = Tag.TH;
+
+    /** Xhtml tag for <code>thead</code>. */
+    Tag THEAD = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "thead";
+        }
+    };
+
+    /** Xhtml tag for <code>title</code>. */
+    Tag TITLE = Tag.TITLE;
+
+    /** Xhtml tag for <code>tr</code>. */
+    Tag TR = Tag.TR;
+
+    /** Xhtml tag for <code>tt</code>. */
+    Tag TT = Tag.TT;
+
+    /** Xhtml tag for <code>u</code>. */
+    Tag U = Tag.U;
+
+    /** Xhtml tag for <code>ul</code>. */
+    Tag UL = Tag.UL;
+
+    /** Xhtml tag for <code>var</code>. */
+    Tag VAR = Tag.VAR ;
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/markup/Markup.java b/doxia-core/src/main/java/org/apache/maven/doxia/markup/Markup.java
new file mode 100644
index 0000000..c560f4b
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/markup/Markup.java
@@ -0,0 +1,82 @@
+package org.apache.maven.doxia.markup;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * List of constants used by all markup syntax.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: Markup.java 657808 2008-05-19 12:11:53Z ltheussl $
+ * @since 1.0
+ */
+public interface Markup
+{
+    /** The vm line separator */
+    String EOL = System.getProperty( "line.separator" );
+
+    // ----------------------------------------------------------------------
+    // Generic separator characters
+    // ----------------------------------------------------------------------
+
+    /** equal character: '=' */
+    char EQUAL = '=';
+
+    /** end character: '>' */
+    char GREATER_THAN = '>';
+
+    /** left curly bracket character: '{' */
+    char LEFT_CURLY_BRACKET = '{';
+
+    /** left square bracket character: '[' */
+    char LEFT_SQUARE_BRACKET = '[';
+
+    /** start character: '<' */
+    char LESS_THAN = '<';
+
+    /** minus character: '-' */
+    char MINUS = '-';
+
+    /** plus character: '+' */
+    char PLUS = '+';
+
+    /** double quote character: '\"' */
+    char QUOTE = '\"';
+
+    /** right curly bracket character: '}' */
+    char RIGHT_CURLY_BRACKET = '}';
+
+    /** right square bracket character: ']' */
+    char RIGHT_SQUARE_BRACKET = ']';
+
+    /** slash character: '/' */
+    char SLASH = '/';
+
+    /** space character: ' ' */
+    char SPACE = ' ';
+
+    /** star character: '*' */
+    char STAR = '*';
+
+    /** colon character: ':' */
+    char COLON = ':';
+
+    /** semicolon character: ';' */
+    char SEMICOLON = ';';
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/markup/TextMarkup.java b/doxia-core/src/main/java/org/apache/maven/doxia/markup/TextMarkup.java
new file mode 100644
index 0000000..fe4e0a0
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/markup/TextMarkup.java
@@ -0,0 +1,38 @@
+package org.apache.maven.doxia.markup;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * List of constants used by <code>Text</code> markup syntax.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: TextMarkup.java 638290 2008-03-18 09:45:22Z bentmann $
+ * @since 1.0
+ */
+public interface TextMarkup
+    extends Markup
+{
+    // ----------------------------------------------------------------------
+    // Text separator characters
+    // ----------------------------------------------------------------------
+
+    /** pipe character: '|' */
+    char PIPE = '|';
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/markup/XmlMarkup.java b/doxia-core/src/main/java/org/apache/maven/doxia/markup/XmlMarkup.java
new file mode 100644
index 0000000..c98636a
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/markup/XmlMarkup.java
@@ -0,0 +1,54 @@
+package org.apache.maven.doxia.markup;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * List of constants used by <code>Xml</code> markup syntax.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XmlMarkup.java 785531 2009-06-17 09:47:59Z ltheussl $
+ * @since 1.0
+ */
+public interface XmlMarkup
+    extends Markup
+{
+    /** XML namespace: "http://www.w3.org/2001/XMLSchema-instance" */
+    String XML_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
+
+    // ----------------------------------------------------------------------
+    // Xml separator characters
+    // ----------------------------------------------------------------------
+
+    /** bang character: '!' */
+    char BANG = '!';
+
+    // ----------------------------------------------------------------------
+    // Xml constants
+    // ----------------------------------------------------------------------
+
+    /** CDATA. String: "CDATA". */
+    String CDATA = "CDATA";
+
+    /** DOCTYPE start. String: "<!DOCTYPE". */
+    String DOCTYPE_START = "<!DOCTYPE";
+
+    /** ENTITY start. String: "<!ENTITY". */
+    String ENTITY_START = "<!ENTITY";
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/module/site/AbstractSiteModule.java b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/AbstractSiteModule.java
new file mode 100644
index 0000000..4039ef3
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/AbstractSiteModule.java
@@ -0,0 +1,80 @@
+package org.apache.maven.doxia.module.site;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * An abstract base class that implements the SiteModule interface.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: AbstractSiteModule.java 783285 2009-06-10 10:52:19Z vsiveton $
+ * @since 1.0
+ */
+public abstract class AbstractSiteModule
+    implements SiteModule
+{
+    /** The source directory. */
+    private final String sourceDirectory;
+
+    /** The default file extension. */
+    private final String extension;
+
+    /** The default file extension. */
+    private final String parserId;
+
+    /**
+     * Constructor with null.
+     */
+    public AbstractSiteModule()
+    {
+        this( null, null, null );
+    }
+
+    /**
+     * @param sourceDirectory not null
+     * @param extension not null
+     * @param parserId not null
+     * @since 1.1.1
+     */
+    protected AbstractSiteModule( String sourceDirectory, String extension, String parserId )
+    {
+        super();
+        this.sourceDirectory = sourceDirectory;
+        this.extension = extension;
+        this.parserId = parserId;
+    }
+
+    /** {@inheritDoc} */
+    public String getSourceDirectory()
+    {
+        return sourceDirectory;
+    }
+
+    /** {@inheritDoc} */
+    public String getExtension()
+    {
+        return extension;
+    }
+
+    /** {@inheritDoc} */
+    public String getParserId()
+    {
+        return parserId;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/module/site/SiteModule.java b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/SiteModule.java
new file mode 100644
index 0000000..d8433ab
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/SiteModule.java
@@ -0,0 +1,54 @@
+package org.apache.maven.doxia.module.site;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Provides definitions for a Doxia module. This is used by the doxia site tools.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: SiteModule.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.0
+ */
+public interface SiteModule
+{
+    /** The Plexus lookup role. */
+    String ROLE = SiteModule.class.getName();
+
+    /**
+     * Returns the directory that contains source files for a given module.
+     *
+     * @return The source directory.
+     */
+    String getSourceDirectory();
+
+    /**
+     * Returns the default file extension for a given module.
+     *
+     * @return The default file extension.
+     */
+    String getExtension();
+
+    /**
+     * Returns the parser id for a given module.
+     *
+     * @return The parser id.
+     */
+    String getParserId();
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/DefaultSiteModuleManager.java b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/DefaultSiteModuleManager.java
new file mode 100644
index 0000000..a8777b5
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/DefaultSiteModuleManager.java
@@ -0,0 +1,77 @@
+package org.apache.maven.doxia.module.site.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.maven.doxia.module.site.SiteModule;
+
+/**
+ * Simple implementation of the SiteModuleManager interface.
+ *
+ * @author Jason van Zyl
+ * @version $Id: DefaultSiteModuleManager.java 698165 2008-09-23 12:52:47Z vsiveton $
+ * @since 1.0
+ * @plexus.component
+ */
+public class DefaultSiteModuleManager
+    implements SiteModuleManager
+{
+    /**
+     * @plexus.requirement role="org.apache.maven.doxia.module.site.SiteModule"
+     */
+    private Map siteModules;
+
+    private Collection siteModulesValues;
+
+    /** {@inheritDoc} */
+    public Collection getSiteModules()
+    {
+        if ( siteModulesValues == null )
+        {
+            Map siteModulesTmp = new LinkedHashMap();
+            for ( Iterator it = siteModules.values().iterator(); it.hasNext(); )
+            {
+                Object obj = it.next();
+                siteModulesTmp.put( obj.getClass(), obj );
+            }
+            siteModulesValues = siteModulesTmp.values();
+        }
+
+        return siteModulesValues;
+    }
+
+    /** {@inheritDoc} */
+    public SiteModule getSiteModule( String id )
+        throws SiteModuleNotFoundException
+    {
+        SiteModule siteModule = (SiteModule) siteModules.get( id );
+
+        if ( siteModule == null )
+        {
+            throw new SiteModuleNotFoundException( "Cannot find site module id = " + id );
+        }
+
+        return siteModule;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/SiteModuleManager.java b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/SiteModuleManager.java
new file mode 100644
index 0000000..21be9e3
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/SiteModuleManager.java
@@ -0,0 +1,55 @@
+package org.apache.maven.doxia.module.site.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.site.SiteModule;
+
+import java.util.Collection;
+
+/**
+ * Handles SiteModule lookups.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: SiteModuleManager.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.0
+ */
+public interface SiteModuleManager
+{
+    /** The Plexus lookup role. */
+    String ROLE = SiteModuleManager.class.getName();
+
+    /**
+     * Returns a collection of SiteModules.
+     *
+     * @return The SiteModules.
+     */
+    Collection getSiteModules();
+
+    /**
+     * Returns the SiteModule that corresponds to the given id.
+     *
+     * @param id The identifier.
+     * @return The corresponding SiteModule.
+     * @throws org.apache.maven.doxia.module.site.manager.SiteModuleNotFoundException if no SiteModule could be found
+     * for the given id.
+     */
+    SiteModule getSiteModule( String id )
+        throws SiteModuleNotFoundException;
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/SiteModuleNotFoundException.java b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/SiteModuleNotFoundException.java
new file mode 100644
index 0000000..a8507f8
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/module/site/manager/SiteModuleNotFoundException.java
@@ -0,0 +1,75 @@
+package org.apache.maven.doxia.module.site.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Encapsulate a Doxia exception that indicates that a SiteModule
+ * does not exist or could not be found.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: SiteModuleNotFoundException.java 662767 2008-06-03 12:25:07Z ltheussl $
+ * @since 1.0
+ */
+public class SiteModuleNotFoundException
+    extends Exception
+{
+    /** serialVersionUID */
+    static final long serialVersionUID = 295967936746221567L;
+
+    /**
+     * Construct a new SiteModuleNotFoundException with the
+     * specified detail message.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the Throwable.getMessage() method.
+     */
+    public SiteModuleNotFoundException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Constructs a new SiteModuleNotFoundException with the specified cause.
+     * The error message is (cause == null ? null : cause.toString() ).
+     *
+     * @param cause the cause. This can be retrieved later by the
+     * Throwable.getCause() method. (A null value is permitted, and indicates
+     * that the cause is nonexistent or unknown.)
+     */
+    public SiteModuleNotFoundException( Throwable cause )
+    {
+        super( cause );
+    }
+
+    /**
+     * Construct a new SiteModuleNotFoundException with the specified
+     * detail message and cause.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the Throwable.getMessage() method.
+     * @param cause The cause. This can be retrieved later by the
+     * Throwable.getCause() method. (A null value is permitted, and indicates
+     * that the cause is nonexistent or unknown.)
+     */
+    public SiteModuleNotFoundException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractParser.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractParser.java
new file mode 100644
index 0000000..11419c1
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractParser.java
@@ -0,0 +1,184 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.StringReader;
+
+import org.apache.maven.doxia.logging.Log;
+import org.apache.maven.doxia.logging.SystemStreamLog;
+import org.apache.maven.doxia.macro.Macro;
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.macro.manager.MacroManager;
+import org.apache.maven.doxia.macro.manager.MacroNotFoundException;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * An abstract base class that defines some convenience methods for parsers.
+ * Provides a macro mechanism to give dynamic functionalities for the parsing.
+ *
+ * @author Jason van Zyl
+ * @version $Id: AbstractParser.java 946932 2010-05-21 08:36:30Z ltheussl $
+ * @since 1.0
+ * @plexus.component
+ */
+public abstract class AbstractParser
+    implements Parser
+{
+    /** Indicates that a second parsing is required. */
+    private boolean secondParsing = false;
+
+    /** @plexus.requirement */
+    private MacroManager macroManager;
+
+    /** Log instance. */
+    private Log logger;
+
+    /** {@inheritDoc} */
+    public int getType()
+    {
+        return UNKNOWN_TYPE;
+    }
+
+    /**
+     * Execute a macro on the given sink.
+     *
+     * @param macroId An id to lookup the macro.
+     * @param request The corresponding MacroRequest.
+     * @param sink The sink to receive the events.
+     * @throws org.apache.maven.doxia.macro.MacroExecutionException if an error occurred during execution.
+     * @throws org.apache.maven.doxia.macro.manager.MacroNotFoundException if the macro could not be found.
+     */
+    // Made public right now because of the structure of the APT parser and
+    // all its inner classes.
+    public void executeMacro( String macroId, MacroRequest request, Sink sink )
+        throws MacroExecutionException, MacroNotFoundException
+    {
+        Macro macro = getMacroManager().getMacro( macroId );
+
+        macro.enableLogging( getLog() );
+
+        macro.execute( sink, request );
+    }
+
+    /**
+     * Returns the current base directory.
+     *
+     * @return The base directory.
+     *
+     * @deprecated this does not work in multi-module builds, see DOXIA-373
+     */
+    protected File getBasedir()
+    {
+        // TODO: This is baaad, it should come in with the request.
+        // (this is only used for macro requests, see AptParser)
+
+        String basedir = System.getProperty( "basedir" );
+
+        if ( basedir != null )
+        {
+            return new File( basedir );
+        }
+
+        return new File( new File( "" ).getAbsolutePath() );
+    }
+
+    /**
+     * Convenience method to parse an arbitrary string and emit events into the given sink.
+     *
+     * @param string A string that provides the source input.
+     * @param sink A sink that consumes the Doxia events.
+     * @throws org.apache.maven.doxia.parser.ParseException if the string could not be parsed.
+     * @since 1.1
+     */
+    public void parse( String string, Sink sink )
+        throws ParseException
+    {
+        parse( new StringReader( string ), sink );
+    }
+
+    /**
+     * Set <code>secondParsing</code> to true, if we need a second parsing.
+     *
+     * @param second True for second parsing.
+     */
+    public void setSecondParsing( boolean second )
+    {
+        this.secondParsing = second;
+    }
+
+    /**
+     * Indicates if we are currently parsing a second time.
+     *
+     * @return true if we are currently parsing a second time.
+     * @since 1.1
+     */
+    protected boolean isSecondParsing()
+    {
+        return secondParsing;
+    }
+
+    /** {@inheritDoc} */
+    public void enableLogging( Log log )
+    {
+        this.logger = log;
+    }
+
+    /**
+     * Returns the current logger for this parser.
+     * If no logger has been configured yet, a new SystemStreamLog is returned.
+     *
+     * @return Log
+     * @since 1.1
+     */
+    protected Log getLog()
+    {
+        if ( logger == null )
+        {
+            logger = new SystemStreamLog();
+        }
+
+        return logger;
+    }
+
+    /**
+     * Gets the current {@link MacroManager}.
+     *
+     * @return The current {@link MacroManager}.
+     * @since 1.1
+     */
+    protected MacroManager getMacroManager()
+    {
+        return macroManager;
+    }
+
+    /**
+     * Initialize the parser. This is called first by
+     * {@link #parse(java.io.Reader, org.apache.maven.doxia.sink.Sink)} and can be used
+     * to set the parser into a clear state so it can be re-used.
+     *
+     * @since 1.1.2
+     */
+    protected void init()
+    {
+        // nop
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractTextParser.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractTextParser.java
new file mode 100644
index 0000000..d5adf47
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractTextParser.java
@@ -0,0 +1,40 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.markup.TextMarkup;
+
+/**
+ * An abstract class that defines some convenience methods for <code>Text</code> parsers.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractTextParser.java 638290 2008-03-18 09:45:22Z bentmann $
+ * @since 1.0
+ */
+public abstract class AbstractTextParser
+    extends AbstractParser
+    implements TextMarkup
+{
+    /** {@inheritDoc} */
+    public final int getType()
+    {
+        return TXT_TYPE;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractXmlParser.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractXmlParser.java
new file mode 100644
index 0000000..b7b3eb7
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractXmlParser.java
@@ -0,0 +1,878 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.util.EntityUtils;
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.markup.XmlMarkup;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.util.HtmlTools;
+import org.apache.maven.doxia.util.XmlValidator;
+
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.pull.MXParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * An abstract class that defines some convenience methods for <code>XML</code> parsers.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractXmlParser.java 1035744 2010-11-16 18:48:53Z dennisl $
+ * @since 1.0
+ */
+public abstract class AbstractXmlParser
+    extends AbstractParser
+    implements XmlMarkup
+{
+    /**
+     * Entity pattern for HTML entity, i.e. &nbsp;
+     * "<!ENTITY(\\s)+([^>|^\\s]+)(\\s)+\"(\\s)*(&[a-zA-Z]{2,6};)(\\s)*\"(\\s)*>
+     * <br/>
+     * see <a href="http://www.w3.org/TR/REC-xml/#NT-EntityDecl">http://www.w3.org/TR/REC-xml/#NT-EntityDecl</a>.
+     */
+    private static final Pattern PATTERN_ENTITY_1 =
+        Pattern.compile( ENTITY_START + "(\\s)+([^>|^\\s]+)(\\s)+\"(\\s)*(&[a-zA-Z]{2,6};)(\\s)*\"(\\s)*>" );
+
+    /**
+     * Entity pattern for Unicode entity, i.e. &#38;
+     * "<!ENTITY(\\s)+([^>|^\\s]+)(\\s)+\"(\\s)*(&(#x?[0-9a-fA-F]{1,5};)*)(\\s)*\"(\\s)*>"
+     * <br/>
+     * see <a href="http://www.w3.org/TR/REC-xml/#NT-EntityDecl">http://www.w3.org/TR/REC-xml/#NT-EntityDecl</a>.
+     */
+    private static final Pattern PATTERN_ENTITY_2 =
+        Pattern.compile( ENTITY_START + "(\\s)+([^>|^\\s]+)(\\s)+\"(\\s)*(&(#x?[0-9a-fA-F]{1,5};)*)(\\s)*\"(\\s)*>" );
+
+    private boolean ignorableWhitespace;
+
+    private boolean collapsibleWhitespace;
+
+    private boolean trimmableWhitespace;
+
+    private Map entities;
+
+    private boolean validate = false;
+
+    /** {@inheritDoc} */
+    public void parse( Reader source, Sink sink )
+        throws ParseException
+    {
+        init();
+
+        // 1 first parsing if validation is required
+        if ( isValidate() )
+        {
+            String content;
+            try
+            {
+                content = IOUtil.toString( new BufferedReader( source ) );
+            }
+            catch ( IOException e )
+            {
+                throw new ParseException( "Error reading the model: " + e.getMessage(), e );
+            }
+
+            new XmlValidator( getLog() ).validate( content );
+
+            source = new StringReader( content );
+        }
+
+        // 2 second parsing to process
+        try
+        {
+            XmlPullParser parser = new MXParser();
+
+            parser.setInput( source );
+
+            sink.enableLogging( getLog() );
+
+            parseXml( parser, sink );
+        }
+        catch ( XmlPullParserException ex )
+        {
+            throw new ParseException( "Error parsing the model: " + ex.getMessage(), ex, ex.getLineNumber(),
+                                      ex.getColumnNumber() );
+        }
+        catch ( MacroExecutionException ex )
+        {
+            throw new ParseException( "Macro execution failed: " + ex.getMessage(), ex );
+        }
+
+        setSecondParsing( false );
+        init();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Convenience method to parse an arbitrary string and emit any xml events into the given sink.
+     */
+    public void parse( String string, Sink sink )
+        throws ParseException
+    {
+        super.parse( string, sink );
+    }
+
+    /** {@inheritDoc} */
+    public final int getType()
+    {
+        return XML_TYPE;
+    }
+
+    /**
+     * Converts the attributes of the current start tag of the given parser to a SinkEventAttributeSet.
+     *
+     * @param parser A parser, not null.
+     * @return a SinkEventAttributeSet or null if the current parser event is not a start tag.
+     * @since 1.1
+     */
+    protected SinkEventAttributeSet getAttributesFromParser( XmlPullParser parser )
+    {
+        int count = parser.getAttributeCount();
+
+        if ( count < 0 )
+        {
+            return null;
+        }
+
+        SinkEventAttributeSet atts = new SinkEventAttributeSet( count );
+
+        for ( int i = 0; i < count; i++ )
+        {
+            atts.addAttribute( parser.getAttributeName( i ), parser.getAttributeValue( i ) );
+        }
+
+        return atts;
+    }
+
+    /**
+     * Parse the model from the XmlPullParser into the given sink.
+     *
+     * @param parser A parser, not null.
+     * @param sink the sink to receive the events.
+     * @throws org.codehaus.plexus.util.xml.pull.XmlPullParserException if there's a problem parsing the model
+     * @throws org.apache.maven.doxia.macro.MacroExecutionException if there's a problem executing a macro
+     */
+    private void parseXml( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        int eventType = parser.getEventType();
+
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                handleStartTag( parser, sink );
+            }
+            else if ( eventType == XmlPullParser.END_TAG )
+            {
+                handleEndTag( parser, sink );
+            }
+            else if ( eventType == XmlPullParser.TEXT )
+            {
+                String text = getText( parser );
+
+                if ( isIgnorableWhitespace() )
+                {
+                    if ( !text.trim().equals( "" ) )
+                    {
+                        handleText( parser, sink );
+                    }
+                }
+                else
+                {
+                    handleText( parser, sink );
+                }
+            }
+            else if ( eventType == XmlPullParser.CDSECT )
+            {
+                handleCdsect( parser, sink );
+            }
+            else if ( eventType == XmlPullParser.COMMENT )
+            {
+                handleComment( parser, sink );
+            }
+            else if ( eventType == XmlPullParser.ENTITY_REF )
+            {
+                handleEntity( parser, sink );
+            }
+            else if ( eventType == XmlPullParser.IGNORABLE_WHITESPACE )
+            {
+                // nop
+            }
+            else if ( eventType == XmlPullParser.PROCESSING_INSTRUCTION )
+            {
+                // nop
+            }
+            else if ( eventType == XmlPullParser.DOCDECL )
+            {
+                addLocalEntities( parser, parser.getText() );
+
+                for ( Iterator it = CachedFileEntityResolver.ENTITY_CACHE.values().iterator(); it.hasNext(); )
+                {
+                    byte[] res = (byte[]) it.next();
+
+                    addDTDEntities( parser, new String( res ) );
+                }
+            }
+
+            try
+            {
+                eventType = parser.nextToken();
+            }
+            catch ( IOException io )
+            {
+                throw new XmlPullParserException( "IOException: " + io.getMessage(), parser, io );
+            }
+        }
+    }
+
+    /**
+     * Goes through the possible start tags.
+     *
+     * @param parser A parser, not null.
+     * @param sink the sink to receive the events.
+     * @throws org.codehaus.plexus.util.xml.pull.XmlPullParserException if there's a problem parsing the model
+     * @throws org.apache.maven.doxia.macro.MacroExecutionException if there's a problem executing a macro
+     */
+    protected abstract void handleStartTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException;
+
+    /**
+     * Goes through the possible end tags.
+     *
+     * @param parser A parser, not null.
+     * @param sink the sink to receive the events.
+     * @throws org.codehaus.plexus.util.xml.pull.XmlPullParserException if there's a problem parsing the model
+     * @throws org.apache.maven.doxia.macro.MacroExecutionException if there's a problem executing a macro
+     */
+    protected abstract void handleEndTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException;
+
+    /**
+     * Handles text events.
+     *
+     * <p>This is a default implementation, if the parser points to a non-empty text element,
+     * it is emitted as a text event into the specified sink.</p>
+     *
+     * @param parser A parser, not null.
+     * @param sink the sink to receive the events. Not null.
+     * @throws org.codehaus.plexus.util.xml.pull.XmlPullParserException if there's a problem parsing the model
+     */
+    protected void handleText( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        String text = getText( parser );
+
+        /*
+         * NOTE: Don't do any whitespace trimming here. Whitespace normalization has already been performed by the
+         * parser so any whitespace that makes it here is significant.
+         */
+        if ( StringUtils.isNotEmpty( text ) )
+        {
+            sink.text( text );
+        }
+    }
+
+    /**
+     * Handles CDATA sections.
+     *
+     * <p>This is a default implementation, all data are emitted as text
+     * events into the specified sink.</p>
+     *
+     * @param parser A parser, not null.
+     * @param sink the sink to receive the events. Not null.
+     * @throws org.codehaus.plexus.util.xml.pull.XmlPullParserException if there's a problem parsing the model
+     */
+    protected void handleCdsect( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        sink.text( getText( parser ) );
+    }
+
+    /**
+     * Handles comments.
+     *
+     * <p>This is a default implementation, all data are emitted as comment
+     * events into the specified sink.</p>
+     *
+     * @param parser A parser, not null.
+     * @param sink the sink to receive the events. Not null.
+     * @throws org.codehaus.plexus.util.xml.pull.XmlPullParserException if there's a problem parsing the model
+     */
+    protected void handleComment( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        sink.comment( getText( parser ).trim() );
+    }
+
+    /**
+     * Handles entities.
+     *
+     * <p>This is a default implementation, all entities are resolved and emitted as text
+     * events into the specified sink, except:</p>
+     * <ul>
+     * <li>the entities with names <code>#160</code>, <code>nbsp</code> and <code>#x00A0</code>
+     * are emitted as <code>nonBreakingSpace()</code> events.</li>
+     * </ul>
+     *
+     * @param parser A parser, not null.
+     * @param sink the sink to receive the events. Not null.
+     * @throws org.codehaus.plexus.util.xml.pull.XmlPullParserException if there's a problem parsing the model
+     */
+    protected void handleEntity( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        String text = getText( parser );
+
+        String name = parser.getName();
+
+        if ( "#160".equals( name ) || "nbsp".equals( name ) || "#x00A0".equals( name ) )
+        {
+            sink.nonBreakingSpace();
+        }
+        else
+        {
+            String unescaped = HtmlTools.unescapeHTML( text );
+
+            sink.text( unescaped );
+        }
+    }
+
+    /**
+     * Handles an unkown event.
+     *
+     * <p>This is a default implementation, all events are emitted as unknown
+     * events into the specified sink.</p>
+     *
+     * @param parser the parser to get the event from.
+     * @param sink the sink to receive the event.
+     * @param type the tag event type. This should be one of HtmlMarkup.TAG_TYPE_SIMPLE,
+     * HtmlMarkup.TAG_TYPE_START, HtmlMarkup.TAG_TYPE_END or HtmlMarkup.ENTITY_TYPE.
+     * It will be passed as the first argument of the required parameters to the Sink
+     * {@link org.apache.maven.doxia.sink.Sink#unknown(String, Object[], org.apache.maven.doxia.sink.SinkEventAttributes)}
+     * method.
+     */
+    protected void handleUnknown( XmlPullParser parser, Sink sink, int type )
+    {
+        Object[] required = new Object[] { new Integer( type ) };
+
+        SinkEventAttributeSet attribs = getAttributesFromParser( parser );
+
+        sink.unknown( parser.getName(), required, attribs );
+    }
+
+    /**
+     * <p>isIgnorableWhitespace.</p>
+     *
+     * @return <code>true</code> if whitespace will be ignored, <code>false</code> otherwise.
+     * @see #setIgnorableWhitespace(boolean)
+     * @since 1.1
+     */
+    protected boolean isIgnorableWhitespace()
+    {
+        return ignorableWhitespace;
+    }
+
+    /**
+     * Specify that whitespace will be ignored. I.e.:
+     * <pre><tr> <td/> </tr></pre>
+     * is equivalent to
+     * <pre><tr><td/></tr></pre>
+     *
+     * @param ignorable <code>true</code> to ignore whitespace, <code>false</code> otherwise.
+     * @since 1.1
+     */
+    protected void setIgnorableWhitespace( boolean ignorable )
+    {
+        this.ignorableWhitespace = ignorable;
+    }
+
+    /**
+     * <p>isCollapsibleWhitespace.</p>
+     *
+     * @return <code>true</code> if text will collapse, <code>false</code> otherwise.
+     * @see #setCollapsibleWhitespace(boolean)
+     * @since 1.1
+     */
+    protected boolean isCollapsibleWhitespace()
+    {
+        return collapsibleWhitespace;
+    }
+
+    /**
+     * Specify that text will be collapsed. I.e.:
+     * <pre>Text   Text</pre>
+     * is equivalent to
+     * <pre>Text Text</pre>
+     *
+     * @param collapsible <code>true</code> to allow collapsible text, <code>false</code> otherwise.
+     * @since 1.1
+     */
+    protected void setCollapsibleWhitespace( boolean collapsible )
+    {
+        this.collapsibleWhitespace = collapsible;
+    }
+
+    /**
+     * <p>isTrimmableWhitespace.</p>
+     *
+     * @return <code>true</code> if text will be trim, <code>false</code> otherwise.
+     * @see #setTrimmableWhitespace(boolean)
+     * @since 1.1
+     */
+    protected boolean isTrimmableWhitespace()
+    {
+        return trimmableWhitespace;
+    }
+
+    /**
+     * Specify that text will be collapsed. I.e.:
+     * <pre><p> Text </p></pre>
+     * is equivalent to
+     * <pre><p>Text</p></pre>
+     *
+     * @param trimmable <code>true</code> to allow trimmable text, <code>false</code> otherwise.
+     * @since 1.1
+     */
+    protected void setTrimmableWhitespace( boolean trimmable )
+    {
+        this.trimmableWhitespace = trimmable;
+    }
+
+    /**
+     * <p>getText.</p>
+     *
+     * @param parser A parser, not null.
+     * @return the {@link XmlPullParser#getText()} taking care of trimmable or collapsible configuration.
+     * @see XmlPullParser#getText()
+     * @see #isCollapsibleWhitespace()
+     * @see #isTrimmableWhitespace()
+     * @since 1.1
+     */
+    protected String getText( XmlPullParser parser )
+    {
+        String text = parser.getText();
+
+        if ( isTrimmableWhitespace() )
+        {
+            text = text.trim();
+        }
+
+        if ( isCollapsibleWhitespace() )
+        {
+            StringBuffer newText = new StringBuffer();
+            String[] elts = StringUtils.split( text, " \r\n" );
+            for ( int i = 0; i < elts.length; i++ )
+            {
+                newText.append( elts[i] );
+                if ( ( i + 1 ) < elts.length )
+                {
+                    newText.append( " " );
+                }
+            }
+            text = newText.toString();
+        }
+
+        return text;
+    }
+
+    /**
+     * Return the defined entities in a local doctype. I.e.:
+     * <pre>
+     * <!DOCTYPE foo [
+     *   <!ENTITY bar "&#x160;">
+     *   <!ENTITY bar1 "&#x161;">
+     * ]>
+     * </pre>
+     *
+     * @return a map of the defined entities in a local doctype.
+     * @since 1.1
+     */
+    protected Map getLocalEntities()
+    {
+        if ( entities == null )
+        {
+            entities = new LinkedHashMap();
+        }
+
+        return entities;
+    }
+
+    /**
+     * <p>isValidate.</p>
+     *
+     * @return <code>true</code> if XML content will be validate, <code>false</code> otherwise.
+     * @since 1.1
+     */
+    public boolean isValidate()
+    {
+        return validate;
+    }
+
+    /**
+     * Specify a flag to validate or not the XML content.
+     *
+     * @param validate the validate to set
+     * @see #parse(Reader, Sink)
+     * @since 1.1
+     */
+    public void setValidate( boolean validate )
+    {
+        this.validate = validate;
+    }
+
+    // ----------------------------------------------------------------------
+    // Private methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Add an entity given by <code>entityName</code> and <code>entityValue</code> to {@link #entities}.
+     * <br/>
+     * By default, we exclude the default XML entities: &amp;, &lt;, &gt;, &quot; and &apos;.
+     *
+     * @param parser not null
+     * @param entityName not null
+     * @param entityValue not null
+     * @throws XmlPullParserException if any
+     * @see {@link XmlPullParser#defineEntityReplacementText(String, String)}
+     */
+    private void addEntity( XmlPullParser parser, String entityName, String entityValue )
+        throws XmlPullParserException
+    {
+        if ( entityName.endsWith( "amp" ) || entityName.endsWith( "lt" ) || entityName.endsWith( "gt" )
+            || entityName.endsWith( "quot" ) || entityName.endsWith( "apos" ) )
+        {
+            return;
+        }
+
+        parser.defineEntityReplacementText( entityName, entityValue );
+        getLocalEntities().put( entityName, entityValue );
+    }
+
+    /**
+     * Handle entities defined in a local doctype as the following:
+     * <pre>
+     * <!DOCTYPE foo [
+     *   <!ENTITY bar "&#x160;">
+     *   <!ENTITY bar1 "&#x161;">
+     * ]>
+     * </pre>
+     *
+     * @param parser not null
+     * @param text not null
+     * @throws XmlPullParserException if any
+     */
+    private void addLocalEntities( XmlPullParser parser, String text )
+        throws XmlPullParserException
+    {
+        int entitiesCount = StringUtils.countMatches( text, ENTITY_START );
+        if ( entitiesCount > 0 )
+        {
+            // text should be foo [...]
+            int start = text.indexOf( "[" );
+            int end = text.lastIndexOf( "]" );
+            if ( start != -1 && end != -1 )
+            {
+                text = text.substring( start + 1, end );
+                addDTDEntities( parser, text );
+            }
+        }
+    }
+
+    /**
+     * Handle entities defined in external doctypes as the following:
+     * <pre>
+     * <!DOCTYPE foo [
+     *   <!-- These are the entity sets for ISO Latin 1 characters for the XHTML -->
+     *   <!ENTITY % HTMLlat1 PUBLIC "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+     *          "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
+     *   %HTMLlat1;
+     * ]>
+     * </pre>
+     *
+     * @param parser not null
+     * @param text not null
+     * @throws XmlPullParserException if any
+     */
+    private void addDTDEntities( XmlPullParser parser, String text )
+        throws XmlPullParserException
+    {
+        int entitiesCount = StringUtils.countMatches( text, ENTITY_START );
+        if ( entitiesCount > 0 )
+        {
+            text = StringUtils.replace( text, ENTITY_START, "\n" + ENTITY_START );
+            BufferedReader reader = new BufferedReader( new StringReader( text ) );
+            String line;
+            String tmpLine = "";
+            try
+            {
+                Matcher matcher;
+                while ( ( line = reader.readLine() ) != null )
+                {
+                    tmpLine += "\n" + line;
+                    matcher = PATTERN_ENTITY_1.matcher( tmpLine );
+                    if ( matcher.find() && matcher.groupCount() == 7 )
+                    {
+                        String entityName = matcher.group( 2 );
+                        String entityValue = matcher.group( 5 );
+
+                        addEntity( parser, entityName, entityValue );
+                        tmpLine = "";
+                    }
+                    else
+                    {
+                        matcher = PATTERN_ENTITY_2.matcher( tmpLine );
+                        if ( matcher.find() && matcher.groupCount() == 8 )
+                        {
+                            String entityName = matcher.group( 2 );
+                            String entityValue = matcher.group( 5 );
+
+                            addEntity( parser, entityName, entityValue );
+                            tmpLine = "";
+                        }
+                    }
+                }
+            }
+            catch ( IOException e )
+            {
+                // nop
+            }
+            finally
+            {
+                IOUtil.close( reader );
+            }
+        }
+    }
+
+    /**
+     * Implementation of the callback mechanism <code>EntityResolver</code>.
+     * Using a mechanism of cached files in temp dir to improve performance when using the <code>XMLReader</code>.
+     */
+    public static class CachedFileEntityResolver
+        implements EntityResolver
+    {
+        /** Map with systemId as key and the content of systemId as byte[]. */
+        protected static final Map ENTITY_CACHE = new Hashtable();
+
+        /** {@inheritDoc} */
+        public InputSource resolveEntity( String publicId, String systemId )
+            throws SAXException, IOException
+        {
+            byte[] res = (byte[]) ENTITY_CACHE.get( systemId );
+            // already cached?
+            if ( res == null )
+            {
+                String systemName = FileUtils.getFile( systemId ).getName();
+                File temp = new File( System.getProperty( "java.io.tmpdir" ), systemName );
+                // maybe already as a temp file?
+                if ( !temp.exists() )
+                {
+                    // is systemId a file or an url?
+                    if ( systemId.toLowerCase( Locale.ENGLISH ).startsWith( "file" ) )
+                    {
+                        // Doxia XSDs are included in the jars, so try to find the resource systemName from
+                        // the classpath...
+                        String resource = "/" + systemName;
+                        URL url = getClass().getResource( resource );
+                        if ( url != null )
+                        {
+                            res = toByteArray( url );
+                        }
+                        else
+                        {
+                            throw new SAXException( "Could not find the SYSTEM entity: " + systemId
+                            + " because '" + resource + "' is not available of the classpath." );
+                        }
+                    }
+                    else
+                    {
+                        res = toByteArray( new URL( systemId ) );
+                    }
+
+                    // write systemId as temp file
+                    copy( res, temp );
+                }
+                else
+                {
+                    // TODO How to refresh Doxia XSDs from temp dir?
+                    res = toByteArray( temp.toURI().toURL() );
+                }
+
+                ENTITY_CACHE.put( systemId, res );
+            }
+
+            InputSource is = new InputSource( new ByteArrayInputStream( res ) );
+            is.setPublicId( publicId );
+            is.setSystemId( systemId );
+
+            return is;
+        }
+
+        /**
+         * If url is not an http/https urls, call {@link IOUtil#toByteArray(java.io.InputStream)} to get the url
+         * content.
+         * Otherwise, use HttpClient to get the http content.
+         * Wrap all internal exceptions to throw SAXException.
+         *
+         * @param url not null
+         * @return return an array of byte
+         * @throws SAXException if any
+         */
+        private static byte[] toByteArray( URL url )
+            throws SAXException
+        {
+            if ( !( url.getProtocol().equalsIgnoreCase( "http" ) || url.getProtocol().equalsIgnoreCase( "https" ) ) )
+            {
+                InputStream is = null;
+                try
+                {
+                    is = url.openStream();
+                    if ( is == null )
+                    {
+                        throw new SAXException( "Cannot open stream from the url: " + url.toString() );
+                    }
+                    return IOUtil.toByteArray( is );
+                }
+                catch ( IOException e )
+                {
+                    throw new SAXException( "IOException: " + e.getMessage(), e );
+                }
+                finally
+                {
+                    IOUtil.close( is );
+                }
+            }
+
+            // it is an HTTP url, using HttpClient...
+            DefaultHttpClient client = new DefaultHttpClient();
+            HttpGet method = new HttpGet( url.toString() );
+            // Set a user-agent that doesn't contain the word "java", otherwise it will be blocked by the W3C
+            // The default user-agent is "Apache-HttpClient/4.0.2 (java 1.5)"
+            method.setHeader( "user-agent", "Apache-Doxia/1.1.4" );
+
+            HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler( 3, false );
+            client.setHttpRequestRetryHandler(retryHandler);
+
+            HttpEntity entity = null;
+            try
+            {
+                HttpResponse response = client.execute( method );
+                int statusCode = response.getStatusLine().getStatusCode();
+                if ( statusCode != HttpStatus.SC_OK )
+                {
+                    throw new IOException( "The status code when accessing the URL '" + url.toString() + "' was "
+                        + statusCode + ", which is not allowed. The server gave this reason for the failure '"
+                        + response.getStatusLine().getReasonPhrase() + "'.");
+                }
+
+                entity = response.getEntity();
+                return EntityUtils.toByteArray( entity );
+            }
+            catch ( ClientProtocolException e )
+            {
+                throw new SAXException( "ClientProtocolException: Fatal protocol violation: " + e.getMessage(), e );
+            }
+            catch ( IOException e )
+            {
+                throw new SAXException( "IOException: Fatal transport error: " + e.getMessage(), e );
+            }
+            finally
+            {
+                if ( entity != null ) {
+                    try
+                    {
+                        entity.consumeContent();
+                    }
+                    catch ( IOException e )
+                    {
+                        // Ignore
+                    }
+                }
+            }
+        }
+
+        /**
+         * Wrap {@link IOUtil#copy(byte[], OutputStream)} to throw SAXException.
+         *
+         * @param res not null array of byte
+         * @param f the file where to write the bytes
+         * @throws SAXException if any
+         * @see {@link IOUtil#copy(byte[], OutputStream)}
+         */
+        private void copy( byte[] res, File f )
+            throws SAXException
+        {
+            if ( f.isDirectory() )
+            {
+                throw new SAXException( "'" + f.getAbsolutePath() + "' is a directory, can not write it." );
+            }
+
+            OutputStream os = null;
+            try
+            {
+                os = new FileOutputStream( f );
+                IOUtil.copy( res, os );
+            }
+            catch ( IOException e )
+            {
+                throw new SAXException( "IOException: " + e.getMessage(), e );
+            }
+            finally
+            {
+                IOUtil.close( os );
+            }
+        }
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/ParseException.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/ParseException.java
new file mode 100644
index 0000000..a54dd20
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/ParseException.java
@@ -0,0 +1,228 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Encapsulate a Doxia parse error.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: ParseException.java 747780 2009-02-25 13:55:23Z vsiveton $
+ * @since 1.0
+ */
+public class ParseException
+    extends Exception
+{
+    /** serialVersionUID */
+    static final long serialVersionUID = 295967936746221567L;
+
+    /** The file that caused the ParseException. */
+    private String fileName;
+
+    /** Line number where the parse exception occurred. */
+    private int lineNumber;
+
+    /** Column number where the parse exception occurred. */
+    private int columnNumber;
+
+    /**
+     * Construct a new <code>ParseException</code> with the specified detail message.
+     * <br/>
+     * <b>Note</b>: no line or column number will be used.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     */
+    public ParseException( String message )
+    {
+        this( null, message, null, -1, -1 );
+    }
+
+    /**
+     * Construct a new <code>ParseException</code> with the specified detail message and cause.
+     * <br/>
+     * <b>Note</b>: no line or column number will be used.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     */
+    public ParseException( String message, Exception e )
+    {
+        this( e, message, null, -1, -1 );
+    }
+
+    /**
+     * Construct a new <code>ParseException</code> with the specified detail message,
+     * line number and column number.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     * @param line The line number where the parsing failed.
+     * This can later be retrieved by the getLineNumber() method.
+     * @param column The column number where the parsing failed.
+     * This can later be retrieved by the getColumnNumber() method.
+     * @since 1.1
+     */
+    public ParseException( String message, int line, int column )
+    {
+        this( null, message, null, line, column );
+    }
+
+    /**
+     * Construct a new <code>ParseException</code> with the specified detail message and cause,
+     * line number and column number.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     * @param line The line number where the parsing failed.
+     * This can later be retrieved by the getLineNumber() method.
+     * @param column The column number where the parsing failed.
+     * This can later be retrieved by the getColumnNumber() method.
+     * @since 1.1
+     */
+    public ParseException( String message, Exception e, int line, int column )
+    {
+        this( e, message, null, line, column );
+    }
+
+    /**
+     * Constructs a new exception with the specified cause. The error message is
+     *  (cause == null ? null : cause.toString() ).
+     *
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     * @deprecated Using {@link #ParseException(Exception, int, int)} to specify the line and column number.
+     */
+    public ParseException( Exception e )
+    {
+        this( e, null, null, -1, -1 );
+    }
+
+    /**
+     * Constructs a new exception with the specified cause, line number and column number. The error message is
+     *  (cause == null ? null : cause.toString() ).
+     *
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     * @param line The line number where the parsing failed.
+     * This can later be retrieved by the getLineNumber() method.
+     * @param column The column number where the parsing failed.
+     * This can later be retrieved by the getColumnNumber() method.
+     * @since 1.1
+     */
+    public ParseException( Exception e, int line, int column )
+    {
+        this( e, null, null, line, column );
+    }
+
+    /**
+     * Construct a new <code>ParseException</code> with the specified cause,
+     * filename and linenumber.
+     *
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     * @param file Name of a file that couldn't be parsed.
+     * This can later be retrieved by the getFileName() method.
+     * @param line The line number where the parsing failed.
+     * This can later be retrieved by the getLineNumber() method.
+     * @deprecated Using {@link #ParseException(Exception, String, int, int)} to specify the column number.
+     */
+    public ParseException( Exception e, String file, int line )
+    {
+        this( e, null, file, line, -1 );
+    }
+
+    /**
+     * Construct a new <code>ParseException</code> with the specified cause,
+     * filename, line number and column number.
+     *
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     * @param file Name of a file that couldn't be parsed.
+     * This can later be retrieved by the getFileName() method.
+     * @param line The line number where the parsing failed.
+     * This can later be retrieved by the getLineNumber() method.
+     * @param column The column number where the parsing failed.
+     * This can later be retrieved by the getColumnNumber() method.
+     */
+    public ParseException( Exception e, String file, int line, int column )
+    {
+        this( e, null, file, line, column );
+    }
+
+    /**
+     * Construct a new <code>ParseException</code> with the specified cause, detail message,
+     * filename, line number and column number.
+     *
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     * @param file Name of a file that couldn't be parsed.
+     * This can later be retrieved by the getFileName() method.
+     * @param line The line number where the parsing failed.
+     * This can later be retrieved by the getLineNumber() method.
+     * @param column The column number where the parsing failed.
+     * This can later be retrieved by the getColumnNumber() method.
+     * @since 1.1
+     */
+    public ParseException( Exception e, String message, String file, int line, int column )
+    {
+        super( ( message == null ) ? ( ( e == null ) ? null : e.getMessage() ) : message, e );
+
+        this.fileName = file;
+        this.lineNumber = line;
+        this.columnNumber = column;
+    }
+
+    /**
+     * <p>Getter for the field <code>fileName</code>.</p>
+     *
+     * @return the file name that caused the <code>ParseException</code>.
+     */
+    public String getFileName()
+    {
+        return fileName;
+    }
+
+    /**
+     * <p>Getter for the field <code>lineNumber</code>.</p>
+     *
+     * @return the line number where the <code>ParseException</code> occurred.
+     */
+    public int getLineNumber()
+    {
+        return lineNumber;
+    }
+
+    /**
+     * <p>Getter for the field <code>columnNumber</code>.</p>
+     *
+     * @return the column number where the <code>ParseException</code> occurred.
+     * @since 1.1
+     */
+    public int getColumnNumber()
+    {
+        return columnNumber;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/Parser.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/Parser.java
new file mode 100644
index 0000000..43ba394
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/Parser.java
@@ -0,0 +1,69 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.logging.LogEnabled;
+import org.apache.maven.doxia.sink.Sink;
+
+import java.io.Reader;
+
+/**
+ * A Parser is responsible for parsing any document in a supported front-end
+ * format, and emitting the standard Doxia events, which can then be consumed
+ * by any Doxia Sink.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: Parser.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.0
+ */
+public interface Parser
+    extends LogEnabled
+{
+    /** The Plexus lookup role. */
+    String ROLE = Parser.class.getName();
+
+    /** Unknown parser type */
+    int UNKNOWN_TYPE = 0;
+
+    /** Text parser type */
+    int TXT_TYPE = 1;
+
+    /** XML parser type */
+    int XML_TYPE = 2;
+
+    /**
+     * Parses the given source model and emits Doxia events into the given sink.
+     *
+     * @param source not null reader that provides the source document.
+     * You could use <code>newReader</code> methods from {@link org.codehaus.plexus.util.ReaderFactory}.
+     * @param sink A sink that consumes the Doxia events.
+     * @throws org.apache.maven.doxia.parser.ParseException if the model could not be parsed.
+     */
+    void parse( Reader source, Sink sink )
+        throws ParseException;
+
+    /**
+     * The parser type value could be {@link #UNKNOWN_TYPE}, {@link #TXT_TYPE} or
+     * {@link #XML_TYPE}.
+     *
+     * @return the type of Parser
+     */
+    int getType();
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/XhtmlBaseParser.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/XhtmlBaseParser.java
new file mode 100644
index 0000000..4ecc36f
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/XhtmlBaseParser.java
@@ -0,0 +1,1003 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.swing.text.html.HTML.Attribute;
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.markup.HtmlMarkup;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.util.DoxiaUtils;
+
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Common base parser for xhtml events.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @author ltheussl
+ * @version $Id: XhtmlBaseParser.java 892639 2009-12-20 18:49:58Z ltheussl $
+ * @since 1.1
+ */
+public class XhtmlBaseParser
+    extends AbstractXmlParser
+        implements HtmlMarkup
+{
+    /** True if a <script></script> block is read. CDATA sections within are handled as rawText. */
+    private boolean scriptBlock;
+
+    /** Used to distinguish <a href=""> from <a name="">. */
+    private boolean isLink;
+
+    /** Used to distinguish <a href=""> from <a name="">. */
+    private boolean isAnchor;
+
+    /** Used for nested lists. */
+    private int orderedListDepth = 0;
+
+    /** Counts section level. */
+    private int sectionLevel;
+
+    /** Verbatim flag, true whenever we are inside a <pre> tag. */
+    private boolean inVerbatim;
+
+    /** Used to recognize the case of img inside figure. */
+    private boolean inFigure;
+
+    /** Decoration properties, eg for texts. */
+    private final SinkEventAttributeSet decoration = new SinkEventAttributeSet();
+
+    /** Map of warn messages with a String as key to describe the error type and a Set as value.
+     * Using to reduce warn messages. */
+    private Map warnMessages;
+
+    /** {@inheritDoc} */
+    public void parse( Reader source, Sink sink )
+        throws ParseException
+    {
+        init();
+
+        try
+        {
+            super.parse( source, sink );
+        }
+        finally
+        {
+            logWarnings();
+
+            setSecondParsing( false );
+            init();
+        }
+    }
+
+    /**
+     * <p>
+     *   Goes through a common list of possible html start tags. These include only tags that can go into
+     *   the body of a xhtml document and so should be re-usable by different xhtml-based parsers.
+     * </p>
+     * <p>
+     *   The currently handled tags are:
+     * </p>
+     * <p>
+     *   <code>
+     *      <h2>, <h3>, <h4>, <h5>, <h6>, <p>, <pre>,
+     *      <ul>, <ol>, <li>, <dl>, <dt>, <dd>, <b>, <strong>,
+     *      <i>, <em>, <code>, <samp>, <tt>, <a>, <table>, <tr>,
+     *      <th>, <td>, <caption>, <br/>, <hr/>, <img/>.
+     *   </code>
+     * </p>
+     *
+     * @param parser A parser.
+     * @param sink the sink to receive the events.
+     * @return True if the event has been handled by this method, i.e. the tag was recognized, false otherwise.
+     */
+    protected boolean baseStartTag( XmlPullParser parser, Sink sink )
+    {
+        boolean visited = true;
+
+        SinkEventAttributeSet attribs = getAttributesFromParser( parser );
+
+        if ( parser.getName().equals( HtmlMarkup.H2.toString() ) )
+        {
+            handleSectionStart( sink, Sink.SECTION_LEVEL_1, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H3.toString() ) )
+        {
+            handleSectionStart( sink, Sink.SECTION_LEVEL_2, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H4.toString() ) )
+        {
+            handleSectionStart( sink, Sink.SECTION_LEVEL_3, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H5.toString() ) )
+        {
+            handleSectionStart( sink, Sink.SECTION_LEVEL_4, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H6.toString() ) )
+        {
+            handleSectionStart( sink, Sink.SECTION_LEVEL_5, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.U.toString() ) )
+        {
+            decoration.addAttribute( SinkEventAttributes.DECORATION, "underline" );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.S.toString() )
+                || parser.getName().equals( HtmlMarkup.STRIKE.toString() )
+                || parser.getName().equals( "del" ) )
+        {
+            decoration.addAttribute( SinkEventAttributes.DECORATION, "line-through" );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.SUB.toString() ) )
+        {
+            decoration.addAttribute( SinkEventAttributes.VALIGN, "sub" );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.SUP.toString() ) )
+        {
+            decoration.addAttribute( SinkEventAttributes.VALIGN, "sup" );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.P.toString() ) )
+        {
+            handlePStart( sink, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.DIV.toString() ) )
+        {
+            visited = handleDivStart( parser, attribs, sink );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.PRE.toString() ) )
+        {
+            handlePreStart( attribs, sink );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.UL.toString() ) )
+        {
+            sink.list( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.OL.toString() ) )
+        {
+            handleOLStart( parser, sink, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.LI.toString() ) )
+        {
+            handleLIStart( sink, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.DL.toString() ) )
+        {
+            sink.definitionList( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.DT.toString() ) )
+        {
+            sink.definitionListItem( attribs );
+            sink.definedTerm( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.DD.toString() ) )
+        {
+            sink.definition( attribs );
+        }
+        else if ( ( parser.getName().equals( HtmlMarkup.B.toString() ) )
+                || ( parser.getName().equals( HtmlMarkup.STRONG.toString() ) ) )
+        {
+            sink.bold();
+        }
+        else if ( ( parser.getName().equals( HtmlMarkup.I.toString() ) )
+                || ( parser.getName().equals( HtmlMarkup.EM.toString() ) ) )
+        {
+            handleFigureCaptionStart( sink, attribs );
+        }
+        else if ( ( parser.getName().equals( HtmlMarkup.CODE.toString() ) )
+                || ( parser.getName().equals( HtmlMarkup.SAMP.toString() ) )
+                || ( parser.getName().equals( HtmlMarkup.TT.toString() ) ) )
+        {
+            sink.monospaced();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.A.toString() ) )
+        {
+            handleAStart( parser, sink, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.TABLE.toString() ) )
+        {
+            handleTableStart( sink, attribs, parser );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.TR.toString() ) )
+        {
+            sink.tableRow( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.TH.toString() ) )
+        {
+            sink.tableHeaderCell( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.TD.toString() ) )
+        {
+            sink.tableCell( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.CAPTION.toString() ) )
+        {
+            sink.tableCaption( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.BR.toString() ) )
+        {
+            sink.lineBreak( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.HR.toString() ) )
+        {
+            sink.horizontalRule( attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.IMG.toString() ) )
+        {
+            handleImgStart( parser, sink, attribs );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.SCRIPT.toString() ) )
+        {
+            handleUnknown( parser, sink, TAG_TYPE_START );
+            scriptBlock = true;
+        }
+        else
+        {
+            visited = false;
+        }
+
+        return visited;
+    }
+
+    /**
+     * <p>
+     *   Goes through a common list of possible html end tags.
+     *   These should be re-usable by different xhtml-based parsers.
+     *   The tags handled here are the same as for {@link #baseStartTag(XmlPullParser,Sink)},
+     *   except for the empty elements (<code><br/>, <hr/>, <img/><code>).
+     * </p>
+     *
+     * @param parser A parser.
+     * @param sink the sink to receive the events.
+     * @return True if the event has been handled by this method, false otherwise.
+     */
+    protected boolean baseEndTag( XmlPullParser parser, Sink sink )
+    {
+        boolean visited = true;
+
+        if ( parser.getName().equals( HtmlMarkup.P.toString() ) )
+        {
+            if ( !inFigure )
+            {
+                sink.paragraph_();
+            }
+        }
+        else if ( parser.getName().equals( HtmlMarkup.U.toString() )
+                || parser.getName().equals( HtmlMarkup.S.toString() )
+                || parser.getName().equals( HtmlMarkup.STRIKE.toString() )
+                || parser.getName().equals( "del" ) )
+        {
+            decoration.removeAttribute( SinkEventAttributes.DECORATION );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.SUB.toString() )
+                || parser.getName().equals( HtmlMarkup.SUP.toString() ) )
+        {
+            decoration.removeAttribute( SinkEventAttributes.VALIGN );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.DIV.toString() ) )
+        {
+            if ( inFigure )
+            {
+                sink.figure_();
+                this.inFigure = false;
+            }
+            else
+            {
+                visited = false;
+            }
+        }
+        else if ( parser.getName().equals( HtmlMarkup.PRE.toString() ) )
+        {
+            verbatim_();
+
+            sink.verbatim_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.UL.toString() ) )
+        {
+            sink.list_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.OL.toString() ) )
+        {
+            sink.numberedList_();
+            orderedListDepth--;
+        }
+        else if ( parser.getName().equals( HtmlMarkup.LI.toString() ) )
+        {
+            handleListItemEnd( sink );
+        }
+        else if ( parser.getName().equals( HtmlMarkup.DL.toString() ) )
+        {
+            sink.definitionList_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.DT.toString() ) )
+        {
+            sink.definedTerm_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.DD.toString() ) )
+        {
+            sink.definition_();
+            sink.definitionListItem_();
+        }
+        else if ( ( parser.getName().equals( HtmlMarkup.B.toString() ) )
+                || ( parser.getName().equals( HtmlMarkup.STRONG.toString() ) ) )
+        {
+            sink.bold_();
+        }
+        else if ( ( parser.getName().equals( HtmlMarkup.I.toString() ) )
+                || ( parser.getName().equals( HtmlMarkup.EM.toString() ) ) )
+        {
+            handleFigureCaptionEnd( sink );
+        }
+        else if ( ( parser.getName().equals( HtmlMarkup.CODE.toString() ) )
+                || ( parser.getName().equals( HtmlMarkup.SAMP.toString() ) )
+                || ( parser.getName().equals( HtmlMarkup.TT.toString() ) ) )
+        {
+            sink.monospaced_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.A.toString() ) )
+        {
+            handleAEnd( sink );
+        }
+
+        // ----------------------------------------------------------------------
+        // Tables
+        // ----------------------------------------------------------------------
+
+        else if ( parser.getName().equals( HtmlMarkup.TABLE.toString() ) )
+        {
+            sink.tableRows_();
+
+            sink.table_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.TR.toString() ) )
+        {
+            sink.tableRow_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.TH.toString() ) )
+        {
+            sink.tableHeaderCell_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.TD.toString() ) )
+        {
+            sink.tableCell_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.CAPTION.toString() ) )
+        {
+            sink.tableCaption_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H2.toString() ) )
+        {
+            sink.sectionTitle1_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H3.toString() ) )
+        {
+            sink.sectionTitle2_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H4.toString() ) )
+        {
+            sink.sectionTitle3_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H5.toString() ) )
+        {
+            sink.sectionTitle4_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.H6.toString() ) )
+        {
+            sink.sectionTitle5_();
+        }
+        else if ( parser.getName().equals( HtmlMarkup.SCRIPT.toString() ) )
+        {
+            handleUnknown( parser, sink, TAG_TYPE_END );
+
+            scriptBlock = false;
+        }
+        else
+        {
+            visited = false;
+        }
+
+        return visited;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Just calls {@link #baseStartTag(XmlPullParser,Sink)}, this should be
+     * overridden by implementing parsers to include additional tags.
+     */
+    protected void handleStartTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( !baseStartTag( parser, sink ) )
+        {
+            if ( getLog().isWarnEnabled() )
+            {
+                String position = "[" + parser.getLineNumber() + ":"
+                    + parser.getColumnNumber() + "]";
+                String tag = "<" + parser.getName() + ">";
+
+                getLog().warn( "Unrecognized xml tag: " + tag + " at " + position );
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Just calls {@link #baseEndTag(XmlPullParser,Sink)}, this should be
+     * overridden by implementing parsers to include additional tags.
+     */
+    protected void handleEndTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( !baseEndTag( parser, sink ) )
+        {
+            // unrecognized tag is already logged in StartTag
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleText( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        String text = getText( parser );
+
+        /*
+         * NOTE: Don't do any whitespace trimming here. Whitespace normalization has already been performed by the
+         * parser so any whitespace that makes it here is significant.
+         *
+         * NOTE: text within script tags is ignored, scripting code should be embedded in CDATA.
+         */
+        if ( StringUtils.isNotEmpty( text ) && !isScriptBlock() )
+        {
+            sink.text( text, decoration );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleComment( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        String text = getText( parser ).trim();
+
+        if ( "PB".equals( text ) )
+        {
+            sink.pageBreak();
+        }
+        else
+        {
+            sink.comment( text );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleCdsect( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        String text = getText( parser );
+
+        if ( isScriptBlock() )
+        {
+            sink.unknown( CDATA, new Object[] {new Integer( CDATA_TYPE ), text}, null );
+        }
+        else
+        {
+            sink.text( text );
+        }
+    }
+
+    /**
+     * Make sure sections are nested consecutively.
+     *
+     * <p>
+     * HTML doesn't have any sections, only sectionTitles (<h2> etc), that means we have to
+     * open close any sections that are missing in between.
+     * </p>
+     *
+     * <p>
+     * For instance, if the following sequence is parsed:
+     * <pre>
+     * <h3></h3>
+     * <h6></h6>
+     * </pre>
+     * we have to insert two section starts before we open the <code><h6></code>.
+     * In the following sequence
+     * <pre>
+     * <h6></h6>
+     * <h3></h3>
+     * </pre>
+     * we have to close two sections before we open the <code><h3></code>.
+     * </p>
+     *
+     * <p>The current level is set to newLevel afterwards.</p>
+     *
+     * @param newLevel the new section level, all upper levels have to be closed.
+     * @param sink the sink to receive the events.
+     */
+    protected void consecutiveSections( int newLevel, Sink sink )
+    {
+        closeOpenSections( newLevel, sink );
+        openMissingSections( newLevel, sink );
+
+        this.sectionLevel = newLevel;
+    }
+
+    /**
+     * Close open sections.
+     *
+     * @param newLevel the new section level, all upper levels have to be closed.
+     * @param sink the sink to receive the events.
+     */
+    private void closeOpenSections( int newLevel, Sink sink )
+    {
+        while ( this.sectionLevel >= newLevel )
+        {
+            if ( sectionLevel == Sink.SECTION_LEVEL_5 )
+            {
+                sink.section5_();
+            }
+            else if ( sectionLevel == Sink.SECTION_LEVEL_4 )
+            {
+                sink.section4_();
+            }
+            else if ( sectionLevel == Sink.SECTION_LEVEL_3 )
+            {
+                sink.section3_();
+            }
+            else if ( sectionLevel == Sink.SECTION_LEVEL_2 )
+            {
+                sink.section2_();
+            }
+            else if ( sectionLevel == Sink.SECTION_LEVEL_1 )
+            {
+                sink.section1_();
+            }
+
+            this.sectionLevel--;
+        }
+    }
+
+    /**
+     * Open missing sections.
+     *
+     * @param newLevel the new section level, all lower levels have to be opened.
+     * @param sink the sink to receive the events.
+     */
+    private void openMissingSections( int newLevel, Sink sink )
+    {
+        while ( this.sectionLevel < newLevel - 1 )
+        {
+            this.sectionLevel++;
+
+            if ( sectionLevel == Sink.SECTION_LEVEL_5 )
+            {
+                sink.section5();
+            }
+            else if ( sectionLevel == Sink.SECTION_LEVEL_4 )
+            {
+                sink.section4();
+            }
+            else if ( sectionLevel == Sink.SECTION_LEVEL_3 )
+            {
+                sink.section3();
+            }
+            else if ( sectionLevel == Sink.SECTION_LEVEL_2 )
+            {
+                sink.section2();
+            }
+            else if ( sectionLevel == Sink.SECTION_LEVEL_1 )
+            {
+                sink.section1();
+            }
+        }
+    }
+
+    /**
+     * Return the current section level.
+     *
+     * @return the current section level.
+     */
+    protected int getSectionLevel()
+    {
+        return this.sectionLevel;
+    }
+
+    /**
+     * Set the current section level.
+     *
+     * @param newLevel the new section level.
+     */
+    protected void setSectionLevel( int newLevel )
+    {
+        this.sectionLevel = newLevel;
+    }
+
+    /**
+     * Stop verbatim mode.
+     */
+    protected void verbatim_()
+    {
+        this.inVerbatim = false;
+    }
+
+    /**
+     * Start verbatim mode.
+     */
+    protected void verbatim()
+    {
+        this.inVerbatim = true;
+    }
+
+    /**
+     * Checks if we are currently inside a <pre> tag.
+     *
+     * @return true if we are currently in verbatim mode.
+     */
+    protected boolean isVerbatim()
+    {
+        return this.inVerbatim;
+    }
+
+    /**
+     * Checks if we are currently inside a <script> tag.
+     *
+     * @return true if we are currently inside <code><script></code> tags.
+     *
+     * @since 1.1.1.
+     */
+    protected boolean isScriptBlock()
+    {
+        return this.scriptBlock;
+    }
+
+    /**
+     * Checks if the given id is a valid Doxia id and if not, returns a transformed one.
+     *
+     * @param id The id to validate.
+     * @return A transformed id or the original id if it was already valid.
+     * @see DoxiaUtils#encodeId(String)
+     */
+    protected String validAnchor( String id )
+    {
+        if ( !DoxiaUtils.isValidId( id ) )
+        {
+            String linkAnchor = DoxiaUtils.encodeId( id, true );
+
+            String msg = "Modified invalid link: '" + id + "' to '" + linkAnchor + "'";
+            logMessage( "modifiedLink", msg );
+
+            return linkAnchor;
+        }
+
+        return id;
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.scriptBlock = false;
+        this.isLink = false;
+        this.isAnchor = false;
+        this.orderedListDepth = 0;
+        this.sectionLevel = 0;
+        this.inVerbatim = false;
+        this.inFigure = false;
+        while( this.decoration.getAttributeNames().hasMoreElements() )
+        {
+            this.decoration.removeAttribute( this.decoration.getAttributeNames().nextElement() );
+        }
+        this.warnMessages = null;
+    }
+
+    private void handleAEnd( Sink sink )
+    {
+        if ( isLink )
+        {
+            sink.link_();
+            isLink = false;
+        }
+        else if ( isAnchor )
+        {
+            sink.anchor_();
+            isAnchor = false;
+        }
+    }
+
+    private void handleAStart( XmlPullParser parser, Sink sink, SinkEventAttributeSet attribs )
+    {
+        String href = parser.getAttributeValue( null, Attribute.HREF.toString() );
+
+        if ( href != null )
+        {
+            int hashIndex = href.indexOf( "#" );
+            if ( hashIndex != -1 && !DoxiaUtils.isExternalLink( href ) )
+            {
+                String hash = href.substring( hashIndex + 1 );
+
+                if ( !DoxiaUtils.isValidId( hash ) )
+                {
+                    href = href.substring( 0, hashIndex ) + "#" + DoxiaUtils.encodeId( hash, true );
+
+                    String msg = "Modified invalid link: '" + hash + "' to '" + href + "'";
+                    logMessage( "modifiedLink", msg );
+                }
+            }
+            sink.link( href, attribs );
+            isLink = true;
+        }
+        else
+        {
+            String name = parser.getAttributeValue( null, Attribute.NAME.toString() );
+
+            if ( name != null )
+            {
+                sink.anchor( validAnchor( name ), attribs );
+                isAnchor = true;
+            }
+            else
+            {
+                String id = parser.getAttributeValue( null, Attribute.ID.toString() );
+                if ( id != null )
+                {
+                    sink.anchor( validAnchor( id ), attribs );
+                    isAnchor = true;
+                }
+            }
+        }
+    }
+
+    private boolean handleDivStart( XmlPullParser parser, SinkEventAttributeSet attribs, Sink sink )
+    {
+        boolean visited = true;
+
+        String divclass = parser.getAttributeValue( null, Attribute.CLASS.toString() );
+
+        if ( "figure".equals( divclass ) )
+        {
+            this.inFigure = true;
+            SinkEventAttributeSet atts = new SinkEventAttributeSet( attribs );
+            atts.removeAttribute( SinkEventAttributes.CLASS );
+            sink.figure( atts );
+        }
+        else
+        {
+            visited = false;
+        }
+
+        return visited;
+    }
+
+    private void handleFigureCaptionEnd( Sink sink )
+    {
+        if ( inFigure )
+        {
+            sink.figureCaption_();
+        }
+        else
+        {
+            sink.italic_();
+        }
+    }
+
+    private void handleFigureCaptionStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( inFigure )
+        {
+            sink.figureCaption( attribs );
+        }
+        else
+        {
+            sink.italic();
+        }
+    }
+
+    private void handleImgStart( XmlPullParser parser, Sink sink, SinkEventAttributeSet attribs )
+    {
+        String src = parser.getAttributeValue( null, Attribute.SRC.toString() );
+
+        if ( src != null )
+        {
+            sink.figureGraphics( src, attribs );
+        }
+    }
+
+    private void handleLIStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( orderedListDepth == 0 )
+        {
+            sink.listItem( attribs );
+        }
+        else
+        {
+            sink.numberedListItem( attribs );
+        }
+    }
+
+    private void handleListItemEnd( Sink sink )
+    {
+        if ( orderedListDepth == 0 )
+        {
+            sink.listItem_();
+        }
+        else
+        {
+            sink.numberedListItem_();
+        }
+    }
+
+    private void handleOLStart( XmlPullParser parser, Sink sink, SinkEventAttributeSet attribs )
+    {
+        int numbering = Sink.NUMBERING_DECIMAL;
+        // this will have to be generalized if we handle styles
+        String style = parser.getAttributeValue( null, Attribute.STYLE.toString() );
+
+        if ( style != null )
+        {
+            if ( "list-style-type: upper-alpha".equals( style ) )
+            {
+                numbering = Sink.NUMBERING_UPPER_ALPHA;
+            }
+            else if ( "list-style-type: lower-alpha".equals( style ) )
+            {
+                numbering = Sink.NUMBERING_LOWER_ALPHA;
+            }
+            else if ( "list-style-type: upper-roman".equals( style ) )
+            {
+                numbering = Sink.NUMBERING_UPPER_ROMAN;
+            }
+            else if ( "list-style-type: lower-roman".equals( style ) )
+            {
+                numbering = Sink.NUMBERING_LOWER_ROMAN;
+            }
+            else if ( "list-style-type: decimal".equals( style ) )
+            {
+                numbering = Sink.NUMBERING_DECIMAL;
+            }
+        }
+
+        sink.numberedList( numbering, attribs );
+        orderedListDepth++;
+    }
+
+    private void handlePStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( !inFigure )
+        {
+            sink.paragraph( attribs );
+        }
+    }
+
+    /*
+     * The PRE element tells visual user agents that the enclosed text is
+     * "preformatted". When handling preformatted text, visual user agents:
+     * - May leave white space intact.
+     * - May render text with a fixed-pitch font.
+     * - May disable automatic word wrap.
+     * - Must not disable bidirectional processing.
+     * Non-visual user agents are not required to respect extra white space
+     * in the content of a PRE element.
+     */
+    private void handlePreStart( SinkEventAttributeSet attribs, Sink sink )
+    {
+        verbatim();
+        attribs.removeAttribute( SinkEventAttributes.DECORATION );
+        sink.verbatim( attribs );
+    }
+
+    private void handleSectionStart( Sink sink, int level, SinkEventAttributeSet attribs )
+    {
+        consecutiveSections( level, sink );
+        sink.section( level, attribs );
+        sink.sectionTitle( level, attribs );
+    }
+
+    private void handleTableStart( Sink sink, SinkEventAttributeSet attribs, XmlPullParser parser )
+    {
+        sink.table( attribs );
+        String border = parser.getAttributeValue( null, Attribute.BORDER.toString() );
+        boolean grid = true;
+
+        if ( border == null || "0".equals( border ) )
+        {
+            grid = false;
+        }
+
+        String align = parser.getAttributeValue( null, Attribute.ALIGN.toString() );
+        int[] justif = {Sink.JUSTIFY_LEFT};
+
+        if ( "center".equals( align ) )
+        {
+            justif[0] = Sink.JUSTIFY_CENTER;
+        }
+        else if ( "right".equals( align ) )
+        {
+            justif[0] = Sink.JUSTIFY_RIGHT;
+        }
+
+        sink.tableRows( justif, grid );
+    }
+
+    /**
+     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
+     *
+     * @param key not null
+     * @param msg not null
+     * @see #parse(Reader, Sink)
+     * @since 1.1.1
+     */
+    private void logMessage( String key, String msg )
+    {
+        msg = "[XHTML Parser] " + msg;
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( msg );
+
+            return;
+        }
+
+        if ( warnMessages == null )
+        {
+            warnMessages = new HashMap();
+        }
+
+        Set set = (Set) warnMessages.get( key );
+        if ( set == null )
+        {
+            set = new TreeSet();
+        }
+        set.add( msg );
+        warnMessages.put( key, set );
+    }
+
+    /**
+     * @since 1.1.1
+     */
+    private void logWarnings()
+    {
+        if ( getLog().isWarnEnabled() && this.warnMessages != null && !isSecondParsing() )
+        {
+            for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                Set set = (Set) entry.getValue();
+                for ( Iterator it2 = set.iterator(); it2.hasNext(); )
+                {
+                    String msg = (String) it2.next();
+
+                    getLog().warn( msg );
+                }
+            }
+
+            this.warnMessages = null;
+        }
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/DefaultParserManager.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/DefaultParserManager.java
new file mode 100644
index 0000000..6f52e41
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/DefaultParserManager.java
@@ -0,0 +1,55 @@
+package org.apache.maven.doxia.parser.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.parser.Parser;
+
+import java.util.Map;
+
+/**
+ * Simple implementation of the <code>ParserManager</code> interface.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: DefaultParserManager.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.0
+ * @plexus.component
+ */
+public class DefaultParserManager
+    implements ParserManager
+{
+    /**
+     * @plexus.requirement role="org.apache.maven.doxia.parser.Parser"
+     */
+    private Map parsers;
+
+    /** {@inheritDoc} */
+    public Parser getParser( String id )
+        throws ParserNotFoundException
+    {
+        Parser parser = (Parser) parsers.get( id );
+
+        if ( parser == null )
+        {
+            throw new ParserNotFoundException( "Cannot find parser with id = " + id );
+        }
+
+        return parser;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/ParserManager.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/ParserManager.java
new file mode 100644
index 0000000..22b97e8
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/ParserManager.java
@@ -0,0 +1,46 @@
+package org.apache.maven.doxia.parser.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.parser.Parser;
+
+/**
+ * Handles parser lookups.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: ParserManager.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.0
+ */
+public interface ParserManager
+{
+    /** The Plexus lookup role. */
+    String ROLE = ParserManager.class.getName();
+
+    /**
+     * Returns the parser that corresponds to the given id.
+     *
+     * @param id The identifier.
+     * @return The corresponding parser.
+     * @throws org.apache.maven.doxia.parser.manager.ParserNotFoundException if no parser could be found
+     * for the given id.
+     */
+    Parser getParser( String id )
+        throws ParserNotFoundException;
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/ParserNotFoundException.java b/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/ParserNotFoundException.java
new file mode 100644
index 0000000..fce8369
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/manager/ParserNotFoundException.java
@@ -0,0 +1,74 @@
+package org.apache.maven.doxia.parser.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Encapsulate a Doxia exception that indicates that a parser
+ * does not exist or could not be found.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: ParserNotFoundException.java 662767 2008-06-03 12:25:07Z ltheussl $
+ * @since 1.0
+ */
+public class ParserNotFoundException
+    extends Exception
+{
+    /** serialVersionUID */
+    static final long serialVersionUID = 295967936746221567L;
+
+    /**
+     * Construct a new ParserNotFoundException with the specified detail message.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the Throwable.getMessage() method.
+     */
+    public ParserNotFoundException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Constructs a new exception with the specified cause. The error
+     * message is (cause == null ? null : cause.toString() ).
+     *
+     * @param cause the cause. This can be retrieved later by the
+     * Throwable.getCause() method. (A null value is permitted, and indicates
+     * that the cause is nonexistent or unknown.)
+     */
+    public ParserNotFoundException( Throwable cause )
+    {
+        super( cause );
+    }
+
+    /**
+     * Construct a new ParserNotFoundException with the specified
+     * detail message and cause.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the Throwable.getMessage() method.
+     * @param cause the cause. This can be retrieved later by the
+     * Throwable.getCause() method. (A null value is permitted, and indicates
+     * that the cause is nonexistent or unknown.)
+     */
+    public ParserNotFoundException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractBinarySinkFactory.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractBinarySinkFactory.java
new file mode 100644
index 0000000..b7bc122
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractBinarySinkFactory.java
@@ -0,0 +1,78 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.codehaus.plexus.util.WriterFactory;
+
+/**
+ * An abstract <code>SinkFactory</code> for binary output.
+ *
+ * @author <a href="mailto:hboutemy at apache.org">Hervé Boutemy</a>
+ * @version $Id: AbstractBinarySinkFactory.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.1
+ */
+public abstract class AbstractBinarySinkFactory
+    implements SinkFactory
+{
+    /** {@inheritDoc} */
+    public Sink createSink( File outputDir, String outputName )
+        throws IOException
+    {
+        return createSink( outputDir, outputName, WriterFactory.UTF_8 );
+    }
+
+    /** {@inheritDoc} */
+    public Sink createSink( File outputDir, String outputName, String encoding )
+        throws IOException
+    {
+        if ( outputDir == null )
+        {
+            throw new IllegalArgumentException( "outputDir cannot be null." );
+        }
+
+        if ( !outputDir.exists() )
+        {
+            outputDir.mkdirs();
+        }
+        else
+        {
+            if ( !outputDir.isDirectory() )
+            {
+                throw new IllegalArgumentException( "The dir '" + outputDir + "' is not a directory." );
+            }
+        }
+
+        OutputStream out = new FileOutputStream( new File( outputDir, outputName ) );
+
+        return createSink( out, encoding );
+    }
+
+    /** {@inheritDoc} */
+    public Sink createSink( OutputStream out )
+        throws IOException
+    {
+        return createSink( out, WriterFactory.UTF_8 );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractSink.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractSink.java
new file mode 100644
index 0000000..cc7ef23
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractSink.java
@@ -0,0 +1,114 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.logging.Log;
+import org.apache.maven.doxia.logging.SystemStreamLog;
+import org.apache.maven.doxia.markup.Markup;
+
+/**
+ * An abstract base class that defines some convenience methods for sinks.
+ *
+ * @author ltheussl
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractSink.java 807164 2009-08-24 11:59:25Z vsiveton $
+ * @since 1.1
+ */
+public abstract class AbstractSink
+    implements Sink, Markup
+{
+    private Log logger;
+
+    /** {@inheritDoc} */
+    public void enableLogging( Log log )
+    {
+        this.logger = log;
+    }
+
+    /**
+     * Returns a logger for this sink.
+     * If no logger has been configured, a new SystemStreamLog is returned.
+     *
+     * @return Log
+     */
+    protected Log getLog()
+    {
+        if ( logger == null )
+        {
+            logger = new SystemStreamLog();
+        }
+
+        return logger;
+    }
+
+    /**
+     * Parses the given String and replaces all occurrences of
+     * '\n', '\r' and '\r\n' with the system EOL. All Sinks should
+     * make sure that text output is filtered through this method.
+     *
+     * @param text the text to scan.
+     * @return a String that contains only System EOLs.
+     */
+     protected static String unifyEOLs( String text )
+     {
+        if ( text == null )
+        {
+            return null;
+        }
+
+        int length = text.length();
+
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; i++ )
+        {
+            if ( text.charAt( i ) == '\r' )
+            {
+                if ( ( i + 1 ) < length && text.charAt( i + 1 ) == '\n' )
+                {
+                    i++;
+                }
+
+                buffer.append( EOL );
+            }
+            else if ( text.charAt( i ) == '\n' )
+            {
+                buffer.append( EOL );
+            }
+            else
+            {
+                buffer.append( text.charAt( i ) );
+            }
+        }
+
+        return buffer.toString();
+    }
+
+     /**
+      * This is called in {@link #head()} or in {@link #close()}, and can be used
+      * to set the sink into a clear state so it can be re-used.
+      *
+      * @since 1.1.2
+      */
+     protected void init()
+     {
+         // nop
+     }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractTextSink.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractTextSink.java
new file mode 100644
index 0000000..601b159
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractTextSink.java
@@ -0,0 +1,36 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.markup.TextMarkup;
+
+/**
+ * An abstract <code>Sink</code> for text markup syntax.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractTextSink.java 747780 2009-02-25 13:55:23Z vsiveton $
+ */
+public abstract class AbstractTextSink
+    extends SinkAdapter
+    implements TextMarkup
+{
+    // nop
+}
+
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractTextSinkFactory.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractTextSinkFactory.java
new file mode 100644
index 0000000..f5ab7c2
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractTextSinkFactory.java
@@ -0,0 +1,97 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import org.codehaus.plexus.util.WriterFactory;
+
+/**
+ * An abstract <code>SinkFactory</code> for Text markup syntax. <code>UTF-8</code> is used
+ * when no encoding is specified.
+ *
+ * @author Hervé Boutemy
+ * @author Benjamin Bentmann
+ * @version $Id: AbstractTextSinkFactory.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.1
+ */
+public abstract class AbstractTextSinkFactory
+    implements SinkFactory
+{
+    /**
+     * Create a text Sink for a given encoding.
+     *
+     * @param writer The writer for the sink output, never <code>null</code>.
+     * @param encoding The character encoding used by the writer.
+     * @return a Sink for text output in the given encoding.
+     */
+    protected abstract Sink createSink( Writer writer, String encoding );
+
+    /** {@inheritDoc} */
+    public Sink createSink( File outputDir, String outputName )
+        throws IOException
+    {
+        return createSink( outputDir, outputName, WriterFactory.UTF_8 );
+    }
+
+    /** {@inheritDoc} */
+    public Sink createSink( File outputDir, String outputName, String encoding )
+        throws IOException
+    {
+        if ( outputDir == null )
+        {
+            throw new IllegalArgumentException( "outputDir cannot be null." );
+        }
+
+        if ( !outputDir.exists() )
+        {
+            outputDir.mkdirs();
+        }
+        else
+        {
+            if ( !outputDir.isDirectory() )
+            {
+                throw new IllegalArgumentException( "The dir '" + outputDir + "' is not a directory." );
+            }
+        }
+
+        Writer writer = WriterFactory.newWriter( new File( outputDir, outputName ), encoding );
+
+        return createSink( writer, encoding );
+    }
+
+    /** {@inheritDoc} */
+    public Sink createSink( OutputStream out )
+        throws IOException
+    {
+        return createSink( out, WriterFactory.UTF_8 );
+    }
+
+    /** {@inheritDoc} */
+    public Sink createSink( OutputStream out, String encoding )
+        throws IOException
+    {
+        return createSink( new OutputStreamWriter( out, encoding ), encoding );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractXmlSink.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractXmlSink.java
new file mode 100644
index 0000000..d09b9d3
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractXmlSink.java
@@ -0,0 +1,203 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.markup.XmlMarkup;
+
+/**
+ * An abstract <code>Sink</code> for xml markup syntax.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractXmlSink.java 762690 2009-04-07 11:11:22Z ltheussl $
+ * @since 1.0
+ */
+public abstract class AbstractXmlSink
+    extends SinkAdapter
+    implements XmlMarkup
+{
+    /** Default namespace prepended to all tags */
+    private String nameSpace;
+
+    /**
+     * Sets the default namespace that is prepended to all tags written by this sink.
+     *
+     * @param ns the default namespace.
+     * @since 1.1
+     */
+    public void setNameSpace( String ns )
+    {
+        this.nameSpace = ns;
+    }
+
+    /**
+     * Return the default namespace that is prepended to all tags written by this sink.
+     *
+     * @return the current default namespace.
+     * @since 1.1
+     */
+    public String getNameSpace()
+    {
+        return this.nameSpace;
+    }
+
+    /**
+     * Starts a Tag. For instance:
+     * <pre>
+     * <tag>
+     * </pre>
+     *
+     * @param t a non null tag
+     * @see #writeStartTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet)
+     */
+    protected void writeStartTag( Tag t )
+    {
+        writeStartTag ( t, null );
+    }
+
+    /**
+     * Starts a Tag with attributes. For instance:
+     * <pre>
+     * <tag attName="attValue">
+     * </pre>
+     *
+     * @param t a non null tag.
+     * @param att a set of attributes. May be null.
+     * @see #writeStartTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet, boolean).
+     */
+    protected void writeStartTag( Tag t, MutableAttributeSet att )
+    {
+        writeStartTag ( t, att, false );
+    }
+
+    /**
+     * Starts a Tag with attributes. For instance:
+     * <pre>
+     * <tag attName="attValue">
+     * </pre>
+     *
+     * @param t a non null tag.
+     * @param att a set of attributes. May be null.
+     * @param isSimpleTag boolean to write as a simple tag.
+     */
+    protected void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
+    {
+        if ( t == null )
+        {
+            throw new IllegalArgumentException( "A tag is required" );
+        }
+
+        StringBuffer sb = new StringBuffer();
+        sb.append( LESS_THAN );
+
+        if ( nameSpace != null )
+        {
+            sb.append( nameSpace ).append( ':' );
+        }
+
+        sb.append( t.toString() );
+
+        sb.append( SinkUtils.getAttributeString( att ) );
+
+        if ( isSimpleTag )
+        {
+            sb.append( SPACE ).append( SLASH );
+        }
+
+        sb.append( GREATER_THAN );
+
+        write( sb.toString() );
+    }
+
+    /**
+     * Writes a system EOL.
+     *
+     * @since 1.1
+     */
+    protected void writeEOL()
+    {
+        write( EOL );
+    }
+
+    /**
+     * Ends a Tag without writing an EOL. For instance: <pre></tag></pre>.
+     *
+     * @param t a tag.
+     */
+    protected void writeEndTag( Tag t )
+    {
+        if ( t == null )
+        {
+            throw new IllegalArgumentException( "A tag is required" );
+        }
+
+        StringBuffer sb = new StringBuffer();
+        sb.append( LESS_THAN );
+        sb.append( SLASH );
+
+        if ( nameSpace != null )
+        {
+            sb.append( nameSpace ).append( ':' );
+        }
+
+        sb.append( t.toString() );
+        sb.append( GREATER_THAN );
+
+        write( sb.toString() );
+    }
+
+    /**
+     * Starts a simple Tag. For instance:
+     * <pre>
+     * <tag />
+     * </pre>
+     *
+     * @param t a non null tag
+     * @see #writeSimpleTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet)
+     */
+    protected void writeSimpleTag( Tag t )
+    {
+        writeSimpleTag( t, null );
+    }
+
+    /**
+     * Starts a simple Tag with attributes. For instance:
+     * <pre>
+     * <tag attName="attValue" />
+     * </pre>
+     *
+     * @param t a non null tag.
+     * @param att a set of attributes. May be null.
+     * @see #writeStartTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet, boolean).
+     */
+    protected void writeSimpleTag( Tag t, MutableAttributeSet att )
+    {
+        writeStartTag ( t, att, true );
+    }
+
+    /**
+     * Write a text to the sink.
+     *
+     * @param text the given text to write
+     */
+    protected abstract void write( String text );
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractXmlSinkFactory.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractXmlSinkFactory.java
new file mode 100644
index 0000000..bdb71d7
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/AbstractXmlSinkFactory.java
@@ -0,0 +1,47 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+/**
+ * An abstract <code>SinkFactory</code> for XML markup syntax. <code>UTF-8</code> is used
+ * when no encoding is specified.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractXmlSinkFactory.java 746978 2009-02-23 12:20:33Z vsiveton $
+ * @since 1.1
+ */
+public abstract class AbstractXmlSinkFactory
+    extends AbstractTextSinkFactory
+{
+    /**
+     * Create a text Sink for a given encoding and for a given language identifier.
+     *
+     * @param writer The writer for the sink output, never <code>null</code>.
+     * @param encoding The character encoding used by the writer.
+     * @param languageId language identifier for the root element as defined by
+     * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
+     * in addition, the empty string may be specified.
+     * @return a Sink for XML output in the given encoding.
+     * @see <a href="http://www.w3.org/TR/REC-xml/#sec-lang-tag">http://www.w3.org/TR/REC-xml/#sec-lang-tag</a>
+     */
+    protected abstract Sink createSink( Writer writer, String encoding, String languageId );
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/PipelineSink.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/PipelineSink.java
new file mode 100644
index 0000000..3bb8c70
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/PipelineSink.java
@@ -0,0 +1,94 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * May be used to invoke the same method on a List of Sinks.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: PipelineSink.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+public class PipelineSink
+    implements InvocationHandler
+{
+    private List pipeline;
+
+    /**
+     * Constructs a PipelineSink for a given List of Sinks.
+     *
+     * @param pipeline A List of Sinks.
+     */
+    public PipelineSink( List pipeline )
+    {
+        this.pipeline = pipeline;
+    }
+
+    /**
+     * Add a Sink to the List of Sinks.
+     *
+     * @param sink the Sink to add.
+     */
+    public void addSink( Sink sink )
+    {
+        pipeline.add( sink );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Invoke a Method on this PipelineSink.
+     *
+     * @throws IllegalAccessException if any.
+     * @throws InvocationTargetException if any.
+     */
+    public Object invoke( Object proxy, Method method, Object[] args )
+            throws IllegalAccessException, InvocationTargetException
+    {
+        for ( Iterator it = pipeline.iterator(); it.hasNext(); )
+        {
+            Sink sink = (Sink) it.next();
+
+            method.invoke( sink, args );
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns an instance of a PipelineSink as a Sink.
+     *
+     * @param pipeline A List of Sinks.
+     * @return a {@link org.apache.maven.doxia.sink.Sink} object.
+     */
+    public static Sink newInstance( List pipeline )
+    {
+        return (Sink) Proxy.newProxyInstance( PipelineSink.class.getClassLoader(),
+                                              new Class[]{Sink.class},
+                                              new PipelineSink( pipeline ) );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkAdapter.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkAdapter.java
new file mode 100644
index 0000000..e3ebf70
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkAdapter.java
@@ -0,0 +1,856 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.MutableAttributeSet;
+
+/**
+ * Empty implementation of the <code>Sink</code> interface. Useful for testing purposes.
+ *
+ * @since 1.0
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: SinkAdapter.java 747837 2009-02-25 15:50:39Z ltheussl $
+ */
+public class SinkAdapter
+    extends AbstractSink
+{
+    /** {@inheritDoc} */
+    public void head()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section1()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section2()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section3()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section4()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section5()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void author()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void date()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void anchor_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        // nop
+    }
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        head();
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        title();
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        author();
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        date();
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        body();
+    }
+
+    /** {@inheritDoc} */
+    public void section( int level, SinkEventAttributes attributes )
+    {
+        if ( level == SECTION_LEVEL_1 )
+        {
+            section1();
+        }
+        else if ( level == SECTION_LEVEL_2 )
+        {
+            section2();
+        }
+        else if ( level == SECTION_LEVEL_3 )
+        {
+            section3();
+        }
+        else if ( level == SECTION_LEVEL_4 )
+        {
+            section4();
+        }
+        else if ( level == SECTION_LEVEL_5 )
+        {
+            section5();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void section_( int level )
+    {
+        if ( level == SECTION_LEVEL_1 )
+        {
+            section1_();
+        }
+        else if ( level == SECTION_LEVEL_2 )
+        {
+            section2_();
+        }
+        else if ( level == SECTION_LEVEL_3 )
+        {
+            section3_();
+        }
+        else if ( level == SECTION_LEVEL_4 )
+        {
+            section4_();
+        }
+        else if ( level == SECTION_LEVEL_5 )
+        {
+            section5_();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        if ( level == SECTION_LEVEL_1 )
+        {
+            sectionTitle1();
+        }
+        else if ( level == SECTION_LEVEL_2 )
+        {
+            sectionTitle2();
+        }
+        else if ( level == SECTION_LEVEL_3 )
+        {
+            sectionTitle3();
+        }
+        else if ( level == SECTION_LEVEL_4 )
+        {
+            sectionTitle4();
+        }
+        else if ( level == SECTION_LEVEL_5 )
+        {
+            sectionTitle5();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+        if ( level == SECTION_LEVEL_1 )
+        {
+            sectionTitle1_();
+        }
+        else if ( level == SECTION_LEVEL_2 )
+        {
+            sectionTitle2_();
+        }
+        else if ( level == SECTION_LEVEL_3 )
+        {
+            sectionTitle3_();
+        }
+        else if ( level == SECTION_LEVEL_4 )
+        {
+            sectionTitle4_();
+        }
+        else if ( level == SECTION_LEVEL_5 )
+        {
+            sectionTitle5_();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void list( SinkEventAttributes attributes )
+    {
+        list();
+    }
+
+    /** {@inheritDoc} */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        listItem();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        numberedList( numbering );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        numberedListItem();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        definitionList();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem( SinkEventAttributes attributes )
+    {
+        definitionListItem();
+    }
+
+    /** {@inheritDoc} */
+    public void definition( SinkEventAttributes attributes )
+    {
+        definition();
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        definedTerm();
+    }
+
+    /** {@inheritDoc} */
+    public void figure( SinkEventAttributes attributes )
+    {
+        figure();
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        figureCaption();
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        figureGraphics( src );
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        table();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        tableRow();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        tableCaption();
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        paragraph();
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes( attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES );
+
+        boolean boxed = false;
+
+        if ( atts != null && atts.isDefined( SinkEventAttributes.DECORATION ) )
+        {
+            boxed = "boxed".equals( atts.getAttribute( SinkEventAttributes.DECORATION ).toString() );
+        }
+
+        verbatim( boxed );
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        horizontalRule();
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        anchor( name );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        link( name );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        lineBreak();
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        text( text );
+    }
+
+    /** {@inheritDoc} */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        // nop
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkEventAttributeSet.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkEventAttributeSet.java
new file mode 100644
index 0000000..da9a96c
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkEventAttributeSet.java
@@ -0,0 +1,413 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.swing.text.AttributeSet;
+
+/**
+ * Implementation of MutableAttributeSet using a LinkedHashMap.
+ *
+ * @author ltheussl
+ * @version $Id: SinkEventAttributeSet.java 759587 2009-03-28 21:01:47Z ltheussl $
+ * @since 1.1
+ */
+public class SinkEventAttributeSet
+    implements SinkEventAttributes, Cloneable
+{
+    /**
+     * An unmodifiable attribute set containing only an underline attribute.
+     */
+    public static final SinkEventAttributes UNDERLINE;
+
+    /**
+     * An unmodifiable attribute set containing only an overline attribute.
+     */
+    public static final SinkEventAttributes OVERLINE;
+
+    /**
+     * An unmodifiable attribute set containing only a linethrough attribute.
+     */
+    public static final SinkEventAttributes LINETHROUGH;
+
+    /**
+     * An unmodifiable attribute set containing only a boxed attribute.
+     */
+    public static final SinkEventAttributes BOXED;
+
+    /**
+     * An unmodifiable attribute set containing only a bold attribute.
+     */
+    public static final SinkEventAttributes BOLD;
+
+    /**
+     * An unmodifiable attribute set containing only an italic attribute.
+     */
+    public static final SinkEventAttributes ITALIC;
+
+    /**
+     * An unmodifiable attribute set containing only a monospaced attribute.
+     */
+    public static final SinkEventAttributes MONOSPACED;
+
+    /**
+     * An unmodifiable attribute set containing only a left attribute.
+     */
+    public static final SinkEventAttributes LEFT;
+
+    /**
+     * An unmodifiable attribute set containing only a right attribute.
+     */
+    public static final SinkEventAttributes RIGHT;
+
+    /**
+     * An unmodifiable attribute set containing only a center attribute.
+     */
+    public static final SinkEventAttributes CENTER;
+
+    /**
+     * An unmodifiable attribute set containing only a justify attribute.
+     */
+    public static final SinkEventAttributes JUSTIFY;
+
+
+    static
+    {
+        UNDERLINE = new SinkEventAttributeSet( new String[] {DECORATION, "underline"} ).unmodifiable();
+        OVERLINE = new SinkEventAttributeSet( new String[] {DECORATION, "overline"} ).unmodifiable();
+        LINETHROUGH = new SinkEventAttributeSet( new String[] {DECORATION, "line-through"} ).unmodifiable();
+        BOXED = new SinkEventAttributeSet( new String[] {DECORATION, "boxed"} ).unmodifiable();
+
+        BOLD = new SinkEventAttributeSet( new String[] {STYLE, "bold"} ).unmodifiable();
+        ITALIC = new SinkEventAttributeSet( new String[] {STYLE, "italic"} ).unmodifiable();
+        MONOSPACED = new SinkEventAttributeSet( new String[] {STYLE, "monospaced"} ).unmodifiable();
+
+        LEFT = new SinkEventAttributeSet( new String[] {ALIGN, "left"} ).unmodifiable();
+        RIGHT = new SinkEventAttributeSet( new String[] {ALIGN, "right"} ).unmodifiable();
+        CENTER = new SinkEventAttributeSet( new String[] {ALIGN, "center"} ).unmodifiable();
+        JUSTIFY = new SinkEventAttributeSet( new String[] {ALIGN, "justify"} ).unmodifiable();
+    }
+
+    private Map attribs;
+
+    private AttributeSet resolveParent;
+
+    /**
+     * Constructs a new, empty SinkEventAttributeSet with default size 5.
+     */
+    public SinkEventAttributeSet()
+    {
+        this( 5 );
+    }
+
+    /**
+     * Constructs a new, empty SinkEventAttributeSet with the specified initial size.
+     *
+     * @param size the initial number of attribs.
+     */
+    public SinkEventAttributeSet( int size )
+    {
+        attribs = new LinkedHashMap( size );
+    }
+
+    /**
+     * Constructs a new SinkEventAttributeSet with the attribute name-value
+     * mappings as given by the specified String array.
+     *
+     * @param attributes the specified String array. If the length of this array
+     * is not an even number, an IllegalArgumentException is thrown.
+     */
+    public SinkEventAttributeSet( String[] attributes )
+    {
+        int n = attributes.length;
+
+        if ( ( n % 2 ) != 0 )
+        {
+            throw new IllegalArgumentException( "Missing attribute!" );
+        }
+
+        attribs = new LinkedHashMap( n / 2 );
+
+        for ( int i = 0; i < n; i = i + 2 )
+        {
+            attribs.put( attributes[i], attributes[i + 1] );
+        }
+    }
+
+    /**
+     * Constructs a new SinkEventAttributeSet with the same attribute name-value
+     * mappings as in the specified AttributeSet.
+     *
+     * @param attributes the specified AttributeSet.
+     */
+    public SinkEventAttributeSet( AttributeSet attributes )
+    {
+        attribs = new LinkedHashMap( attributes.getAttributeCount() );
+
+        Enumeration names = attributes.getAttributeNames();
+
+        while ( names.hasMoreElements() )
+        {
+            Object name = names.nextElement();
+
+            attribs.put( name, attributes.getAttribute( name ) );
+        }
+    }
+
+    /**
+     * Replace this AttributeSet by an unmodifiable view of itself.
+     * Any subsequent attempt to add, remove or modify the underlying mapping
+     * will result in an UnsupportedOperationException.
+     *
+     * @return an unmodifiable view of this AttributeSet.
+     *
+     * @since 1.1.1
+     */
+    public SinkEventAttributeSet unmodifiable()
+    {
+        this.attribs = Collections.unmodifiableMap( attribs );
+
+        return this;
+    }
+
+    /**
+     * Checks whether the set of attribs is empty.
+     *
+     * @return true if the set is empty.
+     */
+    public boolean isEmpty()
+    {
+        return attribs.isEmpty();
+    }
+
+    /** {@inheritDoc} */
+    public int getAttributeCount()
+    {
+        return attribs.size();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isDefined( Object attrName )
+    {
+        return attribs.containsKey( attrName );
+    }
+
+    /** {@inheritDoc} */
+    public boolean isEqual( AttributeSet attr )
+    {
+        return ( ( getAttributeCount() == attr.getAttributeCount() )
+                && containsAttributes( attr ) );
+    }
+
+    /** {@inheritDoc} */
+    public AttributeSet copyAttributes()
+    {
+        return ( (AttributeSet) clone() );
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration getAttributeNames()
+    {
+        return Collections.enumeration( attribs.keySet() );
+    }
+
+    /** {@inheritDoc} */
+    public Object getAttribute( Object key  )
+    {
+        Object value = attribs.get( key  );
+
+        if ( value == null )
+        {
+            AttributeSet parent = getResolveParent();
+
+            if ( parent != null )
+            {
+                value = parent.getAttribute( key  );
+            }
+        }
+
+        return value;
+    }
+
+    /** {@inheritDoc} */
+    public boolean containsAttribute( Object name, Object value )
+    {
+        return value.equals( getAttribute( name ) );
+    }
+
+    /** {@inheritDoc} */
+    public boolean containsAttributes( AttributeSet attributes )
+    {
+        boolean result = true;
+
+        Enumeration names = attributes.getAttributeNames();
+
+        while ( result && names.hasMoreElements() )
+        {
+            Object name = names.nextElement();
+            result = attributes.getAttribute( name ).equals( getAttribute( name ) );
+        }
+
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Adds an attribute with the given name and value.
+     */
+    public void addAttribute( Object name, Object value )
+    {
+        attribs.put( name.toString(), value );
+    }
+
+    /** {@inheritDoc} */
+    public void addAttributes( AttributeSet attributes  )
+    {
+        if ( attributes == null || attributes.getAttributeCount() == 0 )
+        {
+            return;
+        }
+
+        Enumeration names = attributes.getAttributeNames();
+
+        while ( names.hasMoreElements() )
+        {
+            Object name = names.nextElement();
+
+            addAttribute( name, attributes.getAttribute( name ) );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void removeAttribute( Object name )
+    {
+        attribs.remove( name );
+    }
+
+    /** {@inheritDoc} */
+    public void removeAttributes( Enumeration names )
+    {
+        while ( names.hasMoreElements() )
+        {
+            removeAttribute( names.nextElement() );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void removeAttributes( AttributeSet attributes  )
+    {
+        if ( attributes == null )
+        {
+            return;
+        }
+        else if ( attributes == this )
+        {
+            attribs.clear();
+        }
+        else
+        {
+            Enumeration names = attributes.getAttributeNames();
+
+            while ( names.hasMoreElements() )
+            {
+                Object name = names.nextElement();
+                Object value = attributes.getAttribute( name );
+
+                if ( value.equals( getAttribute( name ) ) )
+                {
+                    removeAttribute( name );
+                }
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public AttributeSet getResolveParent()
+    {
+        return this.resolveParent;
+    }
+
+    /** {@inheritDoc} */
+    public void setResolveParent( AttributeSet parent )
+    {
+        this.resolveParent = parent;
+    }
+
+    /** {@inheritDoc} */
+    public Object clone()
+    {
+        SinkEventAttributeSet attr = new SinkEventAttributeSet( attribs.size() );
+        attr.attribs = new LinkedHashMap( attribs );
+
+        if ( resolveParent != null )
+        {
+            attr.resolveParent = resolveParent.copyAttributes();
+        }
+
+        return attr;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode()
+    {
+        final int parentHash = ( resolveParent == null ? 0 : resolveParent.hashCode() );
+
+        return attribs.hashCode() + parentHash;
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+
+        if ( obj instanceof SinkEventAttributeSet )
+        {
+            return isEqual( (SinkEventAttributeSet) obj  );
+        }
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toString()
+    {
+        StringBuffer s = new StringBuffer();
+        Enumeration names = getAttributeNames();
+
+        while ( names.hasMoreElements() )
+        {
+            String key = names.nextElement().toString();
+            String value = getAttribute( key ).toString();
+
+            s.append( ' ' ).append( key ).append( '=' ).append( value );
+        }
+
+        return s.toString();
+    }
+
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkUtils.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkUtils.java
new file mode 100644
index 0000000..d7d3d48
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/SinkUtils.java
@@ -0,0 +1,283 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import java.util.Enumeration;
+import java.util.Arrays;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.MutableAttributeSet;
+
+import org.apache.maven.doxia.markup.Markup;
+
+/**
+ * Collection of common utility methods for sinks.
+ *
+ * @author ltheussl
+ * @version $Id: SinkUtils.java 733395 2009-01-10 23:09:40Z ltheussl $
+ * @since 1.1
+ */
+public class SinkUtils
+{
+
+    /** Do not instantiate. */
+    private SinkUtils()
+    {
+        // Utility class
+    }
+
+    /**
+     * The set of base attributes.
+     */
+    public static final String[] SINK_BASE_ATTRIBUTES =
+    {
+        SinkEventAttributes.CLASS, SinkEventAttributes.ID, SinkEventAttributes.LANG,
+        SinkEventAttributes.STYLE, SinkEventAttributes.TITLE
+    };
+
+    /**
+     * The attributes that are supported for the br tag.
+     */
+    public static final String[] SINK_BR_ATTRIBUTES =
+    {
+        SinkEventAttributes.CLASS, SinkEventAttributes.ID,
+        SinkEventAttributes.STYLE, SinkEventAttributes.TITLE
+    };
+
+    /**
+     * The attributes that are supported for the <img> tag.
+     */
+    public static final String[] SINK_IMG_ATTRIBUTES;
+
+    /**
+     * The attributes that are supported for the section tags, like <p>, <h2>, <div>.
+     */
+    public static final String[] SINK_SECTION_ATTRIBUTES;
+
+    /**
+     * The attributes that are supported for the <div> and <pre> tags.
+     */
+    public static final String[] SINK_VERBATIM_ATTRIBUTES;
+
+    /**
+     * The attributes that are supported for the <hr> tag.
+     */
+    public static final String[] SINK_HR_ATTRIBUTES;
+
+    /**
+     * The attributes that are supported for the <a> tag.
+     */
+    public static final String[] SINK_LINK_ATTRIBUTES;
+
+    /**
+     * The attributes that are supported for the <table> tag.
+     */
+    public static final String[] SINK_TABLE_ATTRIBUTES;
+
+    /**
+     * The attributes that are supported for the <td> and <th> tags.
+     */
+    public static final String[] SINK_TD_ATTRIBUTES;
+
+    /**
+     * The attributes that are supported for the <tr> tag.
+     */
+    public static final String[] SINK_TR_ATTRIBUTES;
+
+    private static final String[] IMG_ATTRIBUTES =
+    {
+        SinkEventAttributes.ALIGN, SinkEventAttributes.ALT, SinkEventAttributes.BORDER,
+        SinkEventAttributes.HEIGHT, SinkEventAttributes.HSPACE, SinkEventAttributes.ISMAP,
+        SinkEventAttributes.SRC, SinkEventAttributes.USEMAP, SinkEventAttributes.VSPACE,
+        SinkEventAttributes.WIDTH
+    };
+
+    private static final String[] HR_ATTRIBUTES =
+    {
+        SinkEventAttributes.ALIGN, SinkEventAttributes.NOSHADE, SinkEventAttributes.SIZE,
+        SinkEventAttributes.WIDTH
+    };
+
+    private static final String[] LINK_ATTRIBUTES =
+    {
+        SinkEventAttributes.CHARSET, SinkEventAttributes.COORDS, SinkEventAttributes.HREF,
+        SinkEventAttributes.HREFLANG, SinkEventAttributes.REL, SinkEventAttributes.REV,
+        SinkEventAttributes.SHAPE, SinkEventAttributes.TARGET, SinkEventAttributes.TYPE
+    };
+
+    private static final String[] TABLE_ATTRIBUTES =
+    {
+        SinkEventAttributes.ALIGN, SinkEventAttributes.BGCOLOR, SinkEventAttributes.BORDER,
+        SinkEventAttributes.CELLPADDING, SinkEventAttributes.CELLSPACING, SinkEventAttributes.FRAME,
+        SinkEventAttributes.RULES, SinkEventAttributes.SUMMARY, SinkEventAttributes.WIDTH
+    };
+
+    private static final String[] TABLE_CELL_ATTRIBUTES =
+    {
+        SinkEventAttributes.ABBRV, SinkEventAttributes.ALIGN, SinkEventAttributes.AXIS,
+        SinkEventAttributes.BGCOLOR, SinkEventAttributes.COLSPAN, SinkEventAttributes.HEADERS,
+        SinkEventAttributes.HEIGHT, SinkEventAttributes.NOWRAP, SinkEventAttributes.ROWSPAN,
+        SinkEventAttributes.SCOPE, SinkEventAttributes.VALIGN, SinkEventAttributes.WIDTH
+    };
+
+    static
+    {
+        SINK_IMG_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, IMG_ATTRIBUTES );
+        SINK_SECTION_ATTRIBUTES =
+                join( SINK_BASE_ATTRIBUTES, new String[] {SinkEventAttributes.ALIGN} );
+        SINK_VERBATIM_ATTRIBUTES =
+                join( SINK_BASE_ATTRIBUTES,
+                new String[] {SinkEventAttributes.ALIGN, SinkEventAttributes.DECORATION, SinkEventAttributes.WIDTH} );
+        SINK_HR_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, HR_ATTRIBUTES );
+        SINK_LINK_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, LINK_ATTRIBUTES );
+        SINK_TABLE_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, TABLE_ATTRIBUTES );
+        SINK_TR_ATTRIBUTES =
+                join( SINK_BASE_ATTRIBUTES,
+                new String[] {SinkEventAttributes.ALIGN, SinkEventAttributes.BGCOLOR, SinkEventAttributes.VALIGN} );
+        SINK_TD_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, TABLE_CELL_ATTRIBUTES );
+    }
+
+    private static String[] join( String[] a, String[] b )
+    {
+        String[] temp = new String[a.length + b.length];
+        System.arraycopy( a, 0, temp, 0, a.length );
+        System.arraycopy( b, 0, temp, a.length, b.length );
+
+        Arrays.sort( temp ); // necessary for binary searches in filterAttributes()
+
+        return temp;
+    }
+
+    /**
+     * Utility method to get an AttributeSet as a String.
+     * The resulting String is in the form ' name1="value1" name2="value2" ...',
+     * ie it can be appended directly to an xml start tag. Attribute values that are itself
+     * AttributeSets are ignored unless the Attribute name is SinkEventAttributeSet.STYLE,
+     * in which case they are written as outlined at
+     * {@link org.apache.maven.doxia.sink.SinkEventAttributes#STYLE SinkEventAttributes.STYLE}.
+     * All other keys and values are written as Strings.
+     *
+     * @param att The AttributeSet. May be null, in which case an empty String is returned.
+     * @return the AttributeSet as a String in a form that can be appended to an xml start tag.
+     */
+    public static String getAttributeString( AttributeSet att )
+    {
+        if ( att == null )
+        {
+            return "";
+        }
+
+        StringBuffer sb = new StringBuffer();
+
+        Enumeration names = att.getAttributeNames();
+
+        while ( names.hasMoreElements() )
+        {
+            Object key = names.nextElement();
+            Object value = att.getAttribute( key );
+
+            if ( value instanceof AttributeSet )
+            {
+                // Other AttributeSets are ignored
+                if ( SinkEventAttributes.STYLE.equals( key.toString() ) )
+                {
+                    sb.append( Markup.SPACE ).append( key.toString() ).append( Markup.EQUAL )
+                        .append( Markup.QUOTE ).append( asCssString( (AttributeSet) value ) )
+                        .append( Markup.QUOTE );
+                }
+            }
+            else
+            {
+                sb.append( Markup.SPACE ).append( key.toString() ).append( Markup.EQUAL )
+                    .append( Markup.QUOTE ).append( value.toString() ).append( Markup.QUOTE );
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private static String asCssString( AttributeSet att )
+    {
+        StringBuffer sb = new StringBuffer();
+
+        Enumeration names = att.getAttributeNames();
+
+        while ( names.hasMoreElements() )
+        {
+            Object key = names.nextElement();
+            Object value = att.getAttribute( key );
+
+            // don't go recursive
+            if ( !( value instanceof AttributeSet ) )
+            {
+                sb.append( key.toString() ).append( Markup.COLON )
+                    .append( Markup.SPACE ).append( value.toString() );
+
+                if ( names.hasMoreElements() )
+                {
+                    sb.append( Markup.SEMICOLON ).append( Markup.SPACE );
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Filters the given AttributeSet.
+     * Removes all attributes whose name (key) is not contained in the sorted array valids.
+     *
+     * @param attributes The AttributeSet to filter. The String values of Attribute names
+     * are compared to the elements of the valids array.
+     * @param valids a sorted array of attribute names that are to be kept in the resulting AttributeSet.
+     *      <b>Note:</b> a binary search is employed, so the array has to be sorted for correct results.
+     * @return A filtered MutableAttributeSet object. Returns null if the input AttributeSet is null.
+     *      If the array of valids is either null or empty, an empty AttributeSet is returned.
+     */
+    public static MutableAttributeSet filterAttributes( AttributeSet attributes, String[] valids )
+    {
+        if ( attributes == null )
+        {
+            return null;
+        }
+
+        if ( valids == null || valids.length == 0 )
+        {
+            return new SinkEventAttributeSet( 0 );
+        }
+
+        MutableAttributeSet atts = new SinkEventAttributeSet( attributes.getAttributeCount() );
+
+        Enumeration names = attributes.getAttributeNames();
+
+        while ( names.hasMoreElements() )
+        {
+            String key = names.nextElement().toString();
+
+            if ( Arrays.binarySearch( valids, key ) >= 0 )
+            {
+                atts.addAttribute( key, attributes.getAttribute( key ) );
+            }
+        }
+
+        return atts;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/XhtmlBaseSink.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/XhtmlBaseSink.java
new file mode 100644
index 0000000..6ee8995
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/XhtmlBaseSink.java
@@ -0,0 +1,2073 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML.Attribute;
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.markup.HtmlMarkup;
+import org.apache.maven.doxia.markup.Markup;
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+
+/**
+ * Abstract base xhtml sink implementation.
+ *
+ * @author Jason van Zyl
+ * @author ltheussl
+ * @version $Id: XhtmlBaseSink.java 926049 2010-03-22 12:40:16Z ltheussl $
+ * @since 1.1
+ */
+public class XhtmlBaseSink
+    extends AbstractXmlSink
+    implements HtmlMarkup
+{
+    // ----------------------------------------------------------------------
+    // Instance fields
+    // ----------------------------------------------------------------------
+
+    /** The PrintWriter to write the result. */
+    private final PrintWriter writer;
+
+    /** Used to collect text events mainly for the head events. */
+    private StringBuffer textBuffer = new StringBuffer();
+
+    /** An indication on if we're inside a head. */
+    private boolean headFlag;
+
+    /** An indication on if we're inside an image caption flag. */
+    private boolean figureCaptionFlag;
+
+    /** An indication on if we're inside a paragraph flag. */
+    private boolean paragraphFlag;
+
+    /** An indication on if we're in verbatim mode. */
+    private boolean verbatimFlag;
+
+    /** Stack of alignment int[] of table cells. */
+    private final LinkedList cellJustifStack;
+
+    /** Stack of justification of table cells. */
+    private final LinkedList isCellJustifStack;
+
+    /** Stack of current table cell. */
+    private final LinkedList cellCountStack;
+
+    /** Used to style successive table rows differently. */
+    private boolean evenTableRow = true;
+
+    /** The stack of StringWriter to write the table result temporary, so we could play with the output DOXIA-177. */
+    private final LinkedList tableContentWriterStack;
+
+    private final LinkedList tableCaptionWriterStack;
+
+    private final LinkedList tableCaptionXMLWriterStack;
+
+    /** The stack of table caption */
+    private final LinkedList tableCaptionStack;
+
+    /** used to store attributes passed to table(). */
+    protected MutableAttributeSet tableAttributes;
+
+    /** Used to distinguish old-style figure handling. */
+    private boolean legacyFigure;
+
+    /** Used to distinguish old-style figure handling. */
+    private boolean legacyFigureCaption;
+
+    /** Indicates that an image is part of a figure. */
+    private boolean inFigure;
+
+    /** Flag to know if {@link #tableRows(int[], boolean)} is called or not. It is mainly to be backward compatible
+     * with some plugins (like checkstyle) which uses:
+     * <pre>
+     * sink.table();
+     * sink.tableRow();
+     * </pre>
+     * instead of
+     * <pre>
+     * sink.table();
+     * sink.tableRows( justify, true );
+     * sink.tableRow();
+     * </pre>
+     * */
+    protected boolean tableRows = false;
+
+    /** Map of warn messages with a String as key to describe the error type and a Set as value.
+     * Using to reduce warn messages. */
+    private Map warnMessages;
+
+    // ----------------------------------------------------------------------
+    // Constructor
+    // ----------------------------------------------------------------------
+
+    /**
+     * Constructor, initialize the PrintWriter.
+     *
+     * @param out The writer to write the result.
+     */
+    public XhtmlBaseSink( Writer out )
+    {
+        this.writer = new PrintWriter( out );
+
+        this.cellJustifStack = new LinkedList();
+        this.isCellJustifStack = new LinkedList();
+        this.cellCountStack = new LinkedList();
+        this.tableContentWriterStack = new LinkedList();
+        this.tableCaptionWriterStack = new LinkedList();
+        this.tableCaptionXMLWriterStack = new LinkedList();
+        this.tableCaptionStack = new LinkedList();
+
+        init();
+    }
+
+    // ----------------------------------------------------------------------
+    // Accessor methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * To use mainly when playing with the head events.
+     *
+     * @return the current buffer of text events.
+     */
+    protected StringBuffer getTextBuffer()
+    {
+        return this.textBuffer;
+    }
+
+    /**
+     * <p>Setter for the field <code>headFlag</code>.</p>
+     *
+     * @param headFlag an header flag.
+     */
+    protected void setHeadFlag( boolean headFlag )
+    {
+        this.headFlag = headFlag;
+    }
+
+    /**
+     * <p>isHeadFlag.</p>
+     *
+     * @return the current headFlag.
+     */
+    protected boolean isHeadFlag()
+    {
+        return this.headFlag ;
+    }
+
+    /**
+     * <p>Setter for the field <code>verbatimFlag</code>.</p>
+     *
+     * @param verb a verbatim flag.
+     */
+    protected void setVerbatimFlag( boolean verb )
+    {
+        this.verbatimFlag = verb;
+    }
+
+    /**
+     * <p>isVerbatimFlag.</p>
+     *
+     * @return the current verbatim flag.
+     */
+    protected boolean isVerbatimFlag()
+    {
+        return this.verbatimFlag ;
+    }
+
+    /**
+     * <p>Setter for the field <code>cellJustif</code>.</p>
+     *
+     * @param justif the new cell justification array.
+     */
+    protected void setCellJustif( int[] justif )
+    {
+        this.cellJustifStack.addLast( justif );
+        this.isCellJustifStack.addLast( Boolean.TRUE );
+    }
+
+    /**
+     * <p>Getter for the field <code>cellJustif</code>.</p>
+     *
+     * @return the current cell justification array.
+     */
+    protected int[] getCellJustif()
+    {
+        return (int[]) this.cellJustifStack.getLast();
+    }
+
+    /**
+     * <p>Setter for the field <code>cellCount</code>.</p>
+     *
+     * @param count the new cell count.
+     */
+    protected void setCellCount( int count )
+    {
+        this.cellCountStack.addLast( new Integer( count ) );
+    }
+
+    /**
+     * <p>Getter for the field <code>cellCount</code>.</p>
+     *
+     * @return the current cell count.
+     */
+    protected int getCellCount()
+    {
+        return Integer.parseInt( this.cellCountStack.getLast().toString() );
+    }
+
+    /**
+     * Reset all variables.
+     *
+     * @deprecated since 1.1.2, use {@link #init()} instead of.
+     */
+    protected void resetState()
+    {
+        init();
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        resetTextBuffer();
+
+        this.headFlag = false;
+        this.verbatimFlag = false;
+        this.evenTableRow = true;
+        this.cellJustifStack.clear();
+        this.isCellJustifStack.clear();
+        this.cellCountStack.clear();
+        this.tableContentWriterStack.clear();
+        this.tableCaptionWriterStack.clear();
+        this.tableCaptionXMLWriterStack.clear();
+        this.tableCaptionStack.clear();
+
+        this.headFlag = false;
+        this.figureCaptionFlag = false;
+        this.paragraphFlag = false;
+        this.verbatimFlag = false;
+        this.evenTableRow = true;
+        this.tableAttributes = null;
+        this.legacyFigure = false;
+        this.legacyFigureCaption = false;
+        this.inFigure = false;
+        this.tableRows = false;
+        this.warnMessages = null;
+    }
+
+    /**
+     * Reset the text buffer.
+     */
+    protected void resetTextBuffer()
+    {
+        this.textBuffer = new StringBuffer();
+    }
+
+    // ----------------------------------------------------------------------
+    // Sections
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void section( int level, SinkEventAttributes attributes )
+    {
+        onSection( level, attributes );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        onSectionTitle( level, attributes );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+        onSectionTitle_( level );
+    }
+
+    /** {@inheritDoc} */
+    public void section_( int level )
+    {
+        onSection_( level );
+    }
+
+    /** {@inheritDoc} */
+    public void section1()
+    {
+        onSection( SECTION_LEVEL_1, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        onSectionTitle( SECTION_LEVEL_1, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        onSectionTitle_( SECTION_LEVEL_1 );
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        onSection_( SECTION_LEVEL_1 );
+    }
+
+    /** {@inheritDoc} */
+    public void section2()
+    {
+        onSection( SECTION_LEVEL_2, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        onSectionTitle( SECTION_LEVEL_2, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        onSectionTitle_( SECTION_LEVEL_2 );
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        onSection_( SECTION_LEVEL_2 );
+    }
+
+    /** {@inheritDoc} */
+    public void section3()
+    {
+        onSection( SECTION_LEVEL_3, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        onSectionTitle( SECTION_LEVEL_3, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        onSectionTitle_( SECTION_LEVEL_3 );
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        onSection_( SECTION_LEVEL_3 );
+    }
+
+    /** {@inheritDoc} */
+    public void section4()
+    {
+        onSection( SECTION_LEVEL_4, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        onSectionTitle( SECTION_LEVEL_4, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        onSectionTitle_( SECTION_LEVEL_4 );
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        onSection_( SECTION_LEVEL_4 );
+    }
+
+    /** {@inheritDoc} */
+    public void section5()
+    {
+        onSection( SECTION_LEVEL_5, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        onSectionTitle( SECTION_LEVEL_5, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        onSectionTitle_( SECTION_LEVEL_5 );
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        onSection_( SECTION_LEVEL_5 );
+    }
+
+    /**
+     * Starts a section. The default class style is <code>section</code>.
+     *
+     * @param depth The level of the section.
+     * @param attributes some attributes. May be null.
+     * @see javax.swing.text.html.HTML.Tag#DIV
+     */
+    protected void onSection( int depth, SinkEventAttributes attributes )
+    {
+        if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 )
+        {
+            MutableAttributeSet att = new SinkEventAttributeSet();
+            att.addAttribute( Attribute.CLASS, "section" );
+            // NOTE: any class entry in attributes will overwrite the above
+            att.addAttributes( SinkUtils.filterAttributes(
+                    attributes, SinkUtils.SINK_BASE_ATTRIBUTES  ) );
+
+            att.removeAttribute( Attribute.ID.toString() );
+            writeStartTag( HtmlMarkup.DIV, att );
+        }
+    }
+
+    /**
+     * Ends a section.
+     *
+     * @param depth The level of the section.
+     * @see javax.swing.text.html.HTML.Tag#DIV
+     */
+    protected void onSection_( int depth )
+    {
+        if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 )
+        {
+            writeEndTag( HtmlMarkup.DIV );
+        }
+    }
+
+    /**
+     * Starts a section title.
+     *
+     * @param depth The level of the section title.
+     * @param attributes some attributes. May be null.
+     * @see javax.swing.text.html.HTML.Tag#H2
+     * @see javax.swing.text.html.HTML.Tag#H3
+     * @see javax.swing.text.html.HTML.Tag#H4
+     * @see javax.swing.text.html.HTML.Tag#H5
+     * @see javax.swing.text.html.HTML.Tag#H6
+     */
+    protected void onSectionTitle( int depth, SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
+
+        if ( depth == SECTION_LEVEL_1 )
+        {
+            writeStartTag( HtmlMarkup.H2, atts );
+        }
+        else if ( depth == SECTION_LEVEL_2 )
+        {
+            writeStartTag( HtmlMarkup.H3, atts );
+        }
+        else if ( depth == SECTION_LEVEL_3 )
+        {
+            writeStartTag( HtmlMarkup.H4, atts );
+        }
+        else if ( depth == SECTION_LEVEL_4 )
+        {
+            writeStartTag( HtmlMarkup.H5, atts );
+        }
+        else if ( depth == SECTION_LEVEL_5 )
+        {
+            writeStartTag( HtmlMarkup.H6, atts );
+        }
+    }
+
+    /**
+     * Ends a section title.
+     *
+     * @param depth The level of the section title.
+     * @see javax.swing.text.html.HTML.Tag#H2
+     * @see javax.swing.text.html.HTML.Tag#H3
+     * @see javax.swing.text.html.HTML.Tag#H4
+     * @see javax.swing.text.html.HTML.Tag#H5
+     * @see javax.swing.text.html.HTML.Tag#H6
+     */
+    protected void onSectionTitle_( int depth )
+    {
+        if ( depth == SECTION_LEVEL_1 )
+        {
+            writeEndTag( HtmlMarkup.H2 );
+        }
+        else if ( depth == SECTION_LEVEL_2 )
+        {
+            writeEndTag( HtmlMarkup.H3 );
+        }
+        else if ( depth == SECTION_LEVEL_3 )
+        {
+            writeEndTag( HtmlMarkup.H4 );
+        }
+        else if ( depth == SECTION_LEVEL_4 )
+        {
+            writeEndTag( HtmlMarkup.H5 );
+        }
+        else if ( depth == SECTION_LEVEL_5 )
+        {
+            writeEndTag( HtmlMarkup.H6 );
+        }
+    }
+
+    // -----------------------------------------------------------------------
+    //
+    // -----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#UL
+     */
+    public void list()
+    {
+        list( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#UL
+     */
+    public void list( SinkEventAttributes attributes )
+    {
+        if ( paragraphFlag )
+        {
+            // The content of element type "p" must match
+            // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
+            // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
+            paragraph_();
+        }
+
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
+
+        writeStartTag( HtmlMarkup.UL, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#UL
+     */
+    public void list_()
+    {
+        writeEndTag( HtmlMarkup.UL );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
+    public void listItem()
+    {
+        listItem( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
+
+        writeStartTag( HtmlMarkup.LI, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
+    public void listItem_()
+    {
+        writeEndTag( HtmlMarkup.LI );
+    }
+
+    /**
+     * The default list style depends on the numbering.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#OL
+     */
+    public void numberedList( int numbering )
+    {
+        numberedList( numbering, null );
+    }
+
+    /**
+     * The default list style depends on the numbering.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#OL
+     */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        if ( paragraphFlag )
+        {
+            // The content of element type "p" must match
+            // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
+            // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
+            paragraph_();
+        }
+
+        String style;
+        switch ( numbering )
+        {
+            case NUMBERING_UPPER_ALPHA:
+                style = "upper-alpha";
+                break;
+            case NUMBERING_LOWER_ALPHA:
+                style = "lower-alpha";
+                break;
+            case NUMBERING_UPPER_ROMAN:
+                style = "upper-roman";
+                break;
+            case NUMBERING_LOWER_ROMAN:
+                style = "lower-roman";
+                break;
+            case NUMBERING_DECIMAL:
+            default:
+                style = "decimal";
+        }
+
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
+
+        if ( atts == null )
+        {
+            atts = new SinkEventAttributeSet( 1 );
+        }
+
+        atts.addAttribute( Attribute.STYLE, "list-style-type: " + style );
+
+        writeStartTag( HtmlMarkup.OL, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#OL
+     */
+    public void numberedList_()
+    {
+        writeEndTag( HtmlMarkup.OL );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
+    public void numberedListItem()
+    {
+        numberedListItem( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
+
+        writeStartTag( HtmlMarkup.LI, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
+    public void numberedListItem_()
+    {
+        writeEndTag( HtmlMarkup.LI );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DL
+     */
+    public void definitionList()
+    {
+        definitionList( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DL
+     */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        if ( paragraphFlag )
+        {
+            // The content of element type "p" must match
+            // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
+            // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
+            paragraph_();
+        }
+
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
+
+        writeStartTag( HtmlMarkup.DL, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DL
+     */
+    public void definitionList_()
+    {
+        writeEndTag( HtmlMarkup.DL );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DT
+     */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
+
+        writeStartTag( HtmlMarkup.DT, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DT
+     */
+    public void definedTerm()
+    {
+        definedTerm( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DT
+     */
+    public void definedTerm_()
+    {
+        writeEndTag( HtmlMarkup.DT );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DD
+     */
+    public void definition()
+    {
+        definition( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DD
+     */
+    public void definition( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
+
+        writeStartTag( HtmlMarkup.DD, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DD
+     */
+    public void definition_()
+    {
+        writeEndTag( HtmlMarkup.DD );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#IMG
+     * @deprecated Use {@link #figure(SinkEventAttributes)}, this method is only kept for
+     * backward compatibility. Note that the behavior is different though, as this method
+     * writes an img tag, while correctly the img tag should be written by  figureGraphics().
+     */
+    public void figure()
+    {
+        write( String.valueOf( LESS_THAN ) + HtmlMarkup.IMG );
+        legacyFigure = true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#IMG
+     */
+    public void figure( SinkEventAttributes attributes )
+    {
+        inFigure = true;
+
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
+
+        if ( atts == null )
+        {
+            atts = new SinkEventAttributeSet( 1 );
+        }
+
+        if ( !atts.isDefined( SinkEventAttributes.CLASS ) )
+        {
+            atts.addAttribute( SinkEventAttributes.CLASS, "figure" );
+        }
+
+        writeStartTag( HtmlMarkup.DIV, atts );
+    }
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        if ( legacyFigure )
+        {
+            if ( !figureCaptionFlag )
+            {
+                // Attribute "alt" is required and must be specified for element type "img".
+                write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE + QUOTE );
+            }
+            write( String.valueOf( SPACE ) + SLASH + GREATER_THAN );
+            legacyFigure = false;
+        }
+        else
+        {
+            writeEndTag( HtmlMarkup.DIV );
+            inFigure = false;
+        }
+
+        figureCaptionFlag = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @deprecated Use {@link #figureGraphics(String,SinkEventAttributes)},
+     * this method is only kept for backward compatibility. Note that the behavior is
+     * different though, as this method does not write the img tag, only the src attribute.
+     */
+    public void figureGraphics( String name )
+    {
+        write( String.valueOf( SPACE ) + Attribute.SRC + EQUAL + QUOTE + escapeHTML( name ) + QUOTE );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        if ( inFigure )
+        {
+            MutableAttributeSet atts = new SinkEventAttributeSet( 1 );
+            atts.addAttribute( SinkEventAttributes.ALIGN, "center" );
+
+            writeStartTag( HtmlMarkup.P, atts );
+        }
+
+        MutableAttributeSet filtered = SinkUtils.filterAttributes( attributes, SinkUtils.SINK_IMG_ATTRIBUTES );
+        if ( filtered != null )
+        {
+            filtered.removeAttribute( Attribute.SRC.toString() );
+        }
+
+        int count = ( attributes == null ? 1 : attributes.getAttributeCount() + 1 );
+
+        MutableAttributeSet atts = new SinkEventAttributeSet( count );
+
+        atts.addAttribute( Attribute.SRC, escapeHTML( src ) );
+        atts.addAttributes( filtered );
+
+        if ( atts.getAttribute( Attribute.ALT.toString() ) == null )
+        {
+            atts.addAttribute( Attribute.ALT.toString(), "" );
+        }
+
+        writeStartTag( HtmlMarkup.IMG, atts, true );
+
+        if ( inFigure )
+        {
+            writeEndTag( HtmlMarkup.P );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @deprecated Use {@link #figureCaption(SinkEventAttributes)},
+     * this method is only kept for backward compatibility. Note that the behavior is
+     * different though, as this method only writes an alt attribute.
+     */
+    public void figureCaption()
+    {
+        figureCaptionFlag = true;
+        write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE );
+        legacyFigureCaption = true;
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        if ( legacyFigureCaption )
+        {
+            write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE );
+            legacyFigureCaption = false;
+            figureCaptionFlag = true;
+        }
+        else
+        {
+            SinkEventAttributeSet atts = new SinkEventAttributeSet( 1 );
+            atts.addAttribute( SinkEventAttributes.ALIGN, "center" );
+            atts.addAttributes( SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  ) );
+
+            paragraph( atts );
+            italic();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        if ( legacyFigureCaption )
+        {
+            write( String.valueOf( QUOTE ) );
+        }
+        else
+        {
+            italic_();
+            paragraph_();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#P
+     */
+    public void paragraph()
+    {
+        paragraph( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#P
+     */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        paragraphFlag = true;
+
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
+
+        writeStartTag( HtmlMarkup.P, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#P
+     */
+    public void paragraph_()
+    {
+        if ( paragraphFlag )
+        {
+            writeEndTag( HtmlMarkup.P );
+            paragraphFlag = false;
+        }
+    }
+
+    /**
+     * The default class style for boxed is <code>source</code>.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DIV
+     * @see javax.swing.text.html.HTML.Tag#PRE
+     */
+    public void verbatim( boolean boxed )
+    {
+        if ( boxed )
+        {
+            verbatim( SinkEventAttributeSet.BOXED );
+        }
+        else
+        {
+            verbatim( null );
+        }
+    }
+
+    /**
+     * The default class style for boxed is <code>source</code>.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DIV
+     * @see javax.swing.text.html.HTML.Tag#PRE
+     */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        if ( paragraphFlag )
+        {
+            // The content of element type "p" must match
+            // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
+            // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
+            paragraph_();
+        }
+
+        verbatimFlag = true;
+
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES  );
+
+        if ( atts == null )
+        {
+            atts = new SinkEventAttributeSet();
+        }
+
+        boolean boxed = false;
+
+        if ( atts.isDefined( SinkEventAttributes.DECORATION ) )
+        {
+            boxed =
+                "boxed".equals( atts.getAttribute( SinkEventAttributes.DECORATION ).toString() );
+        }
+
+        if ( boxed )
+        {
+            atts.addAttribute( Attribute.CLASS, "source" );
+        }
+
+        atts.removeAttribute( SinkEventAttributes.DECORATION );
+
+        String width = (String) atts.getAttribute( Attribute.WIDTH.toString() );
+        atts.removeAttribute( Attribute.WIDTH.toString() );
+
+        writeStartTag( HtmlMarkup.DIV, atts );
+
+        if ( width != null )
+        {
+            atts.addAttribute( Attribute.WIDTH.toString(), width );
+        }
+
+        atts.removeAttribute( Attribute.ALIGN.toString() );
+        atts.removeAttribute( Attribute.CLASS.toString() );
+
+        writeStartTag( HtmlMarkup.PRE, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DIV
+     * @see javax.swing.text.html.HTML.Tag#PRE
+     */
+    public void verbatim_()
+    {
+        writeEndTag( HtmlMarkup.PRE );
+        writeEndTag( HtmlMarkup.DIV );
+
+        verbatimFlag = false;
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#HR
+     */
+    public void horizontalRule()
+    {
+        horizontalRule( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#HR
+     */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_HR_ATTRIBUTES  );
+
+        writeSimpleTag( HtmlMarkup.HR, atts );
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        // start table with tableRows
+        table( null );
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        this.tableContentWriterStack.addLast( new StringWriter() );
+        this.tableRows = false;
+
+        if ( paragraphFlag )
+        {
+            // The content of element type "p" must match
+            // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
+            // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
+            paragraph_();
+        }
+
+        // start table with tableRows
+        if ( attributes == null )
+        {
+            this.tableAttributes = new SinkEventAttributeSet( 0 );
+        }
+        else
+        {
+            this.tableAttributes = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_TABLE_ATTRIBUTES  );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TABLE
+     */
+    public void table_()
+    {
+        this.tableRows = false;
+
+        writeEndTag( HtmlMarkup.TABLE );
+
+        if ( !this.cellCountStack.isEmpty() )
+        {
+            this.cellCountStack.removeLast().toString();
+        }
+
+        if ( this.tableContentWriterStack.isEmpty() )
+        {
+            if ( getLog().isWarnEnabled() )
+            {
+                getLog().warn( "No table content." );
+            }
+            return;
+        }
+
+        String tableContent = this.tableContentWriterStack.removeLast().toString();
+
+        String tableCaption = null;
+        if ( !this.tableCaptionStack.isEmpty() && this.tableCaptionStack.getLast() != null )
+        {
+            tableCaption = this.tableCaptionStack.removeLast().toString();
+        }
+
+        if ( tableCaption != null )
+        {
+            // DOXIA-177
+            StringBuffer sb = new StringBuffer();
+            sb.append( tableContent.substring( 0, tableContent.indexOf( Markup.GREATER_THAN ) + 1 ) );
+            sb.append( tableCaption );
+            sb.append( tableContent.substring( tableContent.indexOf( Markup.GREATER_THAN ) + 1 ) );
+
+            write( sb.toString() );
+        }
+        else
+        {
+            write( tableContent );
+        }
+    }
+
+    /**
+     * The default class style is <code>bodyTable</code>.
+     * The default align is <code>center</code>.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TABLE
+     */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        this.tableRows = true;
+
+        setCellJustif( justification );
+
+        if ( this.tableAttributes == null )
+        {
+            this.tableAttributes = new SinkEventAttributeSet( 0 );
+        }
+
+        MutableAttributeSet att = new SinkEventAttributeSet();
+        if ( !this.tableAttributes.isDefined( Attribute.BORDER.toString() ) )
+        {
+            att.addAttribute( Attribute.BORDER, ( grid ? "1" : "0" ) );
+        }
+
+        if ( !this.tableAttributes.isDefined( Attribute.CLASS.toString() ) )
+        {
+            att.addAttribute( Attribute.CLASS, "bodyTable" );
+        }
+
+        att.addAttributes( this.tableAttributes );
+        this.tableAttributes.removeAttributes( this.tableAttributes );
+
+        writeStartTag( HtmlMarkup.TABLE, att );
+
+        this.cellCountStack.addLast( new Integer( 0 ) );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        this.tableRows = false;
+        if ( !this.cellJustifStack.isEmpty() )
+        {
+            this.cellJustifStack.removeLast();
+        }
+        if ( !this.isCellJustifStack.isEmpty() )
+        {
+            this.isCellJustifStack.removeLast();
+        }
+
+        this.evenTableRow = true;
+    }
+
+    /**
+     * The default class style is <code>a</code> or <code>b</code> depending the row id.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TR
+     */
+    public void tableRow()
+    {
+        // To be backward compatible
+        if ( !this.tableRows )
+        {
+            tableRows( null, false );
+        }
+        tableRow( null );
+    }
+
+    /**
+     * The default class style is <code>a</code> or <code>b</code> depending the row id.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TR
+     */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet att = new SinkEventAttributeSet();
+
+        if ( evenTableRow )
+        {
+            att.addAttribute( Attribute.CLASS, "a" );
+        }
+        else
+        {
+            att.addAttribute( Attribute.CLASS, "b" );
+        }
+
+        att.addAttributes( SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_TR_ATTRIBUTES  ) );
+
+        writeStartTag( HtmlMarkup.TR, att );
+
+        evenTableRow = !evenTableRow;
+
+        if ( !this.cellCountStack.isEmpty() )
+        {
+            this.cellCountStack.removeLast();
+            this.cellCountStack.addLast( new Integer( 0 ) );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TR
+     */
+    public void tableRow_()
+    {
+        writeEndTag( HtmlMarkup.TR );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        tableCell( (SinkEventAttributeSet) null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        tableHeaderCell( (SinkEventAttributeSet) null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        MutableAttributeSet att = new SinkEventAttributeSet();
+        att.addAttribute( Attribute.WIDTH, width );
+
+        tableCell( false, att );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        MutableAttributeSet att = new SinkEventAttributeSet();
+        att.addAttribute( Attribute.WIDTH, width );
+
+        tableCell( true, att );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        tableCell( false, attributes );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        tableCell( true, attributes );
+    }
+
+    /**
+     * @param headerRow true if it is an header row
+     * @param attributes the cell attributes
+     * @see javax.swing.text.html.HTML.Tag#TH
+     * @see javax.swing.text.html.HTML.Tag#TD
+     */
+    private void tableCell( boolean headerRow, MutableAttributeSet attributes )
+    {
+        Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD );
+
+        MutableAttributeSet att = new SinkEventAttributeSet();
+
+        if ( attributes == null )
+        {
+            attributes = new SinkEventAttributeSet( 0 );
+        }
+
+        att.addAttributes( SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_TD_ATTRIBUTES  ) );
+
+        writeStartTag( t, att );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        tableCell_( false );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        tableCell_( true );
+    }
+
+    /**
+     * Ends a table cell.
+     *
+     * @param headerRow true if it is an header row
+     * @see javax.swing.text.html.HTML.Tag#TH
+     * @see javax.swing.text.html.HTML.Tag#TD
+     */
+    private void tableCell_( boolean headerRow )
+    {
+        Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD );
+
+        writeEndTag( t );
+
+        if ( !this.isCellJustifStack.isEmpty() && this.isCellJustifStack.getLast().equals( Boolean.TRUE )
+            && !this.cellCountStack.isEmpty() )
+        {
+            int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() );
+            this.cellCountStack.addLast( new Integer( ++cellCount ) );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#CAPTION
+     */
+    public void tableCaption()
+    {
+        tableCaption( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#CAPTION
+     */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        StringWriter sw = new StringWriter();
+        this.tableCaptionWriterStack.addLast( sw );
+        this.tableCaptionXMLWriterStack.addLast( new PrettyPrintXMLWriter( sw ) );
+
+        // TODO: tableCaption should be written before tableRows (DOXIA-177)
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
+
+        writeStartTag( HtmlMarkup.CAPTION, atts );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#CAPTION
+     */
+    public void tableCaption_()
+    {
+        writeEndTag( HtmlMarkup.CAPTION );
+
+        if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
+        {
+            this.tableCaptionStack.addLast( this.tableCaptionWriterStack.removeLast().toString() );
+            this.tableCaptionXMLWriterStack.removeLast();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
+    public void anchor( String name )
+    {
+        anchor( name, null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Anchor name cannot be null!" );
+        }
+
+        if ( headFlag )
+        {
+            return;
+        }
+
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
+
+        String id = name;
+
+        if ( !DoxiaUtils.isValidId( id ) )
+        {
+            id = DoxiaUtils.encodeId( name, true );
+
+            String msg = "Modified invalid anchor name: '" + name + "' to '" + id + "'";
+            logMessage( "modifiedLink", msg );
+        }
+
+        MutableAttributeSet att = new SinkEventAttributeSet();
+        att.addAttribute( Attribute.NAME, id );
+        att.addAttributes( atts );
+
+        writeStartTag( HtmlMarkup.A, att );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
+    public void anchor_()
+    {
+        if ( !headFlag )
+        {
+            writeEndTag( HtmlMarkup.A );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        link( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        if ( attributes == null )
+        {
+            link( name, null, null );
+        }
+        else
+        {
+            String target = (String) attributes.getAttribute( Attribute.TARGET.toString() );
+            MutableAttributeSet atts = SinkUtils.filterAttributes(
+                    attributes, SinkUtils.SINK_LINK_ATTRIBUTES  );
+
+            link( name, target, atts );
+        }
+    }
+
+    /**
+     * Adds a link with an optional target.
+     * The default style class for external link is <code>externalLink</code>.
+     *
+     * @param href the link href.
+     * @param target the link target, may be null.
+     * @param attributes an AttributeSet, may be null.
+     *      This is supposed to be filtered already.
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
+    private void link( String href, String target, MutableAttributeSet attributes )
+    {
+        if ( href == null )
+        {
+            throw new NullPointerException( "Link name cannot be null!" );
+        }
+
+        if ( headFlag )
+        {
+            return;
+        }
+
+        MutableAttributeSet att = new SinkEventAttributeSet();
+
+        if ( DoxiaUtils.isExternalLink( href  ) )
+        {
+            att.addAttribute( Attribute.CLASS, "externalLink" );
+        }
+
+        att.addAttribute( Attribute.HREF, HtmlTools.escapeHTML( href  ) );
+
+        if ( target != null )
+        {
+            att.addAttribute( Attribute.TARGET, target );
+        }
+
+        if ( attributes != null )
+        {
+            attributes.removeAttribute( Attribute.HREF.toString() );
+            attributes.removeAttribute( Attribute.TARGET.toString() );
+            att.addAttributes( attributes );
+        }
+
+        writeStartTag( HtmlMarkup.A, att );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
+    public void link_()
+    {
+        if ( !headFlag )
+        {
+            writeEndTag( HtmlMarkup.A );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#I
+     */
+    public void italic()
+    {
+        if ( !headFlag )
+        {
+            writeStartTag( HtmlMarkup.I );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#I
+     */
+    public void italic_()
+    {
+        if ( !headFlag )
+        {
+            writeEndTag( HtmlMarkup.I );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#B
+     */
+    public void bold()
+    {
+        if ( !headFlag )
+        {
+            writeStartTag( HtmlMarkup.B );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#B
+     */
+    public void bold_()
+    {
+        if ( !headFlag )
+        {
+            writeEndTag( HtmlMarkup.B );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TT
+     */
+    public void monospaced()
+    {
+        if ( !headFlag )
+        {
+            writeStartTag( HtmlMarkup.TT );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TT
+     */
+    public void monospaced_()
+    {
+        if ( !headFlag )
+        {
+            writeEndTag( HtmlMarkup.TT );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BR
+     */
+    public void lineBreak()
+    {
+        lineBreak( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BR
+     */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        if ( headFlag || isVerbatimFlag() )
+        {
+            getTextBuffer().append( EOL );
+        }
+        else
+        {
+            MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_BR_ATTRIBUTES  );
+
+            writeSimpleTag( HtmlMarkup.BR, atts );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        comment( "PB" );
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        if ( headFlag )
+        {
+            getTextBuffer().append( ' ' );
+        }
+        else
+        {
+            write( " " );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        if ( headFlag )
+        {
+            getTextBuffer().append( text );
+        }
+        else if ( verbatimFlag )
+        {
+            verbatimContent( text );
+        }
+        else
+        {
+            content( text );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        if ( attributes == null )
+        {
+            text( text );
+        }
+        else
+        {
+            if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "underline" ) )
+            {
+                writeStartTag( HtmlMarkup.U );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "line-through" ) )
+            {
+                writeStartTag( HtmlMarkup.S );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sub" ) )
+            {
+                writeStartTag( HtmlMarkup.SUB );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sup" ) )
+            {
+                writeStartTag( HtmlMarkup.SUP );
+            }
+
+            text( text );
+
+            if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sup" ) )
+            {
+                writeEndTag( HtmlMarkup.SUP );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sub" ) )
+            {
+                writeEndTag( HtmlMarkup.SUB );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "line-through" ) )
+            {
+                writeEndTag( HtmlMarkup.S );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "underline" ) )
+            {
+                writeEndTag( HtmlMarkup.U );
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        if ( headFlag )
+        {
+            getTextBuffer().append( text );
+        }
+        else
+        {
+            write( text );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+        if ( StringUtils.isNotEmpty( comment ) && comment.indexOf( "--" ) != -1 )
+        {
+            String originalComment = comment;
+            // http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments
+            while ( comment.indexOf( "--" ) != -1 )
+            {
+                comment = StringUtils.replace( comment, "--", "- -" );
+            }
+
+            getLog()
+                    .warn( "[Xhtml Sink] Modified invalid comment: '" + originalComment + "' to '" + comment + "'" );
+        }
+
+        StringBuffer buf = new StringBuffer( comment.length() + 9 );
+
+        buf.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS ).append( SPACE );
+        buf.append( comment );
+        buf.append( SPACE ).append( MINUS ).append( MINUS ).append( GREATER_THAN );
+
+        write( buf.toString() );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Adding an unkown event, <i>ie</i> an event that was not recognized by a parser.
+     * If {@link org.apache.maven.doxia.util.HtmlTools#getHtmlTag(String) HtmlTools.getHtmlTag( name )}
+     * does not return null, the corresponding tag will be written.
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        if ( requiredParams == null || !( requiredParams[0] instanceof Integer ) )
+        {
+            String msg = "No type information for unknown event: '" + name + "', ignoring!";
+            logMessage( "noTypeInfo", msg );
+
+            return;
+        }
+
+        int tagType = ( (Integer) requiredParams[0] ).intValue();
+
+        if ( tagType == ENTITY_TYPE )
+        {
+            rawText( name );
+
+            return;
+        }
+
+        if ( tagType == CDATA_TYPE )
+        {
+            rawText( EOL + "//<![CDATA[" + requiredParams[1] + "]]>" + EOL );
+
+            return;
+        }
+
+        Tag tag = HtmlTools.getHtmlTag( name );
+
+        if ( tag == null )
+        {
+            String msg = "No HTML tag found for unknown event: '" + name + "', ignoring!";
+            logMessage( "noHtmlTag", msg );
+        }
+        else
+        {
+            if ( tagType == TAG_TYPE_SIMPLE )
+            {
+                writeSimpleTag( tag, escapeAttributeValues( attributes ) );
+            }
+            else if ( tagType == TAG_TYPE_START )
+            {
+                writeStartTag( tag, escapeAttributeValues( attributes ) );
+            }
+            else if ( tagType == TAG_TYPE_END )
+            {
+                writeEndTag( tag );
+            }
+            else
+            {
+                String msg = "No type information for unknown event: '" + name + "', ignoring!";
+                logMessage( "noTypeInfo", msg );
+            }
+        }
+    }
+
+    private SinkEventAttributes escapeAttributeValues( SinkEventAttributes attributes )
+    {
+        SinkEventAttributeSet set = new SinkEventAttributeSet( attributes.getAttributeCount() );
+
+        Enumeration names = attributes.getAttributeNames();
+
+        while ( names.hasMoreElements() )
+        {
+            Object name = names.nextElement();
+
+            set.addAttribute( name, escapeHTML( attributes.getAttribute( name ).toString() ) );
+        }
+
+        return set;
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        writer.flush();
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        writer.close();
+
+        if ( getLog().isWarnEnabled() && this.warnMessages != null )
+        {
+            for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                Set set = (Set) entry.getValue();
+
+                for ( Iterator it2 = set.iterator(); it2.hasNext(); )
+                {
+                    String msg = (String) it2.next();
+
+                    getLog().warn( msg );
+                }
+            }
+
+            this.warnMessages = null;
+        }
+
+        init();
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * Write HTML escaped text to output.
+     *
+     * @param text The text to write.
+     */
+    protected void content( String text )
+    {
+        // small hack due to DOXIA-314
+        text = escapeHTML( text );
+        text = StringUtils.replace( text, "&#", "&#" );
+        write( text );
+    }
+
+    /**
+     * Write HTML escaped text to output.
+     *
+     * @param text The text to write.
+     */
+    protected void verbatimContent( String text )
+    {
+        write( escapeHTML( text ) );
+    }
+
+    /**
+     * Forward to HtmlTools.escapeHTML( text ).
+     *
+     * @param text the String to escape, may be null
+     * @return the text escaped, "" if null String input
+     * @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String)
+     */
+    protected static String escapeHTML( String text )
+    {
+        return HtmlTools.escapeHTML( text, false );
+    }
+
+    /**
+     * Forward to HtmlTools.encodeURL( text ).
+     *
+     * @param text the String to encode, may be null.
+     * @return the text encoded, null if null String input.
+     * @see org.apache.maven.doxia.util.HtmlTools#encodeURL(String)
+     */
+    protected static String encodeURL( String text )
+    {
+        return HtmlTools.encodeURL( text );
+    }
+
+    /** {@inheritDoc} */
+    protected void write( String text )
+    {
+        if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
+        {
+            ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() ).writeText( unifyEOLs( text ) );
+        }
+        else if ( !this.tableContentWriterStack.isEmpty() && this.tableContentWriterStack.getLast() != null )
+        {
+            ( (StringWriter) this.tableContentWriterStack.getLast() ).write( unifyEOLs( text ) );
+        }
+        else
+        {
+            writer.write( unifyEOLs( text ) );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
+    {
+        if ( this.tableCaptionXMLWriterStack.isEmpty() )
+        {
+            super.writeStartTag ( t, att, isSimpleTag );
+        }
+        else
+        {
+            String tag = ( getNameSpace() != null ? getNameSpace() + ":" : "" ) + t.toString();
+            ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() ).startElement( tag );
+
+            if ( att != null )
+            {
+                Enumeration names = att.getAttributeNames();
+                while ( names.hasMoreElements() )
+                {
+                    Object key = names.nextElement();
+                    Object value = att.getAttribute( key );
+
+                    ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() )
+                            .addAttribute( key.toString(), value.toString() );
+                }
+            }
+
+            if ( isSimpleTag )
+            {
+                ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() ).endElement();
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void writeEndTag( Tag t )
+    {
+        if ( this.tableCaptionXMLWriterStack.isEmpty() )
+        {
+            super.writeEndTag( t );
+        }
+        else
+        {
+            ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() ).endElement();
+        }
+    }
+
+    /**
+     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
+     *
+     * @param key not null
+     * @param msg not null
+     * @see #close()
+     * @since 1.1.1
+     */
+    private void logMessage( String key, String msg )
+    {
+        msg = "[XHTML Sink] " + msg;
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( msg );
+
+            return;
+        }
+
+        if ( warnMessages == null )
+        {
+            warnMessages = new HashMap();
+        }
+
+        Set set = (Set) warnMessages.get( key );
+        if ( set == null )
+        {
+            set = new TreeSet();
+        }
+        set.add( msg );
+        warnMessages.put( key, set );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/sink/render/RenderingContext.java b/doxia-core/src/main/java/org/apache/maven/doxia/sink/render/RenderingContext.java
new file mode 100644
index 0000000..1c86502
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/sink/render/RenderingContext.java
@@ -0,0 +1,194 @@
+package org.apache.maven.doxia.sink.render;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.codehaus.plexus.util.PathTool;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * <p>RenderingContext class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: RenderingContext.java 785531 2009-06-17 09:47:59Z ltheussl $
+ * @since 1.1
+ */
+public class RenderingContext
+{
+    private final File basedir;
+
+    private final String inputName;
+
+    private final String outputName;
+
+    private final String parserId;
+
+    private final String relativePath;
+
+    private final String extension;
+
+    private Map attributes;
+
+    /**
+     * <p>Constructor for RenderingContext.</p>
+     *
+     * @param basedir a {@link java.io.File} object.
+     * @param document a {@link java.lang.String} object.
+     */
+    public RenderingContext( File basedir, String document )
+    {
+        this( basedir, document, null );
+    }
+
+    /**
+     * <p>Constructor for RenderingContext.</p>
+     *
+     * @param basedir a {@link java.io.File} object.
+     * @param document a {@link java.lang.String} object.
+     * @param parserId a {@link java.lang.String} object.
+     */
+    public RenderingContext( File basedir, String document, String parserId )
+    {
+        this( basedir, document, parserId, null );
+
+    }
+
+    /**
+     * <p>Constructor for RenderingContext.</p>
+     *
+     * @param basedir a {@link java.io.File} object.
+     * @param document a {@link java.lang.String} object.
+     * @param parserId a {@link java.lang.String} object.
+     * @param extension a {@link java.lang.String} object.
+     */
+    public RenderingContext( File basedir, String document, String parserId, String extension )
+    {
+        this.basedir = basedir;
+        this.extension = extension;
+        if ( StringUtils.isNotEmpty( extension ) )
+        {
+            // here we now the parserId we can play with this
+            // index.xml -> index.html
+            // index.xml.vm -> index.html
+            // download.apt.vm --> download.html
+            int startIndexOfExtension =
+                document.toLowerCase( Locale.ENGLISH ).indexOf( "." + extension.toLowerCase( Locale.ENGLISH ) );
+            String fileNameWithoutExt = document.substring( 0, startIndexOfExtension );
+            this.outputName = fileNameWithoutExt + ".html";
+        }
+        else
+        {
+            this.outputName = document.substring( 0, document.indexOf( "." ) ).replace( '\\', '/' ) + ".html";
+        }
+        this.relativePath = PathTool.getRelativePath( basedir.getPath(), new File( basedir, document ).getPath() );
+
+        this.inputName = document;
+
+        this.parserId = parserId;
+
+        this.attributes = new HashMap();
+    }
+
+    /**
+     * <p>Getter for the field <code>basedir</code>.</p>
+     *
+     * @return a {@link java.io.File} object.
+     */
+    public File getBasedir()
+    {
+        return basedir;
+    }
+
+    /**
+     * <p>Getter for the field <code>inputName</code>.</p>
+     *
+     * @return a {@link java.lang.String} object.
+     */
+    public String getInputName()
+    {
+        return inputName;
+    }
+
+    /**
+     * <p>Getter for the field <code>outputName</code>.</p>
+     *
+     * @return a {@link java.lang.String} object.
+     */
+    public String getOutputName()
+    {
+        return outputName;
+    }
+
+    /**
+     * <p>Getter for the field <code>parserId</code>.</p>
+     *
+     * @return a {@link java.lang.String} object.
+     */
+    public String getParserId()
+    {
+        return parserId;
+    }
+
+    /**
+     * <p>Getter for the field <code>relativePath</code>.</p>
+     *
+     * @return a {@link java.lang.String} object.
+     */
+    public String getRelativePath()
+    {
+        return relativePath;
+    }
+
+    /**
+     * <p>setAttribute.</p>
+     *
+     * @param key a {@link java.lang.String} object.
+     * @param value a {@link java.lang.String} object.
+     */
+    public void setAttribute( String key, String value )
+    {
+        attributes.put( key, value );
+    }
+
+    /**
+     * <p>getAttribute.</p>
+     *
+     * @param key a {@link java.lang.String} object.
+     * @return a {@link java.lang.String} object.
+     */
+    public String getAttribute( String key )
+    {
+        return (String) attributes.get( key );
+    }
+
+    /**
+     * <p>Getter for the field <code>extension</code>.</p>
+     *
+     * @return a {@link java.lang.String} object.
+     */
+    public String getExtension()
+    {
+        return extension;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/util/ByLineReaderSource.java b/doxia-core/src/main/java/org/apache/maven/doxia/util/ByLineReaderSource.java
new file mode 100644
index 0000000..ea98832
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/util/ByLineReaderSource.java
@@ -0,0 +1,155 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Originally from org.apache.doxia.module.apt.AptReaderSource. It was modified
+ * to get unget support
+ */
+
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.Reader;
+
+import org.apache.maven.doxia.parser.ParseException;
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * {@link ByLineSource} default implementation
+ *
+ * @version $Id: ByLineReaderSource.java 775115 2009-05-15 12:54:18Z ltheussl $
+ */
+public class ByLineReaderSource implements ByLineSource
+{
+    /**
+     * reader
+     */
+    private LineNumberReader reader;
+
+    /**
+     * current line number
+     */
+    private int lineNumber;
+
+    /**
+     * holds the last line returned by getNextLine()
+     */
+    private String lastLine;
+
+    /**
+     * <code>true</code> if ungetLine() was called and no getNextLine() was
+     * called
+     */
+    private boolean ungetted = false;
+
+    /**
+     * Creates the ByLineReaderSource.
+     *
+     * @param in real source :)
+     */
+    public ByLineReaderSource( final Reader in )
+    {
+        reader = new LineNumberReader( in );
+
+        lineNumber = -1;
+    }
+
+    /** {@inheritDoc} */
+    public final String getNextLine() throws ParseException
+    {
+        if ( reader == null )
+        {
+            return null;
+        }
+
+        if ( ungetted )
+        {
+            ungetted = false;
+            return lastLine;
+        }
+
+        String line;
+
+        try
+        {
+            line = reader.readLine();
+            if ( line == null )
+            {
+                reader.close();
+                reader = null;
+            }
+            else
+            {
+                lineNumber = reader.getLineNumber();
+            }
+        }
+        catch ( IOException e )
+        {
+            throw new ParseException( e, lineNumber, 0 );
+        }
+
+        lastLine = line;
+
+        return line;
+    }
+
+    /** {@inheritDoc} */
+    public final String getName()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    public final int getLineNumber()
+    {
+        return lineNumber;
+    }
+
+    /** {@inheritDoc} */
+    public final void close()
+    {
+        IOUtil.close( reader );
+        reader = null;
+    }
+
+    /** {@inheritDoc} */
+    public final void ungetLine()
+    {
+        if ( ungetted )
+        {
+            throw new IllegalStateException( "we support only one level of ungetLine()" );
+        }
+        ungetted = true;
+    }
+
+    /** {@inheritDoc} */
+    public final void unget( final String s )
+    {
+        if ( s == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+        if ( s.length() != 0 )
+        {
+            ungetLine();
+            lastLine = s;
+        }
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/util/ByLineSource.java b/doxia-core/src/main/java/org/apache/maven/doxia/util/ByLineSource.java
new file mode 100644
index 0000000..3558eda
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/util/ByLineSource.java
@@ -0,0 +1,78 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * The token are the new lines :)
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 4, 2005
+ * @version $Id: ByLineSource.java 785531 2009-06-17 09:47:59Z ltheussl $
+ */
+public interface ByLineSource
+{
+    /**
+     * <p>getNextLine.</p>
+     *
+     * @return the next line. <code>null</code> if we reached the end.
+     * @throws org.apache.maven.doxia.parser.ParseException on I/O error
+     */
+    String getNextLine() throws ParseException;
+
+    /**
+     * <p>getName.</p>
+     *
+     * @return the name of the input. could be the filename for example.
+     */
+    String getName();
+
+    /**
+     * <p>getLineNumber.</p>
+     *
+     * @return the current line number.
+     */
+    int getLineNumber();
+
+    /**
+     * <p>ungetLine.</p>
+     *
+     * This should throw a java.lang.IllegalStateException if called more than
+     *                               one time without calling getNextLine().
+     */
+    void ungetLine();
+
+
+    /**
+     * <p>unget.</p>
+     *
+     * @param s some text to push back to the parser.
+     * This should throw a java.lang.IllegalStateException if called more than
+     *                               one time without calling getNextLine().
+     */
+    void unget( String s );
+
+
+    /**
+     * close the source.
+     */
+    void close();
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/util/DoxiaUtils.java b/doxia-core/src/main/java/org/apache/maven/doxia/util/DoxiaUtils.java
new file mode 100644
index 0000000..b49a0dd
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/util/DoxiaUtils.java
@@ -0,0 +1,411 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.awt.image.BufferedImage;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import java.net.URL;
+
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+
+import java.util.Date;
+import java.util.Locale;
+
+import javax.imageio.ImageIO;
+
+import javax.swing.text.MutableAttributeSet;
+
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+/**
+ * General Doxia utility methods. The methods in this class should not assume
+ * any specific Doxia module or document format.
+ *
+ * @author ltheussl
+ * @since 1.1
+ * @version $Id: DoxiaUtils.java 782648 2009-06-08 14:34:10Z ltheussl $
+ */
+public class DoxiaUtils
+{
+
+    private static final int MINUS_ONE = 0xFF;
+
+    /**
+     * Checks if the given string corresponds to an internal link,
+     * ie it is a link to an anchor within the same document.
+     *
+     * @param link The link to check.
+     * @return True if the link starts with "#".
+     *
+     * @see #isExternalLink(String)
+     * @see #isLocalLink(String)
+     */
+    public static boolean isInternalLink( String link )
+    {
+        return link.startsWith( "#" );
+    }
+
+    /**
+     * Checks if the given string corresponds to an external URI,
+     * ie is not a link within the same document nor a relative link
+     * to another document (a local link) of the same site.
+     *
+     * @param link The link to check.
+     * @return True if the link (ignoring case) starts with either "http:/",
+     * "https:/", "ftp:/", "mailto:", "file:/", or contains the string "://".
+     * Note that Windows style separators "\" are not allowed
+     * for URIs, see  http://www.ietf.org/rfc/rfc2396.txt , section 2.4.3.
+     *
+     * @see #isInternalLink(String)
+     * @see #isLocalLink(String)
+     */
+    public static boolean isExternalLink( String link )
+    {
+        String text = link.toLowerCase( Locale.ENGLISH );
+
+        return ( text.startsWith( "http:/" ) || text.startsWith( "https:/" )
+            || text.startsWith( "ftp:/" ) || text.startsWith( "mailto:" )
+            || text.startsWith( "file:/" ) || text.indexOf( "://" ) != -1 );
+    }
+
+    /**
+     * Checks if the given string corresponds to a relative link to another document
+     * within the same site, ie it is neither an {@link #isInternalLink(String) internal}
+     * nor an {@link #isExternalLink(String) external} link.
+     *
+     * @param link The link to check.
+     * @return True if the link is neither an external nor an internal link.
+     *
+     * @see #isExternalLink(String)
+     * @see #isInternalLink(String)
+     */
+    public static boolean isLocalLink( String link )
+    {
+        return ( !isExternalLink( link ) && !isInternalLink( link ) );
+    }
+
+    /**
+     * Construct a valid Doxia id.
+     *
+     * <p>
+     *   This method is equivalent to {@link #encodeId(java.lang.String, boolean) encodeId( id, false )}.
+     * </p>
+     *
+     * @param id The id to be encoded.
+     * @return The trimmed and encoded id, or null if id is null.
+     * @see #encodeId(java.lang.String, boolean)
+     */
+    public static String encodeId( String id )
+    {
+        return encodeId( id, false );
+    }
+
+    /**
+     * Construct a valid Doxia id.
+     *
+     * <p>
+     *   A valid Doxia id obeys the same constraints as an HTML ID or NAME token.
+     *   According to the <a href="http://www.w3.org/TR/html4/types.html#type-name">
+     *   HTML 4.01 specification section 6.2 SGML basic types</a>:
+     * </p>
+     * <p>
+     *   <i>ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
+     *   followed by any number of letters, digits ([0-9]), hyphens ("-"),
+     *   underscores ("_"), colons (":"), and periods (".").</i>
+     * </p>
+     * <p>
+     *   According to <a href="http://www.w3.org/TR/xhtml1/#C_8">XHTML 1.0
+     *   section C.8. Fragment Identifiers</a>:
+     * </p>
+     * <p>
+     *   <i>When defining fragment identifiers to be backward-compatible, only
+     *   strings matching the pattern [A-Za-z][A-Za-z0-9:_.-]* should be used.</i>
+     * </p>
+     * <p>
+     *   To achieve this we need to convert the <i>id</i> String. Two conversions
+     *   are necessary and one is done to get prettier ids:
+     * </p>
+     * <ol>
+     *   <li>Remove whitespace at the start and end before starting to process</li>
+     *   <li>If the first character is not a letter, prepend the id with the letter 'a'</li>
+     *   <li>Any spaces are replaced with an underscore '_'</li>
+     *   <li>
+     *     Any characters not matching the above pattern are either dropped,
+     *     or replaced according to the rules specified in the
+     *     <a href="http://www.w3.org/TR/html4/appendix/notes.html#non-ascii-chars">HTML specs</a>.
+     *   </li>
+     * </ol>
+     * <p>
+     *   For letters, the case is preserved in the conversion.
+     * </p>
+     *
+     * <p>
+     * Here are some examples:
+     * </p>
+     * <pre>
+     * DoxiaUtils.encodeId( null )        = null
+     * DoxiaUtils.encodeId( "" )          = "a"
+     * DoxiaUtils.encodeId( "  " )        = "a"
+     * DoxiaUtils.encodeId( " _ " )       = "a_"
+     * DoxiaUtils.encodeId( "1" )         = "a1"
+     * DoxiaUtils.encodeId( "1anchor" )   = "a1anchor"
+     * DoxiaUtils.encodeId( "_anchor" )   = "a_anchor"
+     * DoxiaUtils.encodeId( "a b-c123 " ) = "a_b-c123"
+     * DoxiaUtils.encodeId( "   anchor" ) = "anchor"
+     * DoxiaUtils.encodeId( "myAnchor" )  = "myAnchor"
+     * </pre>
+     *
+     * @param id The id to be encoded.
+     * @param chop true if non-ASCII characters should be ignored.
+     * If false, any non-ASCII characters will be replaced as specified above.
+     * @return The trimmed and encoded id, or null if id is null.
+     * If id is not null, the return value is guaranteed to be a valid Doxia id.
+     *
+     * @since 1.1.1
+     */
+    public static String encodeId( String id, boolean chop )
+    {
+        if ( id == null )
+        {
+            return null;
+        }
+
+        id = id.trim();
+        int length = id.length();
+
+        if ( length == 0 )
+        {
+            return "a";
+        }
+
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = id.charAt( i );
+
+            if ( ( i == 0 ) && ( !isAsciiLetter( c ) ) )
+            {
+                buffer.append( 'a' );
+            }
+
+            if ( c == ' ' )
+            {
+                buffer.append( '_' );
+            }
+            else if ( isAsciiLetter( c ) || isAsciiDigit( c ) || ( c == '-' ) || ( c == '_' ) || ( c == ':' )
+                            || ( c == '.' ) )
+            {
+                buffer.append( c );
+            }
+            else if ( !chop )
+            {
+                byte[] bytes;
+
+                try
+                {
+                    bytes = String.valueOf( c ).getBytes( "UTF8" );
+                }
+                catch ( UnsupportedEncodingException cannotHappen )
+                {
+                    bytes = new byte[0];
+                }
+
+                for ( int j = 0; j < bytes.length; ++j )
+                {
+                    String hex = byteToHex( bytes[j] );
+
+                    buffer.append( '%' );
+
+                    if ( hex.length() == 1 )
+                    {
+                        buffer.append( '0' );
+                    }
+
+                    buffer.append( hex );
+                }
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * Convert a byte to it's hexadecimal equivalent.
+     *
+     * @param b the byte value.
+     * @return the result of Integer.toHexString( b & 0xFF ).
+     *
+     * @since 1.1.1
+     */
+    public static String byteToHex( byte b )
+    {
+        return Integer.toHexString( b & MINUS_ONE );
+    }
+
+    /**
+     * Determines if the specified text is a valid id according to the rules
+     * laid out in {@link #encodeId(String)}.
+     *
+     * @param text The text to be tested.
+     * @return <code>true</code> if the text is a valid id, otherwise <code>false</code>.
+     * @see #encodeId(String)
+     */
+    public static boolean isValidId( String text )
+    {
+        if ( text == null || text.length() == 0 )
+        {
+            return false;
+        }
+
+        for ( int i = 0; i < text.length(); ++i )
+        {
+            char c = text.charAt( i );
+
+            if ( isAsciiLetter( c ) )
+            {
+                continue;
+            }
+
+            if ( ( i == 0 ) || ( c == ' ' ) || ( !isAsciiDigit( c ) && c != '-' && c != '_' && c != ':' && c != '.' ) )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static final SimpleDateFormat DATE_PARSER = new SimpleDateFormat( "", Locale.ENGLISH );
+    private static final ParsePosition DATE_PARSE_POSITION = new ParsePosition( 0 );
+    private static final String[] DATE_PATTERNS = new String[]
+    {
+        "yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd", "yyyy", "dd.MM.yyyy", "dd MMM yyyy",
+        "dd MMM. yyyy", "MMMM yyyy", "MMM. dd, yyyy", "MMM. yyyy", "MMMM dd, yyyy",
+        "MMM d, ''yy", "MMM. ''yy", "MMMM ''yy"
+    };
+
+    /**
+     * <p>Parses a string representing a date by trying different date patterns.</p>
+     *
+     * <p>The following date patterns are tried (in the given order):</p>
+     *
+     * <pre>"yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd", "yyyy", "dd.MM.yyyy", "dd MMM yyyy",
+     *  "dd MMM. yyyy", "MMMM yyyy", "MMM. dd, yyyy", "MMM. yyyy", "MMMM dd, yyyy",
+     *  "MMM d, ''yy", "MMM. ''yy", "MMMM ''yy"</pre>
+     *
+     * <p>A parse is only sucessful if it parses the whole of the input string.
+     * If no parse patterns match, a ParseException is thrown.</p>
+     *
+     * <p>As a special case, the strings <code>"today"</code> and <code>"now"</code>
+     * (ignoring case) return the current date.</p>
+     *
+     * @param str the date to parse, not null.
+     * @return the parsed date, or the current date if the input String (ignoring case) was
+     *      <code>"today"</code> or <code>"now"</code>.
+     * @throws ParseException if no pattern matches.
+     *
+     * @since 1.1.1.
+     */
+    public static Date parseDate( String str )
+            throws ParseException
+    {
+        if ( "today".equals( str.toLowerCase( Locale.ENGLISH ) )
+                || "now".equals( str.toLowerCase( Locale.ENGLISH ) ) )
+        {
+            return new Date();
+        }
+
+        for ( int i = 0; i < DATE_PATTERNS.length; i++ )
+        {
+            DATE_PARSER.applyPattern( DATE_PATTERNS[i] );
+            DATE_PARSE_POSITION.setIndex( 0 );
+            final Date date = DATE_PARSER.parse( str, DATE_PARSE_POSITION );
+
+            if ( date != null && DATE_PARSE_POSITION.getIndex() == str.length() )
+            {
+                return date;
+            }
+        }
+
+        throw new ParseException( "Unable to parse date: " + str, -1 );
+    }
+
+      //
+     // private
+    //
+
+    private static boolean isAsciiLetter( char c )
+    {
+        return ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) );
+    }
+
+    private static boolean isAsciiDigit( char c )
+    {
+        return ( c >= '0' && c <= '9' );
+    }
+
+    /**
+     * Determine width and height of an image. If successful, the returned SinkEventAttributes
+     * contain width and height attribute keys whose values are the width and height of the image (as a String).
+     *
+     * @param logo a String containing either a URL or a path to an image file.
+     * @return a set of SinkEventAttributes, or null if no ImageReader was found to read the image.
+     * @throws java.io.IOException if an error occurs during reading.
+     * @since 1.1.1
+     */
+    public static MutableAttributeSet getImageAttributes( String logo )
+            throws IOException
+    {
+        BufferedImage img = null;
+
+        if ( isExternalLink( logo ) )
+        {
+            img = ImageIO.read( new URL( logo ) );
+        }
+        else
+        {
+            img = ImageIO.read( new File( logo ) );
+        }
+
+        if ( img == null )
+        {
+            return null;
+        }
+
+        MutableAttributeSet atts = new SinkEventAttributeSet();
+        atts.addAttribute( SinkEventAttributeSet.WIDTH, Integer.toString( img.getWidth() ) );
+        atts.addAttribute( SinkEventAttributeSet.HEIGHT, Integer.toString( img.getHeight() ) );
+        // add other attributes?
+
+        return atts;
+    }
+
+    private DoxiaUtils()
+    {
+        // utility class
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/util/HtmlTools.java b/doxia-core/src/main/java/org/apache/maven/doxia/util/HtmlTools.java
new file mode 100644
index 0000000..cb3d93d
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/util/HtmlTools.java
@@ -0,0 +1,485 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.maven.doxia.markup.HtmlMarkup;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * The <code>HtmlTools</code> class defines methods to HTML handling.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: HtmlTools.java 785531 2009-06-17 09:47:59Z ltheussl $
+ * @since 1.0
+ */
+public class HtmlTools
+{
+    private static final Tag[] ALL_TAGS  =
+    {
+        HtmlMarkup.A, HtmlMarkup.ABBR, HtmlMarkup.ACRONYM, HtmlMarkup.ADDRESS, HtmlMarkup.APPLET,
+        HtmlMarkup.AREA, HtmlMarkup.B, HtmlMarkup.BASE, HtmlMarkup.BASEFONT, HtmlMarkup.BDO,
+        HtmlMarkup.BIG, HtmlMarkup.BLOCKQUOTE, HtmlMarkup.BODY, HtmlMarkup.BR, HtmlMarkup.BUTTON,
+        HtmlMarkup.CAPTION, HtmlMarkup.CENTER, HtmlMarkup.CITE, HtmlMarkup.CODE, HtmlMarkup.COL,
+        HtmlMarkup.COLGROUP, HtmlMarkup.DD, HtmlMarkup.DEL, HtmlMarkup.DFN, HtmlMarkup.DIR,
+        HtmlMarkup.DIV, HtmlMarkup.DL, HtmlMarkup.DT, HtmlMarkup.EM, HtmlMarkup.FIELDSET,
+        HtmlMarkup.FONT, HtmlMarkup.FORM, HtmlMarkup.FRAME, HtmlMarkup.FRAMESET, HtmlMarkup.H1,
+        HtmlMarkup.H2, HtmlMarkup.H3, HtmlMarkup.H4, HtmlMarkup.H5, HtmlMarkup.H6, HtmlMarkup.HEAD,
+        HtmlMarkup.HR, HtmlMarkup.HTML, HtmlMarkup.I, HtmlMarkup.IFRAME, HtmlMarkup.IMG,
+        HtmlMarkup.INPUT, HtmlMarkup.INS, HtmlMarkup.ISINDEX, HtmlMarkup.KBD, HtmlMarkup.LABEL,
+        HtmlMarkup.LEGEND, HtmlMarkup.LI, HtmlMarkup.LINK, HtmlMarkup.MAP, HtmlMarkup.MENU,
+        HtmlMarkup.META, HtmlMarkup.NOFRAMES, HtmlMarkup.NOSCRIPT, HtmlMarkup.OBJECT, HtmlMarkup.OL,
+        HtmlMarkup.OPTGROUP, HtmlMarkup.OPTION, HtmlMarkup.P, HtmlMarkup.PARAM, HtmlMarkup.PRE,
+        HtmlMarkup.Q, HtmlMarkup.S, HtmlMarkup.SAMP, HtmlMarkup.SCRIPT, HtmlMarkup.SELECT,
+        HtmlMarkup.SMALL, HtmlMarkup.SPAN, HtmlMarkup.STRIKE, HtmlMarkup.STRONG, HtmlMarkup.STYLE,
+        HtmlMarkup.SUB, HtmlMarkup.SUP, HtmlMarkup.TABLE, HtmlMarkup.TBODY, HtmlMarkup.TD,
+        HtmlMarkup.TEXTAREA, HtmlMarkup.TFOOT, HtmlMarkup.TH, HtmlMarkup.THEAD, HtmlMarkup.TITLE,
+        HtmlMarkup.TR, HtmlMarkup.TT, HtmlMarkup.U, HtmlMarkup.UL, HtmlMarkup.VAR
+    };
+
+    private static final Hashtable TAG_HASHTABLE = new Hashtable( ALL_TAGS.length );
+
+    private static final int ASCII = 0x7E;
+
+    static
+    {
+        for ( int i = 0; i < ALL_TAGS.length; i++ )
+        {
+            TAG_HASHTABLE.put( ALL_TAGS[i].toString(), ALL_TAGS[i] );
+        }
+    }
+
+    /**
+     * Returns a tag for a defined HTML tag name. This is one of
+     * the tags defined in {@link org.apache.maven.doxia.markup.HtmlMarkup}.
+     * If the given name does not represent one of the defined tags, then
+     * <code>null</code> will be returned.
+     *
+     * @param tagName the <code>String</code> name requested.
+     * @return a tag constant corresponding to the <code>tagName</code>,
+     *    or <code>null</code> if not found.
+     * @see <a href="http://www.w3.org/TR/html401/index/elements.html">http://www.w3.org/TR/html401/index/elements.html</a>
+     * @since 1.1
+     */
+    public static Tag getHtmlTag( String tagName )
+    {
+        Object t =  TAG_HASHTABLE.get( tagName );
+
+        return ( t == null ? null : (Tag) t );
+    }
+
+    /**
+     * Escape special HTML characters in a String in <code>xml</code> mode.
+     *
+     * <b>Note</b>: this method doesn't escape non-ascii characters by numeric characters references.
+     *
+     * @param text the String to escape, may be null.
+     * @return The escaped text or the empty string if text == null.
+     * @see #escapeHTML(String,boolean)
+     */
+    public static String escapeHTML( String text )
+    {
+        return escapeHTML( text, true );
+    }
+
+    /**
+     * Escape special HTML characters in a String.
+     *
+     * <pre>
+     * < becomes <code>&lt;</code>
+     * > becomes <code>&gt;</code>
+     * & becomes <code>&amp;</code>
+     * " becomes <code>&quot;</code>
+     * ' becomes <code>&apos;</code> if xmlMode = true
+     * </pre>
+     *
+     * If <code>xmlMode</code> is true, every other character than the above remains unchanged,
+     * if <code>xmlMode</code> is false, non-ascii characters get replaced by their hex code.
+     *
+     * <b>Note</b>: all characters are encoded, i.e.:
+     * <pre>
+     * \u0159       = &#x159;
+     * \uD835\uDFED = &#x1d7ed;
+     * </pre>
+     *
+     * @param text The String to escape, may be null.
+     * @param xmlMode <code>true</code> to replace also ' to &apos, <code>false</code> to replace non-ascii
+     * characters by numeric characters references.
+     * @return The escaped text or the empty string if text == null.
+     * @since 1.1
+     * @see <a href="http://www.w3.org/TR/2000/REC-xml-20001006#sec-predefined-ent">http://www.w3.org/TR/2000/REC-xml-20001006#sec-predefined-ent</a>
+     * @see <a href="http://www.w3.org/TR/html401/charset.html#h-5.3">http://www.w3.org/TR/html401/charset.html#h-5.3</a>
+     */
+    public static final String escapeHTML( String text, boolean xmlMode )
+    {
+        if ( text == null )
+        {
+            return "";
+        }
+
+        int length = text.length();
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = text.charAt( i );
+            switch ( c )
+            {
+                case '<':
+                    buffer.append( "<" );
+                    break;
+                case '>':
+                    buffer.append( ">" );
+                    break;
+                case '&':
+                    buffer.append( "&" );
+                    break;
+                case '\"':
+                    buffer.append( """ );
+                    break;
+                default:
+                    if ( xmlMode )
+                    {
+                        if ( c == '\'' )
+                        {
+                            buffer.append( "'" );
+                        }
+                        else
+                        {
+                            buffer.append( c );
+                        }
+                    }
+                    else
+                    {
+                        if ( c <= ASCII )
+                        {
+                            // ASCII.
+                            buffer.append( c );
+                        }
+                        else
+                        {
+                            buffer.append( "&#x" );
+                            if ( isHighSurrogate( c ) )
+                            {
+                                buffer.append( Integer.toHexString( toCodePoint( c, text.charAt( ++i ) ) ) );
+                            }
+                            else
+                            {
+                                buffer.append( Integer.toHexString( c ) );
+                            }
+                            buffer.append( ';' );
+                        }
+                    }
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * Unescapes HTML entities in a string in non xml mode.
+     *
+     * @param text the <code>String</code> to unescape, may be null.
+     * @return a new unescaped <code>String</code>, <code>null</code> if null string input.
+     * @since 1.1.1.
+     * @see #unescapeHTML(String, boolean)
+     */
+    public static String unescapeHTML( String text )
+    {
+        return unescapeHTML( text, false );
+    }
+
+    /**
+     * Unescapes HTML entities in a string.
+     *
+     * <p> Unescapes a string containing entity escapes to a string
+     * containing the actual Unicode characters corresponding to the
+     * escapes. Supports HTML 4.0 entities.</p>
+     *
+     * <p>For example, the string "&lt;Fran&ccedil;ais&gt;"
+     * will become "<Français>".</p>
+     *
+     * <b>Note</b>: all unicode entities are decoded, i.e.:
+     * <pre>
+     * &#x159;   = \u0159
+     * &#x1d7ed; = \uD835\uDFED
+     * </pre>
+     *
+     * @param text the <code>String</code> to unescape, may be null.
+     * @param xmlMode set to <code>true</code> to replace &apos by '.
+     * @return a new unescaped <code>String</code>, <code>null</code> if null string input.
+     * @since 1.1.1.
+     */
+    public static String unescapeHTML( String text, boolean xmlMode )
+    {
+        if ( text == null )
+        {
+            return null;
+        }
+
+        String unescaped;
+        if ( xmlMode )
+        {
+            unescaped = StringEscapeUtils.unescapeXml( text );
+        }
+        else
+        {
+            // StringEscapeUtils.unescapeHtml returns entities it doesn't recognize unchanged
+            unescaped = StringEscapeUtils.unescapeHtml( text );
+        }
+
+        String tmp = unescaped;
+        List entities = new ArrayList();
+        while ( true )
+        {
+            int i = tmp.indexOf( "&#x" );
+            if ( i == -1 )
+            {
+                break;
+            }
+
+            tmp = tmp.substring( i + 3 );
+            if ( tmp.indexOf( ';' ) != -1 )
+            {
+                String entity = tmp.substring( 0, tmp.indexOf( ';' ) );
+                try
+                {
+                    Integer.parseInt( entity, 16 );
+                    entities.add( entity );
+                }
+                catch ( NumberFormatException e )
+                {
+                    // nop
+                }
+            }
+        }
+
+        for ( int i = 0; i < entities.size(); i++ )
+        {
+            String entity = (String) entities.get( i );
+
+            int codePoint = Integer.parseInt( entity, 16 );
+            unescaped = StringUtils.replace( unescaped, "&#x" + entity + ";", new String( toChars( codePoint ) ) );
+        }
+
+        return unescaped;
+    }
+
+    /**
+     * Encode an url
+     *
+     * @param url the String to encode, may be null
+     * @return the text encoded, null if null String input
+     */
+    public static String encodeURL( String url )
+    {
+        if ( url == null )
+        {
+            return null;
+        }
+
+        StringBuffer encoded = new StringBuffer();
+        int length = url.length();
+
+        char[] unicode = new char[1];
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = url.charAt( i );
+
+            switch ( c )
+            {
+                case ';':
+                case '/':
+                case '?':
+                case ':':
+                case '@':
+                case '&':
+                case '=':
+                case '+':
+                case '$':
+                case ',':
+                case '[':
+                case ']': // RFC 2732 (IPV6)
+                case '-':
+                case '_':
+                case '.':
+                case '!':
+                case '~':
+                case '*':
+                case '\'':
+                case '(':
+                case ')':
+                case '#': // XLink mark
+                    encoded.append( c );
+                    break;
+                default:
+                    if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || ( c >= '0' && c <= '9' ) )
+                    {
+                        encoded.append( c );
+                    }
+                    else
+                    {
+                        byte[] bytes;
+
+                        try
+                        {
+                            if ( isHighSurrogate( c ) )
+                            {
+                                int codePoint = toCodePoint( c, url.charAt( ++i ) );
+                                unicode = toChars( codePoint );
+                                bytes = ( new String( unicode, 0, unicode.length ) ).getBytes( "UTF8" );
+                            }
+                            else
+                            {
+                                unicode[0] = c;
+                                bytes = ( new String( unicode, 0, 1 ) ).getBytes( "UTF8" );
+                            }
+                        }
+                        catch ( UnsupportedEncodingException cannotHappen )
+                        {
+                            bytes = new byte[0];
+                        }
+
+                        for ( int j = 0; j < bytes.length; ++j )
+                        {
+                            String hex = DoxiaUtils.byteToHex( bytes[j] );
+
+                            encoded.append( '%' );
+                            if ( hex.length() == 1 )
+                            {
+                                encoded.append( '0' );
+                            }
+                            encoded.append( hex );
+                        }
+                    }
+            }
+        }
+
+        return encoded.toString();
+    }
+
+    /**
+     * Construct a valid id.
+     *
+     * <p>
+     *   <b>Note</b>: this method is identical to
+     *   {@link DoxiaUtils#encodeId(String,boolean) DoxiaUtils.encodeId( id, true)},
+     *   the rules to encode an id are laid out there.
+     * </p>
+     *
+     * @param id The id to be encoded.
+     * @return The trimmed and encoded id, or null if id is null.
+     * @see DoxiaUtils#encodeId(java.lang.String,boolean)
+     */
+    public static String encodeId( String id )
+    {
+        return DoxiaUtils.encodeId( id, true );
+    }
+
+    /**
+     * Determines if the specified text is a valid id according to the rules
+     * laid out in {@link #encodeId(String)}.
+     *
+     * @param text The text to be tested.
+     * @return <code>true</code> if the text is a valid id, otherwise <code>false</code>.
+     * @see #encodeId(String).
+     */
+    public static boolean isId( String text )
+    {
+        return DoxiaUtils.isValidId( text );
+    }
+
+    private HtmlTools()
+    {
+        // utility class
+    }
+
+//
+// Imported code from ASF Harmony project rev 770909
+// http://svn.apache.org/repos/asf/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/Character.java
+//
+
+    private static final char LUNATE_SIGMA = 0x3FF;
+    private static final char NON_PRIVATE_USE_HIGH_SURROGATE = 0xD800;
+    private static final char LOW_SURROGATE = 0xDC00;
+
+    private static int toCodePoint( char high, char low )
+    {
+        // See RFC 2781, Section 2.2
+        // http://www.faqs.org/rfcs/rfc2781.html
+        int h = ( high & LUNATE_SIGMA ) << 10;
+        int l = low & LUNATE_SIGMA;
+        return ( h | l ) + MIN_SUPPLEMENTARY_CODE_POINT;
+    }
+
+    private static final char MIN_HIGH_SURROGATE = '\uD800';
+    private static final char MAX_HIGH_SURROGATE = '\uDBFF';
+
+    private static boolean isHighSurrogate( char ch )
+    {
+        return ( MIN_HIGH_SURROGATE <= ch && MAX_HIGH_SURROGATE >= ch );
+    }
+
+    private static final int MIN_CODE_POINT = 0x000000;
+    private static final int MAX_CODE_POINT = 0x10FFFF;
+    private static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x10000;
+
+    private static boolean isValidCodePoint( int codePoint )
+    {
+        return ( MIN_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint );
+    }
+
+    private static boolean isSupplementaryCodePoint( int codePoint )
+    {
+        return ( MIN_SUPPLEMENTARY_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint );
+    }
+
+    /**
+     * Converts the given code point to an equivalent character array.
+     *
+     * @param codePoint the code point to convert.
+     * @return If codePoint is a supplementary code point, returns a character array of length 2,
+     * otherwise a character array of length 1 containing only the original int as a char.
+     */
+    public static char[] toChars( int codePoint )
+    {
+        if ( !isValidCodePoint( codePoint ) )
+        {
+            throw new IllegalArgumentException();
+        }
+
+        if ( isSupplementaryCodePoint( codePoint ) )
+        {
+            int cpPrime = codePoint - MIN_SUPPLEMENTARY_CODE_POINT;
+            int high = NON_PRIVATE_USE_HIGH_SURROGATE | ( ( cpPrime >> 10 ) & LUNATE_SIGMA );
+            int low = LOW_SURROGATE | ( cpPrime & LUNATE_SIGMA );
+            return new char[] { (char) high, (char) low };
+        }
+        return new char[] { (char) codePoint };
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/util/LineBreaker.java b/doxia-core/src/main/java/org/apache/maven/doxia/util/LineBreaker.java
new file mode 100644
index 0000000..f315a8a
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/util/LineBreaker.java
@@ -0,0 +1,216 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.codehaus.plexus.util.IOUtil;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Allows to specify the line-length of an output writer.
+ *
+ * @version $Id: LineBreaker.java 746978 2009-02-23 12:20:33Z vsiveton $
+ */
+public class LineBreaker
+{
+    /** The default maximal line length. */
+    public static final int DEFAULT_MAX_LINE_LENGTH = 78;
+
+    /** The system dependent EOL. */
+    private static final String EOL = System.getProperty( "line.separator" );
+
+    /** The destination writer. */
+    private Writer destination;
+
+    /** The writer to use. */
+    private BufferedWriter writer;
+
+    /** The maximal line length. */
+    private int maxLineLength;
+
+    /** The current line length. */
+    private int lineLength = 0;
+
+    /** The string buffer to store the current text. */
+    private StringBuffer word = new StringBuffer( 1024 );
+
+    /**
+     * Constructs a new LineBreaker with DEFAULT_MAX_LINE_LENGTH.
+     *
+     * @param out The writer to use.
+     */
+    public LineBreaker( Writer out )
+    {
+        this( out, DEFAULT_MAX_LINE_LENGTH );
+    }
+
+    /**
+     * Constructs a new LineBreaker with the given max line length.
+     *
+     * @param out The writer to use.
+     * @param max The maximal line length.
+     */
+    public LineBreaker( Writer out, int max )
+    {
+        if ( max <= 0 )
+        {
+            throw new IllegalArgumentException( "maxLineLength <= 0" );
+        }
+
+        destination = out;
+        this.maxLineLength = max;
+        writer = new BufferedWriter( out );
+    }
+
+    /**
+     * Returns the current destination writer.
+     *
+     * @return The destination.
+     */
+    public Writer getDestination()
+    {
+        return destination;
+    }
+
+    /**
+     * Writes the given text to the writer. White space is not preserved.
+     *
+     * @param text The text to write.
+     * @throws java.io.IOException if there's a problem writing the text.
+     */
+    public void write( String text )
+        throws IOException
+    {
+        write( text, /*preserveSpace*/false );
+    }
+
+    /**
+     * Writes the given text to the writer.
+     *
+     * @param text The text to write.
+     * @param preserveSpace True to preserve white space.
+     */
+    public void write( String text, boolean preserveSpace )
+    {
+        int length = text.length();
+
+        try
+        {
+            for ( int i = 0; i < length; ++i )
+            {
+                char c = text.charAt( i );
+
+                switch ( c )
+                {
+                    case ' ':
+                        if ( preserveSpace )
+                        {
+                            word.append( c );
+                        }
+                        else
+                        {
+                            writeWord();
+                        }
+                        break;
+
+                    case '\r':
+                        // if \r\n (windows) then just pass along \n
+                        if ( i + 1 < length && text.charAt( i + 1 ) == '\n' )
+                        {
+                            break;
+                        }
+
+                    case '\n':
+                        writeWord();
+                        writer.write( EOL );
+                        lineLength = 0;
+                        break;
+
+                    default:
+                        word.append( c );
+                }
+
+            }
+        }
+        catch ( Exception e )
+        {
+            // TODO: log
+        }
+    }
+
+    /**
+     * Write out the current StringBuffer and flush the writer.
+     * Any IOException will be swallowed.
+     */
+    public void flush()
+    {
+        try
+        {
+            writeWord();
+            writer.flush();
+        }
+        catch ( IOException e )
+        {
+            // TODO: log
+        }
+    }
+
+    /**
+     * Writes the current StringBuffer to the writer.
+     *
+     * @throws IOException if an exception occurs during writing.
+     */
+    private void writeWord()
+        throws IOException
+    {
+        int length = word.length();
+        if ( length > 0 )
+        {
+            if ( lineLength > 0 )
+            {
+                if ( lineLength + 1 + length > maxLineLength )
+                {
+                    writer.write( EOL );
+                    lineLength = 0;
+                }
+                else
+                {
+                    writer.write( ' ' );
+                    ++lineLength;
+                }
+            }
+
+            writer.write( word.toString() );
+            word.setLength( 0 );
+
+            lineLength += length;
+        }
+    }
+
+    /**
+     * Close the writer.
+     */
+    public void close()
+    {
+        IOUtil.close( writer );
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/util/XmlValidator.java b/doxia-core/src/main/java/org/apache/maven/doxia/util/XmlValidator.java
new file mode 100644
index 0000000..2750290
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/util/XmlValidator.java
@@ -0,0 +1,268 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.XMLConstants;
+
+import org.apache.maven.doxia.logging.Log;
+import org.apache.maven.doxia.markup.XmlMarkup;
+import org.apache.maven.doxia.parser.AbstractXmlParser.CachedFileEntityResolver;
+import org.apache.maven.doxia.parser.ParseException;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * A class to validate xml documents.
+ *
+ * @version $Id$
+ * @since 1.1.3
+ */
+public class XmlValidator
+{
+    /**
+     * Doctype pattern i.e. ".*<!DOCTYPE([^>]*)>.*"
+     * see <a href="http://www.w3.org/TR/REC-xml/#NT-doctypedecl">http://www.w3.org/TR/REC-xml/#NT-doctypedecl</a>.
+     */
+    private static final Pattern PATTERN_DOCTYPE = Pattern.compile( ".*" + XmlMarkup.DOCTYPE_START + "([^>]*)>.*" );
+
+    /** Tag pattern as defined in http://www.w3.org/TR/REC-xml/#NT-Name */
+    private static final Pattern PATTERN_TAG = Pattern.compile( ".*<([A-Za-z][A-Za-z0-9:_.-]*)([^>]*)>.*" );
+
+    /** lazy xmlReader to validate xml content*/
+    private XMLReader xmlReader;
+
+    private Log logger;
+
+    /**
+     * Constructor.
+     *
+     * @param log a logger, not null.
+     */
+    public XmlValidator( Log log )
+    {
+        this.logger = log;
+    }
+
+    /**
+     * Validate an XML content with SAX.
+     *
+     * @param content a not null xml content
+     * @throws ParseException if any.
+     */
+    public void validate( String content )
+        throws ParseException
+    {
+        try
+        {
+            // 1 if there's a doctype
+            boolean hasDoctype = false;
+            Matcher matcher = PATTERN_DOCTYPE.matcher( content );
+            if ( matcher.find() )
+            {
+                hasDoctype = true;
+            }
+
+            // 2 check for an xmlns instance
+            boolean hasXsd = false;
+            matcher = PATTERN_TAG.matcher( content );
+            if ( matcher.find() )
+            {
+                String value = matcher.group( 2 );
+
+                if ( value.indexOf( XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI ) != -1 )
+                {
+                    hasXsd = true;
+                }
+            }
+
+            // 3 validate content
+            getLog().debug( "Validating the content..." );
+            getXmlReader( hasXsd && hasDoctype ).parse( new InputSource( new StringReader( content ) ) );
+        }
+        catch ( IOException e )
+        {
+            throw new ParseException( "Error validating the model: " + e.getMessage(), e );
+        }
+        catch ( SAXException e )
+        {
+            throw new ParseException( "Error validating the model: " + e.getMessage(), e );
+        }
+    }
+
+    /**
+     * @param hasDtdAndXsd to flag the <code>ErrorHandler</code>.
+     * @return an xmlReader instance.
+     * @throws SAXException if any
+     */
+    private XMLReader getXmlReader( boolean hasDtdAndXsd )
+        throws SAXException
+    {
+        if ( xmlReader == null )
+        {
+            MessagesErrorHandler errorHandler = new MessagesErrorHandler( getLog() );
+
+            xmlReader = XMLReaderFactory.createXMLReader( "org.apache.xerces.parsers.SAXParser" );
+            xmlReader.setFeature( "http://xml.org/sax/features/validation", true );
+            xmlReader.setFeature( "http://apache.org/xml/features/validation/schema", true );
+            xmlReader.setErrorHandler( errorHandler );
+            xmlReader.setEntityResolver( new CachedFileEntityResolver() );
+        }
+
+        ( (MessagesErrorHandler) xmlReader.getErrorHandler() ).setHasDtdAndXsd( hasDtdAndXsd );
+
+        return xmlReader;
+    }
+
+    private Log getLog()
+    {
+        return logger;
+    }
+
+    /**
+     * Convenience class to beautify <code>SAXParseException</code> messages.
+     */
+    static class MessagesErrorHandler
+        extends DefaultHandler
+    {
+        private static final int TYPE_UNKNOWN = 0;
+
+        private static final int TYPE_WARNING = 1;
+
+        private static final int TYPE_ERROR = 2;
+
+        private static final int TYPE_FATAL = 3;
+
+        private static final String EOL = XmlMarkup.EOL;
+
+        /** @see org/apache/xerces/impl/msg/XMLMessages.properties#MSG_ELEMENT_NOT_DECLARED */
+        private static final Pattern ELEMENT_TYPE_PATTERN =
+            Pattern.compile( "Element type \".*\" must be declared.", Pattern.DOTALL );
+
+        private final Log log;
+
+        private boolean hasDtdAndXsd;
+
+        public MessagesErrorHandler( Log log )
+        {
+            this.log = log;
+        }
+
+        /**
+         * @param hasDtdAndXsd the hasDtdAndXsd to set
+         */
+        protected void setHasDtdAndXsd( boolean hasDtdAndXsd )
+        {
+            this.hasDtdAndXsd = hasDtdAndXsd;
+        }
+
+        /** {@inheritDoc} */
+        public void warning( SAXParseException e )
+            throws SAXException
+        {
+            processException( TYPE_WARNING, e );
+        }
+
+        /** {@inheritDoc} */
+        public void error( SAXParseException e )
+            throws SAXException
+        {
+            // Workaround for Xerces complaints when an XML with XSD needs also a <!DOCTYPE []> to specify entities
+            // like  
+            // See http://xsd.stylusstudio.com/2001Nov/post08021.htm
+            if ( !hasDtdAndXsd )
+            {
+                processException( TYPE_ERROR, e );
+                return;
+            }
+
+            Matcher m = ELEMENT_TYPE_PATTERN.matcher( e.getMessage() );
+            if ( !m.find() )
+            {
+                processException( TYPE_ERROR, e );
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void fatalError( SAXParseException e )
+            throws SAXException
+        {
+            processException( TYPE_FATAL, e );
+        }
+
+        private void processException( int type, SAXParseException e )
+            throws SAXException
+        {
+            StringBuffer message = new StringBuffer();
+
+            switch ( type )
+            {
+                case TYPE_WARNING:
+                    message.append( "Warning:" );
+                    break;
+
+                case TYPE_ERROR:
+                    message.append( "Error:" );
+                    break;
+
+                case TYPE_FATAL:
+                    message.append( "Fatal error:" );
+                    break;
+
+                case TYPE_UNKNOWN:
+                default:
+                    message.append( "Unknown:" );
+                    break;
+            }
+
+            message.append( EOL );
+            message.append( "  Public ID: " + e.getPublicId() ).append( EOL );
+            message.append( "  System ID: " + e.getSystemId() ).append( EOL );
+            message.append( "  Line number: " + e.getLineNumber() ).append( EOL );
+            message.append( "  Column number: " + e.getColumnNumber() ).append( EOL );
+            message.append( "  Message: " + e.getMessage() ).append( EOL );
+
+            final String logMessage = message.toString();
+
+            switch ( type )
+            {
+                case TYPE_WARNING:
+                    log.warn( logMessage );
+                    break;
+
+                case TYPE_UNKNOWN:
+                case TYPE_ERROR:
+                case TYPE_FATAL:
+                default:
+                    throw new SAXException( logMessage );
+            }
+        }
+    }
+}
diff --git a/doxia-core/src/main/javadoc/org/apache/maven/doxia/package.html b/doxia-core/src/main/javadoc/org/apache/maven/doxia/package.html
new file mode 100644
index 0000000..cedaf5b
--- /dev/null
+++ b/doxia-core/src/main/javadoc/org/apache/maven/doxia/package.html
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<body>
+  <h1>Maven Doxia API.</h1>
+  <p>The Doxia API is based on <i>Sinks</i> and <i>Parser</i> objects: its goal is to parse a given source model
+     using a given parser, and emits Doxia events into the given sink.</p>
+
+  <h2>Using Maven Doxia API</h2>
+  <p>The following snippet shows how to use the Doxia API:</p>
+
+  <div align="left" class="java">
+    <table border="0" cellpadding="3" cellspacing="0" bgcolor="#ffffff">
+      <tr>
+        <!-- start source code -->
+        <td nowrap="nowrap" valign="top" align="left">
+          <code>
+            <font color="#ffffff">  </font><font color="#000000">File userDir = </font><font color="#7f0055"><b>new </b></font><font color="#000000">File</font><font color="#000000">( </font><font color="#000000">System.getProperty </font><font color="#000000">( </font><font color="#2a00ff">"user.dir" </font><font color="#000000">) )</font><font color="#000000">;</font><br />
+            <font color="#ffffff">  </font><font color="#000000">File inputFile = </font><font color="#7f0055"><b>new </b></font><font color="#000000">File</font><font color="#000000">( </font><font color="#000000">userDir, </font><font color="#2a00ff">"test.apt" </font><font color="#000000">)</font><font color="#000000">;</font><br />
+            <font color="#ffffff">  </font><font color="#000000">File outputFile = </font><font color="#7f0055"><b>new </b></font><font color="#000000">File</font><font color="#000000">( </font><font color="#000000">userDir, </font><font color="#2a00ff">"test.html" </font><font color="#000000">)</font><font color="#000000">;</font><br />
+            <font color="#ffffff"></font><br />
+            <font color="#ffffff">  </font><font color="#000000">Reader source = ReaderFactory.newReader</font><font color="#000000">( </font><font color="#000000">inputFile, </font><font color="#2a00ff">"UTF-8" </font><font color="#000000">)</font><font color="#000000">;</font><br />
+            <font color="#ffffff"></font><br />
+            <font color="#ffffff">  </font><font color="#000000">SinkFactory sinkFactory = </font><font color="#000000">(</font><font color="#000000">SinkFactory</font><font color="#000000">) </font><font color="#000000">lookup</font><font color="#000000">( </font><font color="#000000">SinkFactory.ROLE, </font><font color="#2a00ff">"html" </font><font color="#000000">)</font><font color="#000000">; </font><font color="#3f7f5f">//& [...]
+            <font color="#ffffff">  </font><font color="#000000">Sink sink = sinkFactory.createSink</font><font color="#000000">( </font><font color="#000000">outputFile.getParentFile</font><font color="#000000">()</font><font color="#000000">, outputFile.getName</font><font color="#000000">() ) )</font><font color="#000000">;</font><br />
+            <font color="#ffffff"></font><br />
+            <font color="#ffffff">  </font><font color="#000000">Doxia doxia = </font><font color="#000000">(</font><font color="#000000">Doxia</font><font color="#000000">) </font><font color="#000000">lookup</font><font color="#000000">( </font><font color="#000000">Doxia.ROLE </font><font color="#000000">)</font><font color="#000000">; </font><font color="#3f7f5f">// Plexus lookup</font><br />
+            <font color="#ffffff"></font><br />
+            <font color="#ffffff">  </font><font color="#000000">doxia.parse</font><font color="#000000">( </font><font color="#000000">source, </font><font color="#2a00ff">"apt"</font><font color="#000000">, sink </font><font color="#000000">)</font><font color="#000000">;</font>
+          </code>
+        </td>
+        <!-- end source code -->
+      </tr>
+    </table>
+  </div>
+
+  <h2>Resources</h2>
+  <ul>
+    <li><a href="http://maven.apache.org/doxia/">Maven Doxia Website</a></li>
+    <li><a href="http://plexus.codehaus.org/">Plexus</a></li>
+  </ul>
+</body>
diff --git a/doxia-core/src/main/mdo/document.mdo b/doxia-core/src/main/mdo/document.mdo
new file mode 100644
index 0000000..a338c49
--- /dev/null
+++ b/doxia-core/src/main/mdo/document.mdo
@@ -0,0 +1,1268 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<model xmlns="http://modello.codehaus.org/MODELLO/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://modello.codehaus.org/MODELLO/1.0.0 http://modello.codehaus.org/xsd/modello-1.0.0.xsd"
+  xml.namespace="http://maven.apache.org/DOCUMENT/${version}">
+  <id>document</id>
+  <name>Document</name>
+  <description><![CDATA[
+    <p>This is a reference for the Document model used in Doxia.</p>
+    <p>An XSD is available at:</p>
+    <ul>
+      <li><a href="http://maven.apache.org/xsd/document-1.0.1.xsd">http://maven.apache.org/xsd/document-1.0.1.xsd</a>.</li>
+    </ul>
+  ]]></description>
+  <defaults>
+    <default>
+      <key>package</key>
+      <value>org.apache.maven.doxia.document</value>
+    </default>
+  </defaults>
+
+  <classes>
+    <class rootElement="true" xml.tagName="document">
+      <name>DocumentModel</name>
+      <description>Describes the overall document model.</description>
+      <version>1.0.0+</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>outputName</name>
+          <description><![CDATA[
+            The name of the generated document, without extension.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>meta</name>
+          <description><![CDATA[
+            The Meta information properties.
+          ]]></description>
+          <version>1.0.0+</version>
+          <association>
+            <type>DocumentMeta</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>toc</name>
+          <description><![CDATA[
+            The TOC (Table of Contents) information properties.
+          ]]></description>
+          <version>1.0.0+</version>
+          <association>
+            <type>DocumentTOC</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>cover</name>
+          <description><![CDATA[
+            The meta data to construct a cover page for the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <association>
+            <type>DocumentCover</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+
+    <class>
+      <name>DocumentMeta</name>
+      <description><![CDATA[
+        <p>
+          Metadata is general information about a document.
+        </p>
+
+        <p>
+          The metadata elements used here were mostly inspired by the
+          <a href="http://docs.oasis-open.org/office/v1.1/">Open Document Format Specification v. 1.1</a>,
+          which in turn borrows heavily upon the metadata standards developed by the
+          <a href="http://www.dublincore.org">Dublin Core Metadata Initiative</a>.
+        </p>
+      ]]></description>
+      <version>1.0.0+</version>
+      <fields>
+        <field>
+          <name>title</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The title of the document.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>author</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The unique author of the document, usually as a String of "firstName lastName". For
+            more authors, you could use the <authors/> tag.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>authors</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The authors of the document. The names of the entities that
+            are primarily responsible for the content of the document.
+          ]]></description>
+          <association xml.itemsStyle="wrapped" xml.tagName="author">
+            <type>DocumentAuthor</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>subject</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The subject of the document.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>keywords</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The keywords for the document, usually as a String of comma separated keywords.
+            @deprecated use the <keyWords/> tag instead of.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>keyWords</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            A keyword pertaining to the document. The metadata can contain any
+            number of keyword elements, each element specifying one keyword.
+          ]]></description>
+          <association xml.itemsStyle="wrapped" xml.tagName="keyWord">
+            <type>String</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>pageSize</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The pagesize of the document.
+            At least "US", "USLetter" and "A4" should be supported.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>generator</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+             A string that identifies the application or tool that was
+             used to create or last modify the document.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>description</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+             A brief description of the document.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>initialCreator</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The name of the person who created the document initially.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>creator</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The name of the person who last modified the document.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>printedBy</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The name of the person who last printed the document.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>creationDate</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The date and time when the document was created initially.
+            Use the ISO 8601 format "yyyy-MM-dd'T'HH:mm:ss.SSS" in xml.
+          ]]></description>
+          <type>Date</type>
+          <identifier>true</identifier>
+        </field>
+        <field java.getter="false">
+          <name>creationdate</name>
+          <version>1.0.1+</version>
+          <description><![CDATA[
+            The date as String (recommended format is ISO 8601) when the document was created initially.
+            Only used if <code>creationDate</code> is not set.
+            @since 1.1.1
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>date</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The date and time when the document was last modified.
+            Use the ISO 8601 format "yyyy-MM-dd'T'HH:mm:ss.SSS" in xml.
+          ]]></description>
+          <type>Date</type>
+          <identifier>true</identifier>
+        </field>
+        <field java.getter="false">
+          <name>modifydate</name>
+          <version>1.0.1+</version>
+          <description><![CDATA[
+            The date as String (recommended format is ISO 8601) when the document was last modified.
+            Only used if <code>date</code> is not set.
+            @since 1.1.1
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>printDate</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The date and time when the document was last printed.
+            Use the ISO 8601 format "yyyy-MM-dd'T'HH:mm:ss.SSS" in xml.
+          ]]></description>
+          <type>Date</type>
+          <identifier>true</identifier>
+        </field>
+        <field java.getter="false">
+          <name>printdate</name>
+          <version>1.0.1+</version>
+          <description><![CDATA[
+            The date as String (recommended format is ISO 8601) when the document was last printed.
+            Only used if <code>printDate</code> is not set.
+            @since 1.1.1
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>template</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            A template that was used to create the document.
+          ]]></description>
+          <association>
+            <type>DocumentTemplate</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <!--
+        <field>
+          <name>autoReload</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The auto-reload element specifies whether a document is reloaded
+            or replaced by another document after a certain period of time
+            has elapsed.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        -->
+        <field>
+          <name>hyperlinkBehaviour</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The hyperlink-behaviour element specifies the default behavior
+            for hyperlinks in the document.
+          ]]></description>
+          <association>
+            <type>DocumentHyperlinkBehaviour</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>language</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The default language of the document. The language is represented by
+            a two or three letter Language Code taken from the ISO 639 standard,
+            optionally followed by a hyphen (-) and a two-letter Country Code
+            taken from the ISO 3166 standard.
+          ]]></description>
+          <type>String</type>
+          <defaultValue>en-US</defaultValue>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>editingCycles</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The number of editing cycles the document has been through.
+          ]]></description>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>editingDuration</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The total time spent editing the document.
+          ]]></description>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>documentStatistic</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            The statistics of the document, for example, the page count,
+            word count, etc.
+          ]]></description>
+          <association>
+            <type>DocumentStatistic</type>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>confidential</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            whether the content of the document is in some sense confidential.
+          ]]></description>
+          <type>boolean</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>draft</name>
+          <version>1.0.0+</version>
+          <description><![CDATA[
+            whether the content of the document is in some sense preliminary.
+          ]]></description>
+          <type>boolean</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+      <codeSegments>
+        <codeSegment>
+          <code>
+            <![CDATA[
+    /**
+     * @return a comma separated String of all defined keyWords.
+     * @see #getKeywords()
+     * @see #getKeyWords()
+     * @since 1.1.1
+     */
+    public String getAllKeyWords()
+    {
+        StringBuffer sb = new StringBuffer();
+        boolean hasKeywords = false;
+        if ( getKeywords() != null && getKeywords().trim().length() > 0 )
+        {
+            java.util.StringTokenizer st = new java.util.StringTokenizer( getKeywords().trim(), "," );
+            while ( st.hasMoreTokens() )
+            {
+                String s = st.nextToken();
+
+                sb.append( s.trim() );
+
+                if ( st.hasMoreTokens() )
+                {
+                    sb.append( ", " );
+                }
+
+                hasKeywords = true;
+            }
+        }
+        if ( getKeyWords() != null )
+        {
+            for ( java.util.Iterator it = getKeyWords().iterator(); it.hasNext(); )
+            {
+                String keyword = (String) it.next();
+
+                if ( hasKeywords )
+                {
+                    sb.append( ", " );
+                    hasKeywords = false;
+                }
+
+                if ( keyword.trim().length() > 0 )
+                {
+                    sb.append( keyword.trim() );
+
+                    if ( it.hasNext() )
+                    {
+                        sb.append( ", " );
+                    }
+                }
+            }
+        }
+
+        String ret = sb.toString().trim();
+        if ( ret.endsWith( "," ) )
+        {
+            return ret.substring( 0, ret.length() - 1 );
+        }
+
+        return ret;
+    }
+
+    /**
+     * @return {@link #getAuthor()} if the unique author name is defined. Otherwise, return all authors full names
+     * comma separated.
+     * @see #getAuthor()
+     * @see #getAuthors()
+     * @since 1.1.1
+     */
+    public String getAllAuthorNames()
+    {
+        StringBuffer sb = new StringBuffer();
+        if ( getAuthor() != null && getAuthor().trim().length() > 0 )
+        {
+            return getAuthor().trim();
+        }
+
+        if ( getAuthors() != null )
+        {
+            for ( java.util.Iterator it = getAuthors().iterator(); it.hasNext(); )
+            {
+                DocumentAuthor author = (DocumentAuthor) it.next();
+
+                sb.append( author.getFullName().trim() );
+
+                if ( it.hasNext() )
+                {
+                    sb.append( ", " );
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /** ISO 8601 date format, i.e. <code>yyyy-MM-dd</code> **/
+    private static final java.text.DateFormat ISO_8601_FORMAT = new java.text.SimpleDateFormat( "yyyy-MM-dd", java.util.Locale.ENGLISH );
+
+    /**
+     * Get the date and time when the document was created initially.
+     *
+     * @return the <code>getCreationDate()</code> if setted, formatted using ISO-8601 English format, otherwise return
+     * the <code>creationdate</code>.
+     * @since 1.1.1
+     * @see #getCreationDate()
+     */
+    public String getCreationdate()
+    {
+        if ( getCreationDate() != null )
+        {
+            return ISO_8601_FORMAT.format( getCreationDate() );
+        }
+
+        return this.creationdate;
+    }
+
+    /**
+     * Get the date and time when the document was last modified.
+     *
+     * @return the <code>getDate()</code> if setted, formatted using ISO-8601 English format, otherwise return
+     * the <code>modifydate</code>.
+     * @since 1.1.1
+     * @see #getDate()
+     */
+    public String getModifydate()
+    {
+        if ( getDate() != null )
+        {
+            return ISO_8601_FORMAT.format( getDate() );
+        }
+
+        return this.modifydate;
+    }
+
+    /**
+     * Get the date and time when the document was last printed.
+     *
+     * @return the <code>getPrintDate()</code> if setted, formatted using ISO-8601 English format, otherwise return
+     * the <code>printdate</code>.
+     * @since 1.1.1
+     * @see #getPrintDate()
+     */
+    public String getPrintdate()
+    {
+        if ( getPrintDate() != null )
+        {
+            return ISO_8601_FORMAT.format( getPrintDate() );
+        }
+
+        return this.printdate;
+    }
+            ]]>
+          </code>
+        </codeSegment>
+      </codeSegments>
+    </class>
+
+    <class>
+      <name>DocumentAuthor</name>
+      <description>An author of the document.</description>
+      <version>1.0.0+</version>
+      <fields>
+        <field>
+          <name>firstName</name>
+          <description><![CDATA[
+             The first name of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>lastName</name>
+          <description><![CDATA[
+            The last name of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>name</name>
+          <description><![CDATA[
+            The full name of the author, usually as a String of "firstName lastName".
+            @since 1.1.1
+          ]]></description>
+          <version>1.0.1+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>initials</name>
+          <description><![CDATA[
+             The initials of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>title</name>
+          <description><![CDATA[
+             The title of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>position</name>
+          <description><![CDATA[
+             The position of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>email</name>
+          <description><![CDATA[
+            The email address of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>phoneNumber</name>
+          <description><![CDATA[
+            The telephone number of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>faxNumber</name>
+          <description><![CDATA[
+            The fax number of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>companyName</name>
+          <description><![CDATA[
+            The name of the company that employs the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>street</name>
+          <description><![CDATA[
+             The street name of the address of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>city</name>
+          <description><![CDATA[
+            The city name of the address of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>postalCode</name>
+          <description><![CDATA[
+            The postal code of the address of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>country</name>
+          <description><![CDATA[
+            The country of the address of the author.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>state</name>
+          <description><![CDATA[
+            The state or province of the address of the author, if applicable.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+      <codeSegments>
+        <codeSegment>
+          <code>
+            <![CDATA[
+    /**
+     * Get the full name of the author.
+     *
+     * @return <code>name</code> if defined, <code>firsName lastName</code> otherwise.
+     * @see #getName()
+     * @see #getFirstName()
+     * @see #getLastName()
+     * @since 1.1.1
+     */
+    public String getFullName()
+    {
+        if ( getName() != null && getName().trim().length() > 0 )
+        {
+            return getName().trim();
+        }
+
+        return ( getFirstName() != null ? getFirstName().trim() : "null" ) + " "
+            + ( getLastName() != null ? getLastName().trim() : "null" );
+    }
+            ]]>
+          </code>
+        </codeSegment>
+      </codeSegments>
+    </class>
+
+    <class>
+      <name>DocumentTemplate</name>
+      <description>A template that was used to create the document.</description>
+      <version>1.0.0+</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>href</name>
+          <description><![CDATA[
+            The location of the document template.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>title</name>
+          <description><![CDATA[
+            The name of the document template.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>date</name>
+          <description><![CDATA[
+            The date and time when the template was last modified, prior
+            to being used to create the current document.
+            Use the ISO 8601 format "yyyy-MM-dd'T'HH:mm:ss.SSS" in xml.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>Date</type>
+          <identifier>true</identifier>
+        </field>
+        <field java.getter="false" xml.attribute="true">
+          <name>modifydate</name>
+          <version>1.0.1+</version>
+          <description><![CDATA[
+            The date as String (recommended format is ISO 8601) when the template was last modified.
+            Only used if <code>date</code> is not set.
+            @since 1.1.1
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+      <codeSegments>
+        <codeSegment>
+          <code>
+            <![CDATA[
+    /** ISO 8601 date format, i.e. <code>yyyy-MM-dd</code> **/
+    private static final java.text.DateFormat ISO_8601_FORMAT = new java.text.SimpleDateFormat( "yyyy-MM-dd", java.util.Locale.ENGLISH );
+
+    /**
+     * Get the date and time when the template was last modified.
+     *
+     * @return the <code>getDate()</code> if setted, formatted using ISO-8601 English format, otherwise return
+     * the <code>modifydate</code>.
+     * @since 1.1.1
+     * @see #getDate()
+     */
+    public String getModifydate()
+    {
+        if ( getDate() != null )
+        {
+            return ISO_8601_FORMAT.format( getDate() );
+        }
+
+        return this.modifydate;
+    }
+            ]]>
+          </code>
+        </codeSegment>
+      </codeSegments>
+    </class>
+
+    <class>
+      <name>DocumentHyperlinkBehaviour</name>
+      <description>Specifies the default behavior for hyperlinks in the document.</description>
+      <version>1.0.0+</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>targetFrame</name>
+          <description><![CDATA[
+            The name of the default target frame.
+            <p>
+              Specifies the name of the default target frame in which to display
+              a document referenced by a hyperlink.
+            </p>
+
+            <p>
+              This attribute can have one of the following values:
+            </p>
+
+            <dl>
+              <dd>_self</dd>
+              <dt>
+                The referenced document replaces the content
+                of the current frame.
+              </dt>
+
+              <dd>_blank</dd>
+              <dt>
+                The referenced document is displayed in a new frame.
+              </dt>
+
+              <dd>_parent</dd>
+              <dt>
+                The referenced document is displayed in the parent frame
+                of the current frame.
+              </dt>
+
+              <dd>_top</dd>
+              <dt>
+                The referenced document is displayed in the topmost frame,
+                that is the frame that contains the current frame as a child
+                or descendent but is not contained within another frame.
+              </dt>
+
+              <dd>A frame name</dd>
+              <dt>
+                The referenced document is displayed in the named frame.
+                If the named frame does not exist, a new frame with that
+                name is created.
+              </dt>
+            </dl>
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <defaultValue>_self</defaultValue>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+
+    <class>
+      <name>DocumentStatistic</name>
+      <description>Statistical attributes of the document.</description>
+      <version>1.0.0+</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>pageCount</name>
+          <description><![CDATA[
+            The number of pages in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>tableCount</name>
+          <description><![CDATA[
+            The number of tabels in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>drawCount</name>
+          <description><![CDATA[
+            The number of drawings in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>imageCount</name>
+          <description><![CDATA[
+            The number of images in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>objectCount</name>
+          <description><![CDATA[
+            The number of objects in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>oleObjectCount</name>
+          <description><![CDATA[
+            The number of ole-objects in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>paragraphCount</name>
+          <description><![CDATA[
+            The number of paragraphs in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>wordCount</name>
+          <description><![CDATA[
+            The number of words in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>characterCount</name>
+          <description><![CDATA[
+            The number of characters in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>rowCount</name>
+          <description><![CDATA[
+            The number of rows in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>frameCount</name>
+          <description><![CDATA[
+            The number of frames in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>sentenceCount</name>
+          <description><![CDATA[
+            The number of sentences in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>syllableCount</name>
+          <description><![CDATA[
+            The number of syllables in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>nonWhitespaceCharacterCount</name>
+          <description><![CDATA[
+            The number of non-whitespace-characters in the document.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>long</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+
+    <class>
+      <name>DocumentCover</name>
+      <description>Contains meta-data information for the document cover page.</description>
+      <version>1.0.0+</version>
+      <fields>
+        <field>
+          <name>coverTitle</name>
+          <description><![CDATA[
+            The title to appear on the cover.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>coverSubTitle</name>
+          <description><![CDATA[
+            a sub-title to appear on the cover.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>coverVersion</name>
+          <description><![CDATA[
+            The version of the project that appears on the cover page.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>coverType</name>
+          <description><![CDATA[
+            The type of the document. This might be used on the cover page to
+            specify the type of information contained in the document
+            (eg 'User Guide', 'Manual', etc.).
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>coverDate</name>
+          <description><![CDATA[
+            The date to appear on the cover.
+            Use the ISO 8601 format "yyyy-MM-dd'T'HH:mm:ss.SSS" in xml.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>Date</type>
+          <identifier>true</identifier>
+        </field>
+        <field java.getter="false">
+          <name>coverdate</name>
+          <description><![CDATA[
+            The date as String (recommended format is ISO 8601) to appear on the cover.
+            Only used if <code>coverDate</code> is not set.
+            @since 1.1.1
+          ]]></description>
+          <version>1.0.1+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>authors</name>
+          <description><![CDATA[
+            The authors that appear on the cover page.
+            Only used if author is not given.
+          ]]></description>
+          <version>1.0.0+</version>
+          <association xml.itemsStyle="wrapped" xml.tagName="author">
+            <type>DocumentAuthor</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>author</name>
+          <description><![CDATA[
+            A shortcut for the unique author that appears on the cover page.
+            For more authors, you could use the <authors/> tag.
+            @since 1.1.1
+          ]]></description>
+          <version>1.0.1+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>projectName</name>
+          <description><![CDATA[
+            The name of the project.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>projectLogo</name>
+          <description><![CDATA[
+            The location of an image file that represents the project logo.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>companyName</name>
+          <description><![CDATA[
+            The name of the entity that is responsible for the content of the
+            document, or a copyright holder.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>companyLogo</name>
+          <description><![CDATA[
+            The location of an image file that represents the company logo.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+      <codeSegments>
+        <codeSegment>
+          <code>
+            <![CDATA[
+    /**
+     * @return {@link #getAuthor()} if the unique author name is defined. Otherwise, return all authors full names
+     * comma separated.
+     * @see #getAuthor()
+     * @see #getAuthors()
+     * @since 1.1.1
+     */
+    public String getAllAuthorNames()
+    {
+        StringBuffer sb = new StringBuffer();
+        if ( getAuthor() != null && getAuthor().trim().length() > 0 )
+        {
+            return getAuthor().trim();
+        }
+
+        if ( getAuthors() != null )
+        {
+            for ( java.util.Iterator it = getAuthors().iterator(); it.hasNext(); )
+            {
+                DocumentAuthor author = (DocumentAuthor) it.next();
+
+                sb.append( author.getFullName().trim() );
+
+                if ( it.hasNext() )
+                {
+                    sb.append( ", " );
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /** ISO 8601 date format, i.e. <code>yyyy-MM-dd</code> **/
+    private static final java.text.DateFormat ISO_8601_FORMAT = new java.text.SimpleDateFormat( "yyyy-MM-dd", java.util.Locale.ENGLISH );
+
+    /**
+     * Get the date to appear on the cover.
+     *
+     * @return the <code>getCoverDate()</code> if setted, formatted using ISO-8601 English format, otherwise return
+     * the <code>coverdate</code>.
+     * @since 1.1.1
+     * @see #getCoverDate()
+     */
+    public String getCoverdate()
+    {
+        if ( getCoverDate() != null )
+        {
+            return ISO_8601_FORMAT.format( getCoverDate() );
+        }
+
+        return this.coverdate;
+    }
+            ]]>
+          </code>
+        </codeSegment>
+      </codeSegments>
+    </class>
+
+    <class xsd.compositor="sequence">
+      <name>DocumentTOC</name>
+      <description>A Table Of Content for the document.</description>
+      <version>1.0.0+</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>name</name>
+          <description><![CDATA[
+            The name to use for the Table of Contents.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>depth</name>
+          <description><![CDATA[
+            The level up to which toc items are included by default.
+            Defaults to 0 which means only top-level element entries are included.
+            A depth of 1 includes one level of sub-items (sub-sections), etc.
+            This may be overridden for individual entries by using the collapse
+            attribute of the corresponding toc item.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>int</type>
+          <identifier>true</identifier>
+          <defaultValue>0</defaultValue>
+        </field>
+        <field>
+          <name>items</name>
+          <description><![CDATA[
+            TOC item.
+          ]]></description>
+          <version>1.0.0+</version>
+          <association xml.itemsStyle="flat">
+            <type>DocumentTOCItem</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+
+    <class xsd.compositor="sequence">
+      <name>DocumentTOCItem</name>
+      <description>A menu item.</description>
+      <version>1.0.0+</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>name</name>
+          <description><![CDATA[
+            The name to use for the toc.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>ref</name>
+          <description><![CDATA[
+            The ref to use for the item.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>collapse</name>
+          <description><![CDATA[
+           Whether to show any child elements of a TOCItem, overriding
+           the depth given in DocumentTOC. By default, all children
+           are expanded up to the depth given in DocumentTOC.
+          ]]></description>
+          <version>1.0.0+</version>
+          <type>boolean</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>items</name>
+          <description><![CDATA[
+           A table of content item containing sub-items.
+          ]]></description>
+          <version>1.0.0+</version>
+          <association xml.itemsStyle="flat">
+            <type>DocumentTOCItem</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+  </classes>
+</model>
diff --git a/doxia-core/src/site/apt/using-document-xsd.apt b/doxia-core/src/site/apt/using-document-xsd.apt
new file mode 100644
index 0000000..6fb2317
--- /dev/null
+++ b/doxia-core/src/site/apt/using-document-xsd.apt
@@ -0,0 +1,42 @@
+ -----
+ Using Schema Document 1.0.1
+ -----
+ Vincent Siveton
+ ------
+ 2009-01-28
+ ------
+
+~~ Licensed to the Apache Software Foundation (ASF) under one
+~~ or more contributor license agreements.  See the NOTICE file
+~~ distributed with this work for additional information
+~~ regarding copyright ownership.  The ASF licenses this file
+~~ to you under the Apache License, Version 2.0 (the
+~~ "License"); you may not use this file except in compliance
+~~ with the License.  You may obtain a copy of the License at
+~~
+~~   http://www.apache.org/licenses/LICENSE-2.0
+~~
+~~ Unless required by applicable law or agreed to in writing,
+~~ software distributed under the License is distributed on an
+~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+~~ KIND, either express or implied.  See the License for the
+~~ specific language governing permissions and limitations
+~~ under the License.
+
+~~ NOTE: For help with the syntax of this file, see:
+~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Using Schema Document 1.0.1
+
+  The Decoration XSD is located {{{http://maven.apache.org/xsd/document-1.0.1.xsd}here}}.
+
+  Your favorite IDE probably supports XSD schema's for .xml files. You need to specify the following:
+
++-----+
+<document xmlns="http://maven.apache.org/DOCUMENT/1.0.1"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/DOCUMENT/1.0.1 http://maven.apache.org/xsd/document-1.0.1.xsd"
+  outputName="...">
+...
+</document>
++-----+
diff --git a/doxia-core/src/site/site.xml b/doxia-core/src/site/site.xml
new file mode 100644
index 0000000..a586656
--- /dev/null
+++ b/doxia-core/src/site/site.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 ../../../../doxia-sitetools/doxia-decoration-model/target/generated-site/xsd/decoration-1.0.0.xsd">
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu name="Schema Document 1.0">
+      <item name="Reference of Schema Document" href="document.html"/>
+      <item name="Using Schema Document" href="using-document-xsd.html"/>
+    </menu>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/AbstractModuleTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/AbstractModuleTest.java
new file mode 100644
index 0000000..166a3fe
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/AbstractModuleTest.java
@@ -0,0 +1,219 @@
+package org.apache.maven.doxia;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.markup.Markup;
+
+import org.codehaus.plexus.PlexusTestCase;
+import org.codehaus.plexus.util.WriterFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * Provide some common convenience methods to test Doxia modules (parsers and sinks).
+ *
+ * @version $Id: AbstractModuleTest.java 712574 2008-11-09 22:16:42Z hboutemy $
+ * @since 1.0
+ */
+public abstract class AbstractModuleTest
+    extends PlexusTestCase
+    implements Markup
+{
+    /**
+     * Set the system properties:
+     * <ul>
+     * <li><code>line.separator</code> to <code>\n</code> (Unix) to prevent
+     * failure on windows.</li>
+     * </ul>
+     */
+    static
+    {
+        // Safety
+        System.setProperty( "line.separator", "\n" );
+    }
+
+    // ----------------------------------------------------------------------
+    // Methods for creating test reader and writer
+    // ----------------------------------------------------------------------
+
+    /**
+     * Returns a FileWriter to write to a file with the given name
+     * in the test target output directory.
+     *
+     * @param baseName The name of the target file.
+     * @param extension The file extension of the file to write.
+     * @return A FileWriter.
+     * @throws IOException If the FileWriter could not be generated.
+     * @see WriterFactory#newWriter(File, String)
+     */
+    protected Writer getTestWriter( String baseName, String extension )
+        throws IOException
+    {
+        File outputDirectory =
+            new File( getBasedirFile(), outputBaseDir() + getOutputDir() );
+
+        if ( !outputDirectory.exists() )
+        {
+            outputDirectory.mkdirs();
+        }
+
+        return WriterFactory.newWriter( new File( outputDirectory, baseName + "." + extension ), "UTF-8" );
+    }
+
+    /**
+     * Returns an XML FileWriter to write to a file with the given name
+     * in the test target output directory.
+     *
+     * @param baseName The name of the target file.
+     * @return An XML FileWriter.
+     * @throws IOException If the FileWriter could not be generated.
+     * @see #getXmlTestWriter(String, String)
+     */
+    protected Writer getXmlTestWriter( String baseName )
+        throws IOException
+    {
+        return getXmlTestWriter( baseName, outputExtension() );
+    }
+
+    /**
+     * Returns an XML FileWriter to write to a file with the given name
+     * in the test target output directory.
+     *
+     * @param baseName The name of the target file.
+     * @param extension The file extension of the file to write.
+     * @return An XML FileWriter.
+     * @throws IOException If the FileWriter could not be generated.
+     * @see WriterFactory#newXmlWriter(File)
+     */
+    protected Writer getXmlTestWriter( String baseName, String extension )
+        throws IOException
+    {
+        File outputDirectory =
+            new File( getBasedirFile(), outputBaseDir() + getOutputDir() );
+
+        if ( !outputDirectory.exists() )
+        {
+            outputDirectory.mkdirs();
+        }
+
+        return WriterFactory.newXmlWriter(
+            new File( outputDirectory, baseName + "." + extension ) );
+    }
+
+    /**
+     * Returns a FileWriter to write to a file with the given name
+     * in the test target output directory.
+     *
+     * @param baseName The name of the target file.
+     * @return A FileWriter.
+     * @throws IOException If the FileWriter could not be generated.
+     * @see #getTestWriter(String, String)
+     */
+    protected Writer getTestWriter( String baseName )
+        throws IOException
+    {
+        return getTestWriter( baseName, outputExtension() );
+    }
+
+    /**
+     * Returns an InputStreamReader to read a resource from a file
+     * in the test target output directory.
+     *
+     * @param baseName The name of the resource file to read.
+     * @param extension The file extension of the resource file to read.
+     * @return An InputStreamReader.
+     */
+    protected Reader getTestReader( String baseName, String extension )
+    {
+        InputStream is =
+            Thread.currentThread().getContextClassLoader().getResourceAsStream(
+                baseName + "." + extension );
+
+        assertNotNull( "Could not find resource: "
+            + baseName + "." + extension, is );
+
+        InputStreamReader reader = new InputStreamReader( is );
+
+        return reader;
+    }
+
+    /**
+     * Returns an InputStreamReader to read a resource from a file
+     * in the test target output directory.
+     *
+     * @param baseName The name of the resource file to read.
+     * @return An InputStreamReader.
+     * @see #getTestReader(String, String)
+     */
+    protected Reader getTestReader( String baseName )
+    {
+        return getTestReader( baseName, outputExtension() );
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Utility methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Returns the common base directory.
+     *
+     * @return The common base directory as a File.
+     */
+    protected File getBasedirFile()
+    {
+        return new File( getBasedir() );
+    }
+
+    /**
+     * Returns the base directory where all test output will go.
+     *
+     * @return The test output directory.
+     */
+    protected final String outputBaseDir()
+    {
+        return "target/test-output/";
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Abstract methods the individual ModuleTests must provide
+    // ----------------------------------------------------------------------
+
+    /**
+     * Determines the default file extension for the current module.
+     *
+     * @return The default file extension.
+     */
+    protected abstract String outputExtension();
+
+    /**
+     * Returns the directory where test output will go.
+     * Should be relative to outputBaseDir().
+     *
+     * @return The test output directory, relative to outputBaseDir().
+     */
+    protected abstract String getOutputDir();
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/document/DocumentModelTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/document/DocumentModelTest.java
new file mode 100644
index 0000000..04ce4b9
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/document/DocumentModelTest.java
@@ -0,0 +1,371 @@
+package org.apache.maven.doxia.document;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Reader;
+import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Writer;
+
+import org.codehaus.plexus.PlexusTestCase;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.WriterFactory;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Test DocumentModel.
+ *
+ * @author ltheussl
+ */
+public class DocumentModelTest
+    extends PlexusTestCase
+{
+    /** ISO 8601 date format, i.e. <code>yyyy-MM-dd</code> **/
+    private static final java.text.DateFormat ISO_8601_FORMAT =
+            new java.text.SimpleDateFormat( "yyyy-MM-dd", java.util.Locale.ENGLISH );
+
+    /**
+     * Test DocumentModel.
+     *
+     * @throws Exception if any.
+     */
+    public void testDocumentModel()
+            throws Exception
+    {
+        DocumentModel model = getModel();
+        verifyModel( model );
+
+        DocumentModel copy = writeAndRecover( model );
+        verifyModel( copy );
+        assertTrue( copy.equals( model ) );
+    }
+
+    private DocumentModel getModel()
+    {
+        DocumentModel model = new DocumentModel();
+
+        model.setOutputName( "outputName" );
+        model.setModelEncoding( "ISO-8859-1" );
+        model.setCover( getDocumentCover() );
+        model.setToc( getDocumentToc() );
+        model.setMeta( getDocumentMeta() );
+
+        return model ;
+    }
+
+    private void verifyModel( DocumentModel model )
+    {
+        assertTrue( model.equals( model ) );
+        assertFalse( model.equals( null ) );
+        assertTrue ( model.hashCode() != 0 );
+        assertTrue( model.toString().length() > 0 );
+
+        assertEquals( "outputName", model.getOutputName() );
+        assertEquals( "ISO-8859-1", model.getModelEncoding() );
+        verifyDocumentCover( model.getCover() );
+        verifyDocumentTOC( model.getToc() );
+        verifyDocumentMeta( model.getMeta() );
+    }
+
+    private DocumentAuthor getAuthor( int i )
+    {
+        DocumentAuthor author = new DocumentAuthor();
+
+        author.setCity( "city" + i );
+        author.setCompanyName( "companyName" + i );
+        author.setCountry( "country" + i );
+        author.setEmail( "email" + i );
+        author.setFaxNumber( "faxNumber" + i );
+        author.setName( "name" + i );
+        author.setFirstName( "firstName" + i );
+        author.setInitials( "initials" + i );
+        author.setLastName( "lastName" + i );
+        author.setPhoneNumber( "phoneNumber" + i );
+        author.setPosition( "position" + i );
+        author.setPostalCode( "postalCode" + i );
+        author.setState( "state" + i );
+        author.setStreet( "street" + i );
+        author.setTitle( "title" + i );
+
+        return author;
+    }
+
+    private void verifyAuthor( DocumentAuthor documentAuthor, int i )
+    {
+        assertEquals( "city" + i, documentAuthor.getCity() );
+        assertEquals( "companyName" + i, documentAuthor.getCompanyName() );
+        assertEquals( "country" + i, documentAuthor.getCountry() );
+        assertEquals( "email" + i, documentAuthor.getEmail() );
+        assertEquals( "faxNumber" + i, documentAuthor.getFaxNumber() );
+        assertEquals( "name" + i, documentAuthor.getName() );
+        assertEquals( "firstName" + i, documentAuthor.getFirstName() );
+        assertEquals( "initials" + i, documentAuthor.getInitials() );
+        assertEquals( "lastName" + i, documentAuthor.getLastName() );
+        assertEquals( "phoneNumber" + i, documentAuthor.getPhoneNumber() );
+        assertEquals( "position" + i, documentAuthor.getPosition() );
+        assertEquals( "postalCode" + i, documentAuthor.getPostalCode() );
+        assertEquals( "state" + i, documentAuthor.getState() );
+        assertEquals( "street" + i, documentAuthor.getStreet() );
+        assertEquals( "title" + i, documentAuthor.getTitle() );
+    }
+
+    private DocumentCover getDocumentCover()
+    {
+        DocumentCover cover = new DocumentCover();
+        cover.addAuthor( getAuthor( 1 ) );
+        cover.setAuthor( "Author" );
+        cover.setCompanyLogo( "companyLogo" );
+        cover.setCompanyName( "companyName" );
+        cover.setCoverDate( new Date( 0L ) );
+        cover.setCoverSubTitle( "coverSubTitle" );
+        cover.setCoverTitle( "coverTitle" );
+        cover.setCoverType( "coverType" );
+        cover.setCoverVersion( "coverVersion" );
+        cover.setProjectLogo( "projectLogo" );
+        cover.setProjectName( "projectName" );
+
+        return cover;
+    }
+
+    private void verifyDocumentCover( DocumentCover cover )
+    {
+        List authors = cover.getAuthors();
+        assertEquals( 1, authors.size() );
+        verifyAuthor( (DocumentAuthor) authors.get( 0 ), 1 );
+
+        assertEquals( "Author", cover.getAuthor() );
+        assertEquals( "companyLogo", cover.getCompanyLogo() );
+        assertEquals( "companyName", cover.getCompanyName() );
+        assertEquals( 0L, cover.getCoverDate().getTime() );
+        // the actual String depends on the timezone you're in
+        assertEquals( ISO_8601_FORMAT.format( new Date( 0L ) ), cover.getCoverdate() );
+        assertEquals( "coverSubTitle", cover.getCoverSubTitle() );
+        assertEquals( "coverTitle", cover.getCoverTitle() );
+        assertEquals( "coverType", cover.getCoverType() );
+        assertEquals( "coverVersion", cover.getCoverVersion() );
+        assertEquals( "projectLogo", cover.getProjectLogo() );
+        assertEquals( "projectName", cover.getProjectName() );
+    }
+
+    private DocumentStatistic getDocumentStatistic()
+    {
+        DocumentStatistic statistic = new DocumentStatistic();
+
+        statistic.setCharacterCount( 2L );
+        statistic.setDrawCount( 3L );
+        statistic.setFrameCount( 4L );
+        statistic.setImageCount( 5L );
+        statistic.setNonWhitespaceCharacterCount( 6L );
+        statistic.setObjectCount( 1L );
+        statistic.setOleObjectCount( 8L );
+        statistic.setPageCount( 7L );
+        statistic.setParagraphCount( 5L );
+        statistic.setRowCount( 6L );
+        statistic.setSentenceCount( 3L );
+        statistic.setSyllableCount( 10L );
+        statistic.setTableCount( 2L );
+        statistic.setWordCount( 11L );
+
+        return statistic;
+    }
+
+    private void verifyDocumentStatistic( DocumentStatistic documentStatistic )
+    {
+        assertEquals( 2L, documentStatistic.getCharacterCount() );
+        assertEquals( 3L, documentStatistic.getDrawCount() );
+        assertEquals( 4L, documentStatistic.getFrameCount() );
+        assertEquals( 5L, documentStatistic.getImageCount() );
+        assertEquals( 6L, documentStatistic.getNonWhitespaceCharacterCount() );
+        assertEquals( 1L, documentStatistic.getObjectCount() );
+        assertEquals( 8L, documentStatistic.getOleObjectCount() );
+        assertEquals( 7L, documentStatistic.getPageCount() );
+        assertEquals( 5L, documentStatistic.getParagraphCount() );
+        assertEquals( 6L, documentStatistic.getRowCount() );
+        assertEquals( 3L, documentStatistic.getSentenceCount() );
+        assertEquals( 10L, documentStatistic.getSyllableCount() );
+        assertEquals( 2L, documentStatistic.getTableCount() );
+        assertEquals( 11L, documentStatistic.getWordCount() );
+    }
+
+    private DocumentHyperlinkBehaviour getDocumentHyperlinkBehaviour()
+    {
+        DocumentHyperlinkBehaviour hylink = new DocumentHyperlinkBehaviour();
+
+        hylink.setTargetFrame( "targetFrame" );
+
+        return hylink;
+    }
+
+    private void verifyDocumentHyperlinkBehaviour( DocumentHyperlinkBehaviour hyperlinkBehaviour )
+    {
+        assertEquals( "targetFrame", hyperlinkBehaviour.getTargetFrame() );
+    }
+
+    private DocumentMeta getDocumentMeta()
+    {
+        DocumentMeta meta = new DocumentMeta();
+
+        meta.setAuthor( "author" );
+        meta.addAuthor( getAuthor( 2 ) );
+        meta.setConfidential( true );
+        meta.setCreationDate( new Date( 1L ) );
+        meta.setCreator( "creator" );
+        meta.setDate( new Date( 2L ) );
+        meta.setDescription( "description" );
+        meta.setDocumentStatistic( getDocumentStatistic() );
+        meta.setDraft( true );
+        meta.setEditingCycles( 15L );
+        meta.setEditingDuration( 3L );
+        meta.setGenerator( "generator" );
+        meta.setHyperlinkBehaviour( getDocumentHyperlinkBehaviour() );
+        meta.setInitialCreator( "initialCreator" );
+        meta.addKeyWord( "keyword1" );
+        meta.addKeyWord( "keyword2" );
+        meta.setLanguage( "language" );
+        meta.setPageSize( "pageSize" );
+        meta.setPrintDate( new Date( 4L ) );
+        meta.setPrintedBy( "printedBy" );
+        meta.setSubject( "subject" );
+        meta.setTemplate( getDocumentTemplate() );
+        meta.setTitle( "title" );
+
+        return meta;
+    }
+
+    private void verifyDocumentMeta( DocumentMeta meta )
+    {
+        assertEquals( "author", meta.getAuthor() );
+        List authors = meta.getAuthors();
+        assertEquals( 1, authors.size() );
+        verifyAuthor( (DocumentAuthor) authors.get( 0 ), 2 );
+
+        assertTrue( meta.isConfidential() );
+        assertEquals( 1L, meta.getCreationDate().getTime() );
+        assertEquals( "creator", meta.getCreator() );
+        assertEquals( 2L, meta.getDate().getTime() );
+        assertEquals( "description", meta.getDescription() );
+        verifyDocumentStatistic( meta.getDocumentStatistic() );
+        assertTrue( meta.isDraft() );
+        assertEquals( 15L, meta.getEditingCycles() );
+        assertEquals( 3L, meta.getEditingDuration() );
+        assertEquals( "generator", meta.getGenerator() );
+        verifyDocumentHyperlinkBehaviour( meta.getHyperlinkBehaviour() );
+        assertEquals( "initialCreator", meta.getInitialCreator() );
+        assertEquals( "keyword1, keyword2", meta.getAllKeyWords() );
+        assertEquals( "language", meta.getLanguage() );
+        assertEquals( "pageSize", meta.getPageSize() );
+        assertEquals( 4L, meta.getPrintDate().getTime() );
+        assertEquals( "printedBy", meta.getPrintedBy() );
+        assertEquals( "subject", meta.getSubject() );
+        verifyDocumentTemplate( meta.getTemplate() );
+        assertEquals( "title", meta.getTitle() );
+    }
+
+    private DocumentTemplate getDocumentTemplate()
+    {
+        DocumentTemplate template = new DocumentTemplate();
+
+        template.setDate( new Date( 3L ) );
+        template.setHref( "href" );
+        template.setTitle( "title" );
+
+        return template;
+    }
+
+    private void verifyDocumentTemplate( DocumentTemplate template )
+    {
+        assertEquals( 3L, template.getDate().getTime() );
+        assertEquals( "href", template.getHref() );
+        assertEquals( "title", template.getTitle() );
+    }
+
+    private DocumentTOC getDocumentToc()
+    {
+        DocumentTOCItem item1 = new DocumentTOCItem();
+        item1.setName( "First document" );
+        item1.setRef( "doc1.apt" );
+
+        DocumentTOCItem item2 = new DocumentTOCItem();
+        item2.setName( "Second document" );
+        item2.setRef( "doc2.xml" );
+
+        DocumentTOC toc = new DocumentTOC();
+        toc.setName( "name" );
+        toc.addItem( item1 );
+        toc.addItem( item2 );
+
+        return toc;
+    }
+
+    private void verifyDocumentTOC( DocumentTOC toc )
+    {
+        assertEquals( "name", toc.getName() );
+    }
+
+    private DocumentModel writeAndRecover( DocumentModel model )
+            throws IOException
+    {
+        File dir = getTestFile( "target/test-output/xpp3/" );
+
+        if ( !dir.exists() )
+        {
+            dir.mkdirs();
+        }
+
+        File testFile = getTestFile( dir.getAbsolutePath(), "testModel.xml" );
+        Writer w = null;
+
+        try
+        {
+            w = WriterFactory.newXmlWriter( testFile );
+            new DocumentXpp3Writer().write( w, model );
+        }
+        finally
+        {
+            IOUtil.close( w );
+        }
+
+        DocumentModel documentModel;
+
+        Reader reader = null;
+
+        try
+        {
+            reader = ReaderFactory.newXmlReader( testFile );
+            documentModel = new DocumentXpp3Reader().read( reader );
+        }
+        catch ( XmlPullParserException e )
+        {
+            throw (IOException) new IOException( "Error parsing document descriptor" ).initCause( e );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+
+        return documentModel;
+    }
+}
\ No newline at end of file
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/index/IndexEntryTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/index/IndexEntryTest.java
new file mode 100644
index 0000000..9c57e2c
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/index/IndexEntryTest.java
@@ -0,0 +1,76 @@
+package org.apache.maven.doxia.index;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * @author <a href="mailto:trygve.laugstol at objectware.no">Trygve Laugstøl</a>
+ * @version $Id: IndexEntryTest.java 762694 2009-04-07 11:16:56Z ltheussl $
+ */
+public class IndexEntryTest
+    extends TestCase
+{
+    /**
+     * Test IndexEntry.
+     */
+    public void testIndexEntry()
+    {
+        IndexEntry root = new IndexEntry( null );
+
+        assertIndexEntry( root, null, 0, null, null );
+
+        // -----------------------------------------------------------------------
+        // Chapter 1
+        // -----------------------------------------------------------------------
+
+        IndexEntry chapter1 = new IndexEntry( root, "chapter-1" );
+
+        assertIndexEntry( root, null, 1, null, null );
+
+        assertIndexEntry( chapter1, root, 0, null, null );
+
+        // -----------------------------------------------------------------------
+        // Chapter 2
+        // -----------------------------------------------------------------------
+
+        IndexEntry chapter2 = new IndexEntry( root, "chapter-2" );
+
+        assertIndexEntry( root, null, 2, null, null );
+
+        assertIndexEntry( chapter1, root, 0, null, chapter2 );
+        assertIndexEntry( chapter2, root, 0, chapter1, null );
+
+        chapter2.setTitle( "Title 2" );
+        assertTrue( chapter2.toString().indexOf( "Title 2" ) != -1 );
+    }
+
+    private void assertIndexEntry( IndexEntry entry, IndexEntry parent, int childCount,
+            IndexEntry prevEntry, IndexEntry nextEntry )
+    {
+        assertEquals( parent, entry.getParent() );
+
+        assertEquals( childCount, entry.getChildEntries().size() );
+
+        assertEquals( prevEntry, entry.getPrevEntry() );
+
+        assertEquals( nextEntry, entry.getNextEntry() );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/macro/EchoMacroTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/macro/EchoMacroTest.java
new file mode 100644
index 0000000..6ee81bc
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/macro/EchoMacroTest.java
@@ -0,0 +1,81 @@
+package org.apache.maven.doxia.macro;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+
+/**
+ * Test echo macro.
+ *
+ * @author ltheussl
+ */
+public class EchoMacroTest
+        extends TestCase
+{
+
+    /**
+     * Test of execute method, of class EchoMacro.
+     */
+    public void testExecute()
+    {
+        Map macroParameters = new HashMap();
+        macroParameters.put( "paramName", "paramValue" );
+        macroParameters.put( "parser", "parserValue" );
+        macroParameters.put( "sourceContent", "sourceContentValue" );
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+        MacroRequest request = new MacroRequest( macroParameters, new File( "." ) );
+
+        new EchoMacro().execute( sink, request );
+
+        Iterator it = sink.getEventList().iterator();
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "verbatim", event.getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "echo" + Macro.EOL,  (String) event.getArgs()[0] );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "paramName ---> paramValue" + Macro.EOL,  (String) event.getArgs()[0] );
+        event = (SinkEventElement) it.next();
+        assertEquals( "verbatim_", event.getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /**
+     * Test log.
+     */
+    public void testLog()
+    {
+        EchoMacro macro = new EchoMacro();
+        macro.enableLogging( null );
+        assertNotNull ( macro.getLog() );
+        assertNotNull ( macro.getLog() );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/macro/SwfMacroTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/macro/SwfMacroTest.java
new file mode 100644
index 0000000..198be86
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/macro/SwfMacroTest.java
@@ -0,0 +1,109 @@
+package org.apache.maven.doxia.macro;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+
+/**
+ * Test swf macro.
+ *
+ * @author ltheussl
+ */
+public class SwfMacroTest
+        extends TestCase
+{
+
+    /**
+     * Test of execute method, of class SwfMacro.
+     *
+     * @throws MacroExecutionException if a macro fails during testing.
+     */
+    public void testExecute()
+            throws MacroExecutionException
+    {
+
+        Map macroParameters = new HashMap();
+        macroParameters.put( "src", "src.swf" );
+        macroParameters.put( "id", "Movie" );
+        macroParameters.put( "width", "50" );
+        macroParameters.put( "height", "60" );
+        macroParameters.put( "quality", "best" );
+        macroParameters.put( "menu", "true" );
+        macroParameters.put( "loop", "3" );
+        macroParameters.put( "play", "false" );
+        macroParameters.put( "version", "6" );
+        macroParameters.put( "allowScript", "always" );
+
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+        MacroRequest request = new MacroRequest( macroParameters, new File( "." ) );
+        SwfMacro macro = new SwfMacro();
+        macro.required( "src", "value" );
+
+        try
+        {
+            macro.required( "src", "" );
+            fail();
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            macro.required( "src", null );
+            fail();
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        macro.execute( sink, request );
+
+        Iterator it = sink.getEventList().iterator();
+        SinkEventElement event = (SinkEventElement) it.next();
+
+        assertEquals( "rawText", event.getName() );
+        assertFalse( it.hasNext() );
+
+        request = new MacroRequest( new HashMap(), new File( "." ) );
+        sink.reset();
+
+        macro.execute( sink, request );
+
+        it = sink.getEventList().iterator();
+        event = (SinkEventElement) it.next();
+
+        assertEquals( "rawText", event.getName() );
+        assertFalse( it.hasNext() );
+    }
+
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/macro/manager/MacroManagerTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/macro/manager/MacroManagerTest.java
new file mode 100644
index 0000000..699c8c9
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/macro/manager/MacroManagerTest.java
@@ -0,0 +1,55 @@
+package org.apache.maven.doxia.macro.manager;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.macro.Macro;
+
+import org.codehaus.plexus.PlexusTestCase;
+
+/** @author Jason van Zyl */
+public class MacroManagerTest
+    extends PlexusTestCase
+{
+    /**
+     * Test MacroManager.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    public void testMacroManager()
+        throws Exception
+    {
+        MacroManager mm = (MacroManager) lookup( MacroManager.ROLE );
+
+        assertNotNull( mm );
+
+        Macro macro = mm.getMacro( "snippet" );
+        assertNotNull( macro );
+
+        try
+        {
+            macro = mm.getMacro( "weirdId" );
+            fail( "should not exist!" );
+        }
+        catch ( MacroNotFoundException macroNotFoundException )
+        {
+            assertNotNull( macroNotFoundException );
+        }
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/macro/snippet/SnippetMacroTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/macro/snippet/SnippetMacroTest.java
new file mode 100644
index 0000000..2abcc3d
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/macro/snippet/SnippetMacroTest.java
@@ -0,0 +1,117 @@
+package org.apache.maven.doxia.macro.snippet;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+
+import org.codehaus.plexus.PlexusTestCase;
+
+/**
+ * Test snippet macro.
+ *
+ * @author ltheussl
+ */
+public class SnippetMacroTest
+        extends PlexusTestCase
+{
+    /**
+     * Test of execute method, of class SnippetMacro.
+     *
+     * @throws MacroExecutionException if a macro fails during testing.
+     */
+    public void testExecute()
+            throws MacroExecutionException
+    {
+        File basedir = new File( getBasedir() );
+        Map macroParameters = new HashMap();
+        macroParameters.put( "file", "src/test/resources/macro/snippet/testSnippet.txt" );
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        MacroRequest request = new MacroRequest( macroParameters, basedir );
+        SnippetMacro macro = new SnippetMacro();
+        macro.execute( sink, request );
+
+        Iterator it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        String snippet = (String) event.getArgs()[0];
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        assertTrue( snippet.indexOf( "preamble" ) != -1 );
+        assertTrue( snippet.indexOf( "first snippet" ) != -1 );
+        assertTrue( snippet.indexOf( "interlude" ) != -1 );
+        assertTrue( snippet.indexOf( "second snippet" ) != -1 );
+        assertTrue( snippet.indexOf( "conclusion" ) != -1 );
+
+        // again
+
+        macroParameters.put( "id", "firstId" );
+        macroParameters.put( "verbatim", "" );
+        sink.reset();
+        request = new MacroRequest( macroParameters, basedir );
+        macro.execute( sink, request );
+
+        it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        snippet = (String) event.getArgs()[0];
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        assertTrue( snippet.indexOf( "preamble" ) == -1 );
+        assertTrue( snippet.indexOf( "first snippet" ) != -1 );
+        assertTrue( snippet.indexOf( "interlude" ) == -1 );
+        assertTrue( snippet.indexOf( "second snippet" ) == -1 );
+        assertTrue( snippet.indexOf( "conclusion" ) == -1 );
+
+        // again
+
+        macroParameters.put( "id", "secondId" );
+        macroParameters.put( "verbatim", "false" );
+        sink.reset();
+        request = new MacroRequest( macroParameters, basedir );
+        macro.execute( sink, request );
+
+        it = sink.getEventList().iterator();
+        event = (SinkEventElement) it.next();
+        assertEquals( "rawText", event.getName() );
+        snippet = (String) event.getArgs()[0];
+        assertFalse( it.hasNext() );
+
+        assertTrue( snippet.indexOf( "preamble" ) == -1 );
+        assertTrue( snippet.indexOf( "first snippet" ) == -1 );
+        assertTrue( snippet.indexOf( "interlude" ) == -1 );
+        assertTrue( snippet.indexOf( "second snippet" ) != -1 );
+        assertTrue( snippet.indexOf( "conclusion" ) == -1 );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/macro/toc/TocMacroTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/macro/toc/TocMacroTest.java
new file mode 100644
index 0000000..18f041e
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/macro/toc/TocMacroTest.java
@@ -0,0 +1,179 @@
+package org.apache.maven.doxia.macro.toc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.StringWriter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.parser.XhtmlBaseParser;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+import org.apache.maven.doxia.sink.XhtmlBaseSink;
+
+/**
+ * Test toc macro.
+ *
+ * @author ltheussl
+ * @version $Id: TocMacroTest.java 808200 2009-08-26 22:04:08Z vsiveton $
+ */
+public class TocMacroTest
+    extends TestCase
+{
+    /**
+     * Test of execute method, of class TocMacro.
+     *
+     * @throws MacroExecutionException if a macro fails during testing.
+     */
+    public void testExecute()
+        throws MacroExecutionException
+    {
+        String sourceContent = "<div><h2>h21</h2><h2>h22</h2><h3>h3</h3><h4>h4</h4><h2>h23</h2></div>";
+
+        XhtmlBaseParser parser = new XhtmlBaseParser();
+        parser.setSecondParsing( true );
+
+        Map macroParameters = new HashMap();
+        macroParameters.put( "parser", parser );
+        macroParameters.put( "sourceContent", sourceContent );
+        macroParameters.put( "section", "sec1" );
+
+        File basedir = new File( "" );
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+        MacroRequest request = new MacroRequest( macroParameters, basedir );
+        TocMacro macro = new TocMacro();
+        macro.execute( sink, request );
+
+        Iterator it = sink.getEventList().iterator();
+        assertEquals( "list", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        // test parameters
+
+        parser = new XhtmlBaseParser();
+        parser.setSecondParsing( true );
+        macroParameters.put( "parser", parser );
+        macroParameters.put( "section", "2" );
+        macroParameters.put( "fromDepth", "1" );
+        macroParameters.put( "toDepth", "2" );
+        macroParameters.put( "class", "myClass" );
+        macroParameters.put( "id", "myId" );
+
+        sink.reset();
+        request = new MacroRequest( macroParameters, basedir );
+        macro.execute( sink, request );
+
+        it = sink.getEventList().iterator();
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "list", event.getName() );
+        SinkEventAttributeSet atts = (SinkEventAttributeSet) event.getArgs()[0];
+        assertEquals( "myId", atts.getAttribute( "id" ) );
+        assertEquals( "myClass", atts.getAttribute( "class" ) );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "h22", (String) event.getArgs()[0] );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "h3", (String) event.getArgs()[0] );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /**
+     * Test DOXIA-366.
+     *
+     * @throws MacroExecutionException if a macro fails during testing.
+     */
+    public void testTocStyle()
+        throws MacroExecutionException
+    {
+        String sourceContent =
+            "<div><h2>h<b>21</b></h2><h2>h<i>22</i></h2><h3>h<tt>3</tt></h3><h4>h4</h4><h2>h23</h2></div>";
+
+        XhtmlBaseParser parser = new XhtmlBaseParser();
+        parser.setSecondParsing( true );
+
+        Map macroParameters = new HashMap();
+        macroParameters.put( "parser", parser );
+        macroParameters.put( "sourceContent", sourceContent );
+        macroParameters.put( "section", "sec1" );
+
+        File basedir = new File( "" );
+
+        StringWriter out = new StringWriter();
+        XhtmlBaseSink sink = new XhtmlBaseSink( out );
+        MacroRequest request = new MacroRequest( macroParameters, basedir );
+        TocMacro macro = new TocMacro();
+        macro.execute( sink, request );
+
+        assertTrue( out.toString().indexOf( "<a href=\"#h21\">h21</a>" ) != -1 );
+        assertTrue( out.toString().indexOf( "<a href=\"#h22\">h22</a>" ) != -1 );
+        assertTrue( out.toString().indexOf( "<a href=\"#h3\">h3</a>" ) != -1 );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/module/AbstractIdentityTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/module/AbstractIdentityTest.java
new file mode 100644
index 0000000..d4bee24
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/module/AbstractIdentityTest.java
@@ -0,0 +1,166 @@
+package org.apache.maven.doxia.module;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.apache.maven.doxia.AbstractModuleTest;
+
+import org.apache.maven.doxia.logging.PlexusLoggerWrapper;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.Parser;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkTestDocument;
+import org.apache.maven.doxia.sink.TextSink;
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * If a module provides both Parser and Sink, this class
+ * can be used to check that chaining them together
+ * results in the identity transformation, ie the model is still the same
+ * after being piped through a Parser and the corresponding Sink.
+ *
+ * @version $Id: AbstractIdentityTest.java 779957 2009-05-29 13:16:29Z vsiveton $
+ */
+public abstract class AbstractIdentityTest
+    extends AbstractModuleTest
+{
+    /** Expected Identity String */
+    private String expected;
+
+    /**
+     * Set to true if the identity transformation should actually be asserted,
+     * by default only the expected and actual results are written to a file, but not compared.
+     */
+    private boolean assertIdentity;
+
+    /**
+     * Create a new instance of the parser to test.
+     *
+     * @return the parser to test.
+     */
+    protected abstract Parser createParser();
+
+    /**
+     * Return a new instance of the sink that is being tested.
+     *
+     * @param writer The writer for the sink.
+     * @return A new sink.
+     */
+    protected abstract Sink createSink( Writer writer );
+
+    /**
+     * Pipes a full model generated by {@link SinkTestDocument} through
+     * a Sink (generated by {@link #createSink(Writer)}) and a Parser
+     * (generated by {@link #createParser()}) and checks if the result
+     * is the same as the original model. By default, this doesn't actually
+     * assert anything (use {@link #assertIdentity(boolean)} in the setUp()
+     * of an implementation to switch on the test), but the two generated
+     * output files, expected.txt and actual.txt, can be compared for differences.
+     *
+     * @throws IOException if there's a problem reading/writing a test file.
+     * @throws ParseException if a model cannot be parsed.
+     */
+    public void testIdentity()
+        throws IOException, ParseException
+    {
+        // generate the expected model
+        StringWriter writer = new StringWriter();
+        Sink sink = new TextSink( writer );
+        SinkTestDocument.generate( sink );
+        sink.close();
+        expected = writer.toString();
+
+        // write to file for comparison
+        Writer fileWriter = getTestWriter( "expected" );
+        fileWriter.write( expected );
+        IOUtil.close( fileWriter );
+
+        // generate the actual model
+        writer = new StringWriter();
+        sink = createSink( writer );
+        SinkTestDocument.generate( sink );
+        sink.close();
+        StringReader reader = new StringReader( writer.toString() );
+
+        writer = new StringWriter();
+        sink = new TextSink( writer );
+        Parser parser = createParser();
+        parser.enableLogging( new PlexusLoggerWrapper( getContainer().getLogger() ) );
+        parser.parse( reader, sink );
+        String actual = writer.toString();
+
+        // write to file for comparison
+        fileWriter = getTestWriter( "actual" );
+        fileWriter.write( actual );
+        IOUtil.close( fileWriter );
+
+        // Disabled by default, it's unlikely that all our modules
+        // will pass this test any time soon, but the generated
+        // output files can still be compared.
+
+        if ( assertIdentity )
+        {
+            // TODO: make this work for at least apt and xdoc modules?
+            assertEquals( "Identity test failed!", getExpected(), actual );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected String getOutputDir()
+    {
+        return "identity/";
+    }
+
+    /**
+     * The output files generated by this class are text files,
+     * independend of the kind of module being tested.
+     *
+     * @return The String "txt".
+     */
+    protected String outputExtension()
+    {
+        return "txt";
+    }
+
+    /**
+     * Set to true if the identity transformation should actually be asserted,
+     * by default only the expected and actual results are written to a file, but not compared.
+     * This should be called during setUp().
+     *
+     * @param doAssert True to actually execute the test.
+     */
+    protected void assertIdentity( boolean doAssert )
+    {
+        this.assertIdentity = doAssert;
+    }
+
+    /**
+     * @return the expected identity string
+     */
+    protected String getExpected()
+    {
+        return expected;
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTest.java
new file mode 100644
index 0000000..778f5f0
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTest.java
@@ -0,0 +1,123 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.AbstractModuleTest;
+import org.apache.maven.doxia.sink.WellformednessCheckingSink;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.TextSink;
+import org.codehaus.plexus.util.IOUtil;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * Test the parsing of sample input files.
+ * <br/>
+ * <b>Note</b>: you have to provide a sample "test." + outputExtension()
+ * file in the test resources directory if you extend this class.
+ *
+ * @version $Id: AbstractParserTest.java 735474 2009-01-18 15:33:20Z vsiveton $
+ * @since 1.0
+ */
+public abstract class AbstractParserTest
+    extends AbstractModuleTest
+{
+    /**
+     * Create a new instance of the parser to test.
+     *
+     * @return the parser to test.
+     */
+    protected abstract Parser createParser();
+
+    /**
+     * Returns the directory where all parser test output will go.
+     *
+     * @return The test output directory.
+     */
+    protected String getOutputDir()
+    {
+        return "parser/";
+    }
+
+    /**
+     * Parse a test document '"test." + outputExtension()'
+     * with parser from {@link #createParser()}, and output to a new
+     * {@link WellformednessCheckingSink}. Asserts that output is well-formed.
+     *
+     * @throws IOException if the test document cannot be read.
+     * @throws ParseException if the test document cannot be parsed.
+     */
+    public final void testParser()
+        throws IOException, ParseException
+    {
+        WellformednessCheckingSink sink = new WellformednessCheckingSink();
+
+        Reader reader = null;
+        try
+        {
+            reader = getTestReader( "test", outputExtension() );
+
+            createParser().parse( reader, sink );
+
+            assertTrue( "Parser output not well-formed, last offending element: "
+                + sink.getOffender(), sink.isWellformed() );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+    }
+
+     /**
+     * Parse a test document '"test." + outputExtension()'
+     * with parser from {@link #createParser()}, and output to a text file,
+     * using the {@link org.apache.maven.doxia.sink.TextSink TextSink}.
+     *
+     * @throws IOException if the test document cannot be read.
+     * @throws ParseException if the test document cannot be parsed.
+     */
+    public final void testDocument()
+        throws IOException, ParseException
+    {
+        Writer writer = null;
+        Reader reader = null;
+
+        try
+        {
+            writer = getTestWriter( "test", "txt" );
+
+            reader = getTestReader( "test", outputExtension() );
+
+            Sink sink = new TextSink( writer );
+
+            createParser().parse( reader, sink );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+            IOUtil.close( writer );
+        }
+    }
+
+
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTestCase.java b/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTestCase.java
new file mode 100644
index 0000000..41fc5cc
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTestCase.java
@@ -0,0 +1,77 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkAdapter;
+import org.codehaus.plexus.PlexusTestCase;
+
+import java.io.FileReader;
+import java.io.Reader;
+
+/**
+ * Test the parsing of sample input files
+ *
+ * @author <a href="mailto:carlos at apache.org">Carlos Sanchez</a>
+ * @author <a href="mailto:evenisse at codehaus.org">Emmanuel Venisse</a>
+ * @version $Id: AbstractParserTestCase.java 747735 2009-02-25 10:43:09Z ltheussl $
+ * @since 1.0
+ */
+public abstract class AbstractParserTestCase
+    extends PlexusTestCase
+{
+    /**
+     * Parser to use to convert input to sink events
+     *
+     * @return the parser to use
+     */
+    protected abstract Parser getParser();
+
+    /**
+     * Path of the model to test, relative to basedir
+     *
+     * @return the relative path
+     */
+    protected abstract String getDocument();
+
+    /**
+     * Sink to write the output of the parsing
+     *
+     * @return a SinkAdapter if not overridden
+     */
+    protected Sink getSink()
+    {
+        return new SinkAdapter();
+    }
+
+    /**
+     * Parse the model in the path specified by {@link #getDocument()},
+     * with parser from {@link #getParser()}, and output to sink from {@link #getSink()}
+     *
+     * @throws Exception if any.
+     */
+    public void testParser()
+        throws Exception
+    {
+        Reader reader = new FileReader( getTestFile( getBasedir(), getDocument() ) );
+
+        getParser().parse( reader, getSink() );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/parser/XhtmlBaseParserTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/parser/XhtmlBaseParserTest.java
new file mode 100644
index 0000000..250fe40
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/parser/XhtmlBaseParserTest.java
@@ -0,0 +1,675 @@
+package org.apache.maven.doxia.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Iterator;
+
+import org.apache.maven.doxia.logging.Log;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+
+import org.codehaus.plexus.PlexusTestCase;
+
+/**
+ * Test for XhtmlBaseParser.
+ *
+ * @author ltheussl
+ * @version $Id: XhtmlBaseParserTest.java 892639 2009-12-20 18:49:58Z ltheussl $
+ * @since 1.1
+ */
+public class XhtmlBaseParserTest
+    extends PlexusTestCase
+{
+    private XhtmlBaseParser parser;
+    private final SinkEventTestingSink sink = new SinkEventTestingSink();
+
+    /** {@inheritDoc} */
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+
+        parser = new XhtmlBaseParser();
+        parser.getLog().setLogLevel( Log.LEVEL_ERROR );
+        sink.reset();
+    }
+
+    /** {@inheritDoc} */
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    /** @throws Exception  */
+    public void testHeadingEventsList()
+        throws Exception
+    {
+        String text = "<p><h2></h2><h3></h3><h4></h4><h5></h5><h6></h6><h2></h2></p>";
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section3", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle3", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle3_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section4", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle4", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle4_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section5", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle5", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle5_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section5_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section4_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section3_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        // this one is missing because we enclose everything in <p> which is not valid xhtml,
+        // needs to be tested in overriding parser, eg XhtmlParser, XdocParser.
+        //assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testNestedHeadingEventsList()
+        throws Exception
+    {
+        // DOXIA-241
+        String text = "<p><h2></h2><h6></h6><h3></h3></p>";
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "section2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section3", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section4", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "section5", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle5", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle5_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section5_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "section4_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section3_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "section2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2_", ( (SinkEventElement) it.next() ).getName() );
+        // these two are missing because we enclose everything in <p> which is not valid xhtml,
+        // needs to be tested in overriding parser, eg XhtmlParser, XdocParser.
+        //assertEquals( "section2_", ( (SinkEventElement) it.next() ).getName() );
+        //assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testFigureEventsList()
+        throws Exception
+    {
+        String text = "<img src=\"source\" title=\"caption\" />";
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "figureGraphics", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testTableEventsList()
+        throws Exception
+    {
+        // TODO: table caption, see DOXIA-177
+
+        String text = "<table align=\"center\"><tr><th>Header</th></tr><tr><td>cell</td></tr></table>";
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "table", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableHeaderCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableHeaderCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "table_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testSignificantWhiteSpace()
+        throws Exception
+    {
+        // NOTE significant white space
+        String text = "<p><b>word</b> <i>word</i></p>";
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+
+        SinkEventElement el = (SinkEventElement) it.next();
+        assertEquals( "text", el.getName() );
+        assertEquals( " ",  (String) el.getArgs()[0] );
+
+        assertEquals( "italic", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+
+        // same test with EOL
+        String eol = System.getProperty( "line.separator" );
+        text = "<p><b>word</b>" + eol + "<i>word</i></p>";
+
+        sink.reset();
+        parser.parse( text, sink );
+        it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+
+        el = (SinkEventElement) it.next();
+        assertEquals( "text", el.getName() );
+        // according to section 2.11 of the XML spec, parsers must normalize line breaks to "\n"
+        assertEquals( "\n",  (String) el.getArgs()[0] );
+
+        assertEquals( "italic", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+
+        // DOXIA-189: there should be no EOL after closing tag
+        text = "<p>There should be no space after the last <i>word</i>.</p>";
+
+        sink.reset();
+        parser.parse( text, sink );
+        it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic_", ( (SinkEventElement) it.next() ).getName() );
+
+        el = (SinkEventElement) it.next();
+        assertEquals( "text", el.getName() );
+        assertEquals( ".",  (String) el.getArgs()[0] );
+
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testPreFormattedText()
+        throws Exception
+    {
+        String text = "<pre><a href=\"what.html\">what</a></pre>";
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        text = "<pre><![CDATA[<a href=\"what.html\">what</a>]]></pre>";
+        sink.reset();
+        parser.parse( text, sink );
+
+        it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        text = "<pre><![CDATA[<pre>what</pre>]]></pre>";
+        sink.reset();
+        parser.parse( text, sink );
+
+        it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testPreEOL()
+        throws Exception
+    {
+        // test EOLs within <pre>: the sink MUST receive a text event for the EOL
+        String text = "<pre><a href=\"what.html\">what</a>" + XhtmlBaseParser.EOL
+                + "<a href=\"what.html\">what</a></pre>";
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /** @throws Exception  */
+    public void testDoxia250()
+        throws Exception
+    {
+        StringBuffer sb = new StringBuffer();
+        sb.append( "<!DOCTYPE test [" ).append( XhtmlBaseParser.EOL );
+        sb.append( "<!ENTITY foo \"&#x159;\">" ).append( XhtmlBaseParser.EOL );
+        sb.append( "<!ENTITY foo1 \" \">" ).append( XhtmlBaseParser.EOL );
+        sb.append( "<!ENTITY foo2 \"&#x161;\">" ).append( XhtmlBaseParser.EOL );
+        sb.append( "<!ENTITY tritPos \"&#x1d7ed;\">" ).append( XhtmlBaseParser.EOL );
+        sb.append( "]>" ).append( XhtmlBaseParser.EOL );
+        sb.append( "<b>&foo;&foo1;&foo2;&tritPos;</b>" );
+
+        parser.setValidate( false );
+        parser.parse( sb.toString(), sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "bold", event.getName() );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "\u0159",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "\u00A0",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "\u0161",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "\uD835\uDFED",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "bold_", event.getName() );
+    }
+
+    /** @throws Exception  */
+    public void testEntities()
+        throws Exception
+    {
+        final String text = "<!DOCTYPE test [<!ENTITY flo \"&#x159;\"><!ENTITY tritPos \"&#x1d7ed;\"><!ENTITY fo \"A\"><!ENTITY myCustom \"&fo;\">]>"
+                + "<body><h2>&&flo;&#x159;&tritPos;&#x1d7ed;</h2><p>&&flo;&#x159;&tritPos;&#x1d7ed;&myCustom;</p></body>";
+
+        parser.setValidate( false );
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+
+        SinkEventElement textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "&", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\u0159", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\u0159", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\uD835\uDFED",  (String) textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\uD835\uDFED", textEvt.getArgs()[0] );
+
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "&", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\u0159", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\u0159", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\uD835\uDFED",  (String) textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\uD835\uDFED", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "A", textEvt.getArgs()[0] );
+
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testDecoration()
+        throws Exception
+    {
+        String text = "<div><u>u</u><s>s</s><del>del</del><strike>strike</strike><sub>sub</sub><sup>sup</sup></div>";
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "u",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "s",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "del",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "strike",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "sub",  (String) event.getArgs()[0] );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "sup",  (String) event.getArgs()[0] );
+//        assertTrue( ( (SinkEventAttributeSet) event.getArgs()[1] )
+//                .containsAttribute( SinkEventAttributeSet.VALIGN, "sup" ) ); // TODO
+    }
+
+    /** @throws Exception  */
+    public void testLists()
+        throws Exception
+    {
+        String text = "<div><ul><li></li></ul><ol><li></li></ol><dl><dt></dt><dd></dd></dl></div>";
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "list", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "numberedList", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedListItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedListItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedList_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "definitionList", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionListItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definedTerm", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definedTerm_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definition", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definition_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionListItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionList_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /** @throws Exception  */
+    public void testStyles()
+        throws Exception
+    {
+        String text = "<div><b></b><strong></strong><i></i><em></em><code></code><samp></samp><tt></tt></div>";
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "italic", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "monospaced", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "monospaced_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "monospaced", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "monospaced_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "monospaced", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "monospaced_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /** @throws Exception  */
+    public void testSimpleTags()
+        throws Exception
+    {
+        String text = "<div><br/><hr/><img src=\"img.src\"/></div>";
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "lineBreak", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "horizontalRule", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figureGraphics", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /** @throws Exception  */
+    public void testSpecial()
+        throws Exception
+    {
+        String text = "<p><!-- a pagebreak: --><!-- PB -->  <unknown /></p>";
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "comment", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "pageBreak", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "nonBreakingSpace", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "nonBreakingSpace", ( (SinkEventElement) it.next() ).getName() );
+        // unknown events are not reported by the base parser
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /** @throws Exception  */
+    public void testTable()
+        throws Exception
+    {
+        String text = "<table><caption></caption><tr><th></th></tr><tr><td></td></tr></table>";
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "table", ( (SinkEventElement) it.next() ).getName() );
+
+        // DOXIA-374
+        SinkEventElement el = (SinkEventElement) it.next();
+        assertEquals( "tableRows", el.getName() );
+        assertFalse( ( (Boolean) el.getArgs()[1] ).booleanValue() );
+
+        assertEquals( "tableCaption", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCaption_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableHeaderCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableHeaderCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "table_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /** @throws Exception  */
+    public void testFigure()
+        throws Exception
+    {
+        String text = "<div class=\"figure\"><p><img src=\"src.jpg\"/></p><p><i></i></p></div>";
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "figure", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figureGraphics", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figureCaption", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figureCaption_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figure_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /** @throws Exception  */
+    public void testAnchorLink()
+        throws Exception
+    {
+        String text = "<div><a href=\"\"></a>" +
+                "<a href=\"valid\"></a>" +
+                "<a href=\"#1invalid\"></a>" +
+                "<a href=\"http://www.fo.com/index.html#1invalid\"></a>" +
+                "<a name=\"valid\"></a>" +
+                "<a name=\"1invalid\"></a>" +
+                "<a id=\"1invalid\"></a></div>";
+
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        SinkEventElement element = (SinkEventElement) it.next();
+        assertEquals( "link", element.getName() );
+        assertEquals( "", element.getArgs()[0] );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+
+        element = (SinkEventElement) it.next();
+        assertEquals( "link", element.getName() );
+        assertEquals( "valid", element.getArgs()[0] );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+
+        element = (SinkEventElement) it.next();
+        assertEquals( "link", element.getName() );
+        assertEquals( "#a1invalid", element.getArgs()[0] );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+
+        element = (SinkEventElement) it.next();
+        assertEquals( "link", element.getName() );
+        assertEquals( "http://www.fo.com/index.html#1invalid", element.getArgs()[0] );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+
+        element = (SinkEventElement) it.next();
+        assertEquals( "anchor", element.getName() );
+        assertEquals( "valid", element.getArgs()[0] );
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+
+        element = (SinkEventElement) it.next();
+        assertEquals( "anchor", element.getName() );
+        assertEquals( "a1invalid", element.getArgs()[0] );
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+
+        element = (SinkEventElement) it.next();
+        assertEquals( "anchor", element.getName() );
+        assertEquals( "a1invalid", element.getArgs()[0] );
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /**
+     * Test entities in attributes.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    public void testAttributeEntities()
+        throws Exception
+    {
+        String text = "<script type=\"text/javascript\" src=\"http://ex.com/ex.js?v=l&l=e\"></script>";
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        SinkEventElement event = (SinkEventElement) it.next();
+
+        assertEquals( "unknown", event.getName() );
+        assertEquals( "script", event.getArgs()[0] );
+        SinkEventAttributeSet attribs = (SinkEventAttributeSet) event.getArgs()[2];
+        // ampersand should be un-escaped
+        assertEquals( "http://ex.com/ex.js?v=l&l=e", attribs.getAttribute( "src" ) );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        sink.reset();
+        text = "<img src=\"http://ex.com/ex.jpg?v=l&l=e\" alt=\"image\"/>";
+        parser.parse( text, sink );
+
+        it = sink.getEventList().iterator();
+        event = (SinkEventElement) it.next();
+        assertEquals( "figureGraphics", event.getName() );
+        attribs = (SinkEventAttributeSet) event.getArgs()[1];
+        // ampersand should be un-escaped
+        assertEquals( "http://ex.com/ex.jpg?v=l&l=e", attribs.getAttribute( "src" ) );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractSinkTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractSinkTest.java
new file mode 100644
index 0000000..249f145
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractSinkTest.java
@@ -0,0 +1,1014 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.AbstractModuleTest;
+import org.apache.maven.doxia.logging.PlexusLoggerWrapper;
+import org.codehaus.plexus.util.IOUtil;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Abstract base class to test sinks.
+ *
+ * @version $Id: AbstractSinkTest.java 779959 2009-05-29 13:20:44Z vsiveton $
+ * @since 1.0
+ */
+public abstract class AbstractSinkTest
+    extends AbstractModuleTest
+{
+    private final CharArrayWriter testWriter = new CharArrayWriter();
+    private Sink sink;
+
+    /**
+     * Resets the writer and creates a new sink with it.
+     *
+     * {@inheritDoc}
+     *
+     * @throws java.lang.Exception if something goes wrong.
+     */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        testWriter.reset();
+        sink = createSink( testWriter );
+        sink.enableLogging( new PlexusLoggerWrapper( getContainer().getLogger() ) );
+    }
+
+    // ---------------------------------------------------------------------
+    // Common test cases
+    // ----------------------------------------------------------------------
+
+    /**
+     * Tests that the current sink is able to render the common test document. If the sink is an Xml sink defined
+     * by {@link #isXmlSink()}, it uses an Xml Writer defined by {@link #getXmlTestWriter(String)}. If not, it uses
+     * the Writer defined by {@link #getTestWriter(String)}.
+     *
+     * @see SinkTestDocument
+     * @throws IOException If the target test document could not be generated.
+     * @see #isXmlSink()
+     * @see #getTestWriter(String)
+     * @see #getXmlTestWriter(String)
+     */
+    public final void testTestDocument() throws IOException
+    {
+        Writer writer = ( isXmlSink() ? getXmlTestWriter( "testDocument" ) : getTestWriter( "testDocument" ) );
+        Sink testSink = createSink( writer );
+
+        try
+        {
+            SinkTestDocument.generate( testSink );
+        }
+        finally
+        {
+            testSink.close();
+            IOUtil.close( writer );
+        }
+    }
+
+    /**
+     * Checks that the sequence <code>[title(), text( title ), title_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getTitleBlock getTitleBlock}( title ).
+     */
+    public void testTitle()
+    {
+        String title = "Grodek";
+        sink.title();
+        sink.text( title );
+        sink.title_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getTitleBlock( title );
+
+        assertEquals( "Wrong title!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[author(), text( author ), author_()]
+     * </code>, invoked on the current sink, produces the same result as
+     * {@link #getAuthorBlock getAuthorBlock}( author ).
+     */
+    public void testAuthor()
+    {
+        String author = "Georg_Trakl";
+        sink.author();
+        sink.text( author );
+        sink.author_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getAuthorBlock( author );
+
+        assertEquals( "Wrong author!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[date(), text( date ), date_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getDateBlock getDateBlock}( date ).
+     */
+    public void testDate()
+    {
+        String date = "1914";
+        sink.date();
+        sink.text( date );
+        sink.date_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getDateBlock( date );
+
+        assertEquals( "Wrong date!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[head(), head_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getHeadBlock getHeadBlock()}.
+     */
+    public void testHead()
+    {
+        sink.head();
+        sink.head_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getHeadBlock();
+
+        assertEquals( "Wrong head!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[body(), body_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getBodyBlock getBodyBlock()}.
+     */
+    public void testBody()
+    {
+        sink.body();
+        sink.body_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getBodyBlock();
+
+        assertEquals( "Wrong body!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[sectionTitle(), text( title ),
+     * sectionTitle_()]</code>, invoked on the current sink, produces
+     * the same result as
+     * {@link #getSectionTitleBlock getSectionTitleBlock}( title ).
+     */
+    public void testSectionTitle()
+    {
+        String title = "Title";
+        sink.sectionTitle();
+        sink.text( title );
+        sink.sectionTitle_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getSectionTitleBlock( title );
+
+        assertEquals( "Wrong sectionTitle!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[section1(), sectionTitle1(),
+     * text( title ), sectionTitle1_(), section1_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getSection1Block getSection1Block}( title ).
+     */
+    public void testSection1()
+    {
+        String title = "Title1";
+        sink.section1();
+        sink.sectionTitle1();
+        sink.text( title );
+        sink.sectionTitle1_();
+        sink.section1_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getSection1Block( title );
+
+        assertEquals( "Wrong section1 block!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[section2(), sectionTitle2(),
+     * text( title ), sectionTitle2_(), section2_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getSection2Block getSection2Block}( title ).
+     */
+    public void testSection2()
+    {
+        String title = "Title2";
+        sink.section2();
+        sink.sectionTitle2();
+        sink.text( title );
+        sink.sectionTitle2_();
+        sink.section2_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getSection2Block( title );
+
+        assertEquals( "Wrong section2 block!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[section3(), sectionTitle3(),
+     * text( title ), sectionTitle3_(), section3_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getSection3Block getSection3Block}( title ).
+     */
+    public void testSection3()
+    {
+        String title = "Title3";
+        sink.section3();
+        sink.sectionTitle3();
+        sink.text( title );
+        sink.sectionTitle3_();
+        sink.section3_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getSection3Block( title );
+
+        assertEquals( "Wrong section3 block!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[section4(), sectionTitle4(),
+     * text( title ), sectionTitle4_(), section4_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getSection4Block getSection4Block}( title ).
+     *
+     */
+    public void testSection4()
+    {
+        String title = "Title4";
+        sink.section4();
+        sink.sectionTitle4();
+        sink.text( title );
+        sink.sectionTitle4_();
+        sink.section4_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getSection4Block( title );
+
+        assertEquals( "Wrong section4 block!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[section5(), sectionTitle5(),
+     * text( title ), sectionTitle5_(), section5_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getSection5Block getSection5Block}( title ).
+     */
+    public void testSection5()
+    {
+        String title = "Title5";
+        sink.section5();
+        sink.sectionTitle5();
+        sink.text( title );
+        sink.sectionTitle5_();
+        sink.section5_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getSection5Block( title );
+
+        assertEquals( "Wrong section5 block!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[list(), listItem(), text( item ),
+     * listItem_(), list_()]</code>, invoked on the current sink, produces
+     * the same result as {@link #getListBlock getListBlock}( item ).
+     *
+     */
+    public void testList()
+    {
+        String item = "list_item";
+        sink.list();
+        sink.listItem();
+        sink.text( item );
+        sink.listItem_();
+        sink.list_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getListBlock( item );
+
+        assertEquals( "Wrong list!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>
+     * [numberedList( Sink.NUMBERING_LOWER_ROMAN ), numberedListItem(),
+     * text( item ), numberedListItem_(), numberedList_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getNumberedListBlock getNumberedListBlock}( item ).
+     */
+    public void testNumberedList()
+    {
+        String item = "numbered_list_item";
+        sink.numberedList( Sink.NUMBERING_LOWER_ROMAN );
+        sink.numberedListItem();
+        sink.text( item );
+        sink.numberedListItem_();
+        sink.numberedList_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getNumberedListBlock( item );
+
+        assertEquals( "Wrong numbered list!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[definitionList(), definitionListItem(),
+     * definedTerm(), text( definum ), definedTerm_(), definition(),
+     * text( definition ), definition_(), definitionListItem_(),
+     * definitionList_()]</code>, invoked on the current sink, produces the same
+     * result as {@link #getDefinitionListBlock getDefinitionListBlock}
+     * ( definum, definition ).
+     */
+    public void testDefinitionList()
+    {
+        String definum = "definum";
+        String definition = "definition";
+        sink.definitionList();
+        sink.definitionListItem();
+        sink.definedTerm();
+        sink.text( definum );
+        sink.definedTerm_();
+        sink.definition();
+        sink.text( definition );
+        sink.definition_();
+        sink.definitionListItem_();
+        sink.definitionList_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getDefinitionListBlock( definum, definition );
+
+        assertEquals( "Wrong definition list!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[figure(), figureGraphics( source ),
+     * figureCaption(), text( caption ), figureCaption_(), figure_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getFigureBlock getFigureBlock}( source, caption ).
+     */
+    public void testFigure()
+    {
+        String source = "figure.jpg";
+        String caption = "Figure_caption";
+        sink.figure();
+        sink.figureGraphics( source );
+        sink.figureCaption();
+        sink.text( caption );
+        sink.figureCaption_();
+        sink.figure_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getFigureBlock( source, caption );
+
+        assertEquals( "Wrong figure!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[table(),
+     * tableRows( Sink.JUSTIFY_CENTER, false ), tableRow(), tableCell(),
+     * text( cell ), tableCell_(), tableRow_(), tableRows_(), tableCaption(),
+     * text( caption ), tableCaption_(), table_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getTableBlock getTableBlock}( cell, caption ).
+     */
+    public void testTable()
+    {
+        String cell = "cell";
+        String caption = "Table_caption";
+        int[] justify = { Sink.JUSTIFY_CENTER };
+        sink.table();
+        sink.tableRows( justify, false );
+        sink.tableRow();
+        sink.tableCell();
+        sink.text( cell );
+        sink.tableCell_();
+        sink.tableRow_();
+        sink.tableRows_();
+        sink.tableCaption();
+        sink.text( caption );
+        sink.tableCaption_();
+        sink.table_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getTableBlock( cell, caption );
+
+        assertEquals( "Wrong table!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[paragraph(), text( text ),
+     * paragraph_()]</code>, invoked on the current sink, produces
+     * the same result as {@link #getParagraphBlock getParagraphBlock}( text ).
+     */
+    public void testParagraph()
+    {
+        String text = "Text";
+        sink.paragraph();
+        sink.text( text );
+        sink.paragraph_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getParagraphBlock( text );
+
+        assertEquals( "Wrong paragraph!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[verbatim( SinkEventAttributeSet.BOXED ), text( text ),
+     * verbatim_()]</code>, invoked on the current sink, produces the
+     * same result as {@link #getVerbatimBlock getVerbatimBlock}( text ).
+     */
+    public void testVerbatim()
+    {
+        String text = "Text";
+        sink.verbatim( SinkEventAttributeSet.BOXED );
+        sink.text( text );
+        sink.verbatim_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getVerbatimBlock( text );
+
+        assertEquals( "Wrong verbatim!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[horizontalRule()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getHorizontalRuleBlock getHorizontalRuleBlock()}.
+     */
+    public void testHorizontalRule()
+    {
+        sink.horizontalRule();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getHorizontalRuleBlock();
+
+        assertEquals( "Wrong horizontal rule!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[pageBreak()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getPageBreakBlock getPageBreakBlock()}.
+     */
+    public void testPageBreak()
+    {
+        sink.pageBreak();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getPageBreakBlock();
+
+        assertEquals( "Wrong pageBreak!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[anchor( anchor ), text( anchor ),
+     * anchor_()]</code>, invoked on the current sink, produces the same
+     * result as {@link #getAnchorBlock getAnchorBlock}( anchor ).
+     */
+    public void testAnchor()
+    {
+        String anchor = "Anchor";
+        sink.anchor( anchor );
+        sink.text( anchor );
+        sink.anchor_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getAnchorBlock( anchor );
+
+        assertEquals( "Wrong anchor!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[link( link ), text( text ),
+     * link_()]</code>, invoked on the current sink, produces the same
+     * result as {@link #getLinkBlock getLinkBlock}( link, text ).
+     */
+    public void testLink()
+    {
+        String link = "#Link";
+        String text = "Text";
+        sink.link( link );
+        sink.text( text );
+        sink.link_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getLinkBlock( link, text );
+
+        assertEquals( "Wrong link!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[italic(), text( text ), italic_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getItalicBlock getItalicBlock}( text ).
+     */
+    public void testItalic()
+    {
+        String text = "Italic";
+        sink.italic();
+        sink.text( text );
+        sink.italic_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getItalicBlock( text );
+
+        assertEquals( "Wrong italic!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[bold(), text( text ), bold_()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getBoldBlock getBoldBlock}( text ).
+     */
+    public void testBold()
+    {
+        String text = "Bold";
+        sink.bold();
+        sink.text( text );
+        sink.bold_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getBoldBlock( text );
+
+        assertEquals( "Wrong bold!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[monospaced(), text( text ),
+     * monospaced_()]</code>, invoked on the current sink, produces the same
+     * result as {@link #getMonospacedBlock getMonospacedBlock}( text ).
+     */
+    public void testMonospaced()
+    {
+        String text = "Monospaced";
+        sink.monospaced();
+        sink.text( text );
+        sink.monospaced_();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getMonospacedBlock( text );
+
+        assertEquals( "Wrong monospaced!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[lineBreak()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getLineBreakBlock getLineBreakBlock()}.
+     */
+    public void testLineBreak()
+    {
+        sink.lineBreak();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getLineBreakBlock();
+
+        assertEquals( "Wrong lineBreak!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[nonBreakingSpace()]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getNonBreakingSpaceBlock getNonBreakingSpaceBlock()}.
+     */
+    public void testNonBreakingSpace()
+    {
+        sink.nonBreakingSpace();
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getNonBreakingSpaceBlock();
+
+        assertEquals( "Wrong nonBreakingSpace!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[text( text )]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getTextBlock getTextBlock()}.
+     */
+    public void testText()
+    {
+        String text = "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
+        sink.text( text );
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getTextBlock( text );
+
+        assertEquals( "Wrong text!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[rawText( text )]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getRawTextBlock getRawTextBlock}( text ).
+     */
+    public void testRawText()
+    {
+        String text = "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
+        sink.rawText( text );
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getRawTextBlock( text );
+
+        assertEquals( "Wrong rawText!", expected, actual );
+    }
+
+    /**
+     * Checks that the sequence <code>[comment(comment)]</code>,
+     * invoked on the current sink, produces the same result as
+     * {@link #getCommentBlock getCommentBlock}( comment ).
+     * @since 1.1.1
+     */
+    public void testComment()
+    {
+        String comment = "Simple comment with ----";
+        sink.comment( comment );
+        sink.flush();
+        sink.close();
+
+        String actual = testWriter.toString();
+        String expected = getCommentBlock( comment );
+
+        assertEquals( "Wrong comment!", expected, actual );
+    }
+
+    // ----------------------------------------------------------------------
+    // Utility methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Returns the sink that is currently being tested.
+     * @return The current test sink.
+     */
+    protected Sink getSink()
+    {
+        return sink;
+    }
+
+    /**
+     * Returns a String representation of all events that have been written to the sink.
+     * @return The Sink content as a String.
+     */
+    protected String getSinkContent()
+    {
+        return testWriter.toString();
+    }
+
+    /**
+     * Returns the directory where all sink test output will go.
+     * @return The test output directory.
+     */
+    protected String getOutputDir()
+    {
+        return "sink/";
+    }
+
+    // ----------------------------------------------------------------------
+    // Abstract methods the individual SinkTests must provide
+    // ----------------------------------------------------------------------
+
+    /**
+     * This method allows to use the correct Writer in {@link #testTestDocument()}.
+     *
+     * @return <code>true</code> if the Sink is an XML one, <code>false</code> otherwise.
+     * @see #testTestDocument()
+     */
+    protected abstract boolean isXmlSink();
+
+    /**
+     * Return a new instance of the sink that is being tested.
+     * @param writer The writer for the sink.
+     * @return A new sink.
+     */
+    protected abstract Sink createSink( Writer writer );
+
+    /**
+     * Returns a title block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a title block on the current sink.
+     * @see #testTitle()
+     */
+    protected abstract String getTitleBlock( String title );
+
+    /**
+     * Returns an author block generated by this sink.
+     * @param author The author to use.
+     * @return The result of invoking an author block on the current sink.
+     * @see #testAuthor()
+     */
+    protected abstract String getAuthorBlock( String author );
+
+    /**
+     * Returns a date block generated by this sink.
+     * @param date The date to use.
+     * @return The result of invoking a date block on the current sink.
+     * @see #testDate()
+     */
+    protected abstract String getDateBlock( String date );
+
+    /**
+     * Returns a head block generated by this sink.
+     * @return The result of invoking a head block on the current sink.
+     * @see #testHead()
+     */
+    protected abstract String getHeadBlock();
+
+    /**
+     * Returns a body block generated by this sink.
+     * @return The result of invoking a body block on the current sink.
+     * @see #testBody()
+     */
+    protected abstract String getBodyBlock();
+
+    /**
+     * Returns a SectionTitle block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a SectionTitle block on the current sink.
+     * @see #testSectionTitle()
+     */
+    protected abstract String getSectionTitleBlock( String title );
+
+    /**
+     * Returns a Section1 block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a Section1 block on the current sink.
+     * @see #testSection1()
+     */
+    protected abstract String getSection1Block( String title );
+
+    /**
+     * Returns a Section2 block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a Section2 block on the current sink.
+     * @see #testSection2()
+     */
+    protected abstract String getSection2Block( String title );
+
+    /**
+     * Returns a Section3 block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a Section3 block on the current sink.
+     * @see #testSection3()
+     */
+    protected abstract String getSection3Block( String title );
+
+    /**
+     * Returns a Section4 block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a Section4 block on the current sink.
+     * @see #testSection4()
+     */
+    protected abstract String getSection4Block( String title );
+
+    /**
+     * Returns a Section5 block generated by this sink.
+     * @param title The title to use.
+     * @return The result of invoking a Section5 block on the current sink.
+     * @see #testSection5()
+     */
+    protected abstract String getSection5Block( String title );
+
+    /**
+     * Returns a list block generated by this sink.
+     * @param item The item to use.
+     * @return The result of invoking a list block on the current sink.
+     * @see #testList()
+     */
+    protected abstract String getListBlock( String item );
+
+    /**
+     * Returns a NumberedList block generated by this sink.
+     * @param item The item to use.
+     * @return The result of invoking a NumberedList block on the current sink.
+     * @see #testNumberedList()
+     */
+    protected abstract String getNumberedListBlock( String item );
+
+    /**
+     * Returns a DefinitionList block generated by this sink.
+     * @param definum The term to define.
+     * @param definition The definition.
+     * @return The result of invoking a DefinitionList block on the current sink.
+     * @see #testDefinitionList()
+     */
+    protected abstract String getDefinitionListBlock( String definum,
+        String definition );
+
+    /**
+     * Returns a Figure block generated by this sink.
+     * @param source The figure source string.
+     * @param caption The caption to use (may be null).
+     * @return The result of invoking a Figure block on the current sink.
+     * @see #testFigure()
+     */
+    protected abstract String getFigureBlock( String source, String caption );
+
+    /**
+     * Returns a Table block generated by this sink.
+     * @param cell A tabel cell to use.
+     * @param caption The caption to use (may be null).
+     * @return The result of invoking a Table block on the current sink.
+     * @see #testTable()
+     */
+    protected abstract String getTableBlock( String cell, String caption );
+
+    /**
+     * Returns a Paragraph block generated by this sink.
+     * @param text The text to use.
+     * @return The result of invoking a Paragraph block on the current sink.
+     * @see #testParagraph()
+     */
+    protected abstract String getParagraphBlock( String text );
+
+    /**
+     * Returns a Verbatim block generated by this sink.
+     * @param text The text to use.
+     * @return The result of invoking a Verbatim block on the current sink.
+     * @see #testVerbatim()
+     */
+    protected abstract String getVerbatimBlock( String text );
+
+    /**
+     * Returns a HorizontalRule block generated by this sink.
+     * @return The result of invoking a HorizontalRule block on the current sink.
+     * @see #testHorizontalRule()
+     */
+    protected abstract String getHorizontalRuleBlock();
+
+    /**
+     * Returns a PageBreak block generated by this sink.
+     * @return The result of invoking a PageBreak block on the current sink.
+     * @see #testPageBreak()
+     */
+    protected abstract String getPageBreakBlock();
+
+    /**
+     * Returns a Anchor block generated by this sink.
+     * @param anchor The anchor to use.
+     * @return The result of invoking a Anchor block on the current sink.
+     * @see #testAnchor()
+     */
+    protected abstract String getAnchorBlock( String anchor );
+
+    /**
+     * Returns a Link block generated by this sink.
+     * @param link The link to use.
+     * @param text The link text.
+     * @return The result of invoking a Link block on the current sink.
+     * @see #testLink()
+     */
+    protected abstract String getLinkBlock( String link, String text );
+
+    /**
+     * Returns a Italic block generated by this sink.
+     * @param text The text to use.
+     * @return The result of invoking a Italic block on the current sink.
+     * @see #testItalic()
+     */
+    protected abstract String getItalicBlock( String text );
+
+    /**
+     * Returns a Bold block generated by this sink.
+     * @param text The text to use.
+     * @return The result of invoking a Bold block on the current sink.
+     * @see #testBold()
+     */
+    protected abstract String getBoldBlock( String text );
+
+    /**
+     * Returns a Monospaced block generated by this sink.
+     * @param text The text to use.
+     * @return The result of invoking a Monospaced block on the current sink.
+     * @see #testMonospaced()
+     */
+    protected abstract String getMonospacedBlock( String text );
+
+    /**
+     * Returns a LineBreak block generated by this sink.
+     * @return The result of invoking a LineBreak block on the current sink.
+     * @see #testLineBreak()
+     */
+    protected abstract String getLineBreakBlock();
+
+    /**
+     * Returns a NonBreakingSpace block generated by this sink.
+     * @return The result of invoking a NonBreakingSpace block
+     * on the current sink.
+     * @see #testNonBreakingSpace()
+     */
+    protected abstract String getNonBreakingSpaceBlock();
+
+    /**
+     * Returns a Text block generated by this sink.
+     * @param text The text to use.
+     * @return The result of invoking a Text block on the current sink.
+     * @see #testText()
+     */
+    protected abstract String getTextBlock( String text );
+
+    /**
+     * Returns a RawText block generated by this sink.
+     * @param text The text to use.
+     * @return The result of invoking a RawText block on the current sink.
+     * @see #testRawText()
+     */
+    protected abstract String getRawTextBlock( String text );
+
+    /**
+     * Returns a comment block generated by this sink.
+     * @param text The text to use.
+     * @return The result of invoking a comment block on the current sink.
+     * @see #testComment()
+     * @since 1.1.1
+     */
+    protected abstract String getCommentBlock( String text );
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractSinkTestCase.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractSinkTestCase.java
new file mode 100644
index 0000000..e58b477
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractSinkTestCase.java
@@ -0,0 +1,147 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+
+import org.apache.maven.doxia.parser.Parser;
+import org.codehaus.plexus.PlexusTestCase;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: AbstractSinkTestCase.java 747735 2009-02-25 10:43:09Z ltheussl $
+ * @since 1.0
+ */
+public abstract class AbstractSinkTestCase
+    extends PlexusTestCase
+{
+    private Writer testWriter;
+
+    // ---------------------------------------------------------------------
+    // Test case
+    // ----------------------------------------------------------------------
+
+    /**
+     * Parses the test apt document (obtained via {@link #getTestReader()}) with the Parser returned
+     * by {@link #createParser()} into the Sink returned by {@link #createSink()}.
+     *
+     * @throws java.lang.Exception if anything goes wrong.
+     */
+    public void testApt()
+        throws Exception
+    {
+        Parser parser = createParser();
+
+        parser.parse( getTestReader(), createSink() );
+    }
+
+    // ----------------------------------------------------------------------
+    // Abstract methods the individual SinkTests must provide
+    // ----------------------------------------------------------------------
+
+    /**
+     * Return the default extension of files created by the test Sink.
+     *
+     * @return the extension of files created by the test Sink.
+     * @see #createSink()
+     */
+    protected abstract String outputExtension();
+
+    /**
+     * Return a Parser for testing.
+     *
+     * @return a test Parser.
+     */
+    protected abstract Parser createParser();
+
+    /**
+     * Return a Sink for testing.
+     *
+     * @return a test Sink.
+     * @throws java.lang.Exception if the Sink cannot be constructed.
+     */
+    protected abstract Sink createSink()
+        throws Exception;
+
+    // ----------------------------------------------------------------------
+    // Methods for creating the test reader and writer
+    // ----------------------------------------------------------------------
+
+    /**
+     * Returns a Writer to write a test output result. The Writer writes to a File
+     * <code>"target/output/test. + extension"</code>, where extension is returned by
+     * {@link #outputExtension()}, in the current base directory.
+     *
+     * @return a Writer to write a test output result.
+     * @throws java.lang.Exception if the Writer cannot be constructed.
+     */
+    protected Writer getTestWriter()
+        throws Exception
+    {
+        if ( testWriter == null )
+        {
+            File outputDirectory = new File( getBasedirFile(), "target/output" );
+
+            if ( !outputDirectory.exists() )
+            {
+                outputDirectory.mkdirs();
+            }
+
+            testWriter = new FileWriter( new File( outputDirectory, "test." + outputExtension() ) );
+        }
+
+        return testWriter;
+    }
+
+    /**
+     * Returns a Reader that gives access to a common test apt file.
+     *
+     * @return a Reader to access the test apt resource file.
+     * @throws java.lang.Exception if the Reader cannot be constructed.
+     */
+    protected Reader getTestReader()
+        throws Exception
+    {
+        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( "test.apt" );
+
+        InputStreamReader reader = new InputStreamReader( is );
+
+        return reader;
+    }
+
+    // ----------------------------------------------------------------------
+    // Utility methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Return the current base diretory as a File.
+     *
+     * @return the current base diretory as a File.
+     */
+    public File getBasedirFile()
+    {
+        return new File( getBasedir() );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractXmlSinkTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractXmlSinkTest.java
new file mode 100644
index 0000000..c4dba14
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/AbstractXmlSinkTest.java
@@ -0,0 +1,154 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author ltheussl
+ */
+public class AbstractXmlSinkTest
+        extends TestCase
+{
+    /**
+     * Test of set/getNameSpace method, of class AbstractXmlSink.
+     */
+    public void testNameSpace()
+    {
+        final Tag t = Tag.A;
+        final String ns = "ns";
+        final XmlTestSink instance = new XmlTestSink();
+
+        instance.writeStartTag( t );
+        instance.writeEndTag( t );
+        assertEquals( "<a></a>", instance.getText() );
+
+        instance.writeSimpleTag( t );
+        assertEquals( "<a />", instance.getText() );
+
+        instance.setNameSpace( ns );
+
+        instance.writeStartTag( t );
+        instance.writeEndTag( t );
+        assertEquals( "<ns:a></ns:a>", instance.getText() );
+
+        instance.writeSimpleTag( t );
+        assertEquals( "<ns:a />", instance.getText() );
+
+        assertEquals( ns, instance.getNameSpace() );
+
+        try
+        {
+            instance.writeStartTag( null );
+            fail( "null tag should fail!" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            instance.writeEndTag( null );
+            fail( "null tag should fail!" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+    /**
+     * Test of writeStartTag method, of class AbstractXmlSink.
+     */
+    public void testWriteStartTag()
+    {
+        final Tag t = Tag.A;
+        final SinkEventAttributes att = new SinkEventAttributeSet( SinkEventAttributeSet.BOLD );
+        final XmlTestSink instance = new XmlTestSink();
+
+        instance.writeStartTag( t );
+        assertEquals( "<a>", instance.getText() );
+
+        instance.writeStartTag( t, att );
+        assertEquals( "<a style=\"bold\">", instance.getText() );
+
+        instance.writeStartTag( t, att, false );
+        assertEquals( "<a style=\"bold\">", instance.getText() );
+
+        instance.writeStartTag( t, att, true );
+        assertEquals( "<a style=\"bold\" />", instance.getText() );
+    }
+
+    /**
+     * Test of writeEOL method, of class AbstractXmlSink.
+     */
+    public void testWriteEOL()
+    {
+        final XmlTestSink instance = new XmlTestSink();
+
+        instance.writeEOL();
+        assertEquals( System.getProperty( "line.separator" ), instance.getText() );
+    }
+
+    /**
+     * Test of writeSimpleTag method, of class AbstractXmlSink.
+     */
+    public void testWriteSimpleTag()
+    {
+        final Tag t = Tag.A;
+        final SinkEventAttributes att = new SinkEventAttributeSet( SinkEventAttributeSet.BOLD );
+        final XmlTestSink instance = new XmlTestSink();
+
+        instance.writeSimpleTag( t );
+        assertEquals( "<a />", instance.getText() );
+
+        instance.writeSimpleTag( t, att );
+        assertEquals( "<a style=\"bold\" />", instance.getText() );
+    }
+
+    /** Test sink. */
+    private class XmlTestSink
+            extends AbstractXmlSink
+    {
+        private final StringBuffer buffer = new StringBuffer( 0 );
+
+        public void reset()
+        {
+            buffer.setLength( 0 );
+        }
+
+        public String getText()
+        {
+            String text = buffer.toString();
+            reset();
+
+            return text;
+        }
+
+        protected void write( String text )
+        {
+            buffer.append( text );
+        }
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkAdapterTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkAdapterTest.java
new file mode 100644
index 0000000..07e06e4
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkAdapterTest.java
@@ -0,0 +1,554 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author ltheussl
+ */
+public class SinkAdapterTest
+        extends TestCase
+{
+    private final SinkAdapter instance = new SinkAdapter();
+
+    /**
+     * Test of head method, of class SinkAdapter.
+     */
+    public void testHead()
+    {
+        instance.head();
+        instance.head( null );
+        instance.head_();
+    }
+
+    /**
+     * Test of body method, of class SinkAdapter.
+     */
+    public void testBody()
+    {
+        instance.body();
+        instance.body( null );
+        instance.body_();
+    }
+
+    /**
+     * Test of section1 method, of class SinkAdapter.
+     */
+    public void testSection1()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_1;
+        instance.section1();
+        instance.section1_();
+        instance.section( level, null );
+        instance.section_( level );
+    }
+
+    /**
+     * Test of section2 method, of class SinkAdapter.
+     */
+    public void testSection2()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_2;
+        instance.section2();
+        instance.section2_();
+        instance.section( level, null );
+        instance.section_( level );
+    }
+
+    /**
+     * Test of section3 method, of class SinkAdapter.
+     */
+    public void testSection3()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_3;
+        instance.section3();
+        instance.section3_();
+        instance.section( level, null );
+        instance.section_( level );
+    }
+
+    /**
+     * Test of section4 method, of class SinkAdapter.
+     */
+    public void testSection4()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_4;
+        instance.section4();
+        instance.section4_();
+        instance.section( level, null );
+        instance.section_( level );
+    }
+
+    /**
+     * Test of section5 method, of class SinkAdapter.
+     */
+    public void testSection5()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_5;
+        instance.section5();
+        instance.section5_();
+        instance.section( level, null );
+        instance.section_( level );
+    }
+
+    /**
+     * Test of list method, of class SinkAdapter.
+     */
+    public void testList()
+    {
+        instance.list();
+        instance.list( null );
+        instance.list_();
+    }
+
+    /**
+     * Test of listItem method, of class SinkAdapter.
+     */
+    public void testListItem()
+    {
+        instance.listItem();
+        instance.listItem( null );
+        instance.listItem_();
+    }
+
+    /**
+     * Test of numberedList method, of class SinkAdapter.
+     */
+    public void testNumberedList()
+    {
+        final int numbering = SinkAdapter.NUMBERING_DECIMAL;
+        instance.numberedList( numbering );
+        instance.numberedList( numbering, null );
+        instance.numberedList_();
+    }
+
+    /**
+     * Test of numberedListItem method, of class SinkAdapter.
+     */
+    public void testNumberedListItem()
+    {
+        instance.numberedListItem();
+        instance.numberedListItem( null );
+        instance.numberedListItem_();
+    }
+
+    /**
+     * Test of definitionList method, of class SinkAdapter.
+     */
+    public void testDefinitionList()
+    {
+        instance.definitionList();
+        instance.definitionList( null );
+        instance.definitionList_();
+    }
+
+    /**
+     * Test of definitionListItem method, of class SinkAdapter.
+     */
+    public void testDefinitionListItem()
+    {
+        instance.definitionListItem();
+        instance.definitionListItem( null );
+        instance.definitionListItem_();
+    }
+
+    /**
+     * Test of definition method, of class SinkAdapter.
+     */
+    public void testDefinition()
+    {
+        instance.definition();
+        instance.definition( null );
+        instance.definition_();
+    }
+
+    /**
+     * Test of figure method, of class SinkAdapter.
+     */
+    public void testFigure()
+    {
+        instance.figure();
+        instance.figure( null );
+        instance.figure_();
+    }
+
+    /**
+     * Test of table method, of class SinkAdapter.
+     */
+    public void testTable()
+    {
+        instance.table();
+        instance.table( null );
+        instance.table_();
+    }
+
+    /**
+     * Test of tableRows method, of class SinkAdapter.
+     */
+    public void testTableRows()
+    {
+        final int[] justification = null;
+        final boolean grid = false;
+        instance.tableRows( justification, grid );
+        instance.tableRows_();
+    }
+
+    /**
+     * Test of tableRow method, of class SinkAdapter.
+     */
+    public void testTableRow()
+    {
+        instance.tableRow();
+        instance.tableRow( null );
+        instance.tableRow_();
+    }
+
+    /**
+     * Test of title method, of class SinkAdapter.
+     */
+    public void testTitle()
+    {
+        instance.title();
+        instance.title( null );
+        instance.title_();
+    }
+
+    /**
+     * Test of author method, of class SinkAdapter.
+     */
+    public void testAuthor()
+    {
+        instance.author();
+        instance.author( null );
+        instance.author_();
+    }
+
+    /**
+     * Test of date method, of class SinkAdapter.
+     */
+    public void testDate()
+    {
+        instance.date();
+        instance.date( null );
+        instance.date_();
+    }
+
+    /**
+     * Test of sectionTitle method, of class SinkAdapter.
+     */
+    public void testSectionTitle()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_1;
+        instance.sectionTitle();
+        instance.sectionTitle_();
+        instance.sectionTitle( level, null );
+        instance.sectionTitle_( level );
+    }
+
+    /**
+     * Test of sectionTitle1 method, of class SinkAdapter.
+     */
+    public void testSectionTitle1()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_1;
+        instance.sectionTitle1();
+        instance.sectionTitle1_();
+        instance.sectionTitle( level, null );
+        instance.sectionTitle_( level );
+    }
+
+    /**
+     * Test of sectionTitle2 method, of class SinkAdapter.
+     */
+    public void testSectionTitle2()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_2;
+        instance.sectionTitle2();
+        instance.sectionTitle2_();
+        instance.sectionTitle( level, null );
+        instance.sectionTitle_( level );
+    }
+
+    /**
+     * Test of sectionTitle3 method, of class SinkAdapter.
+     */
+    public void testSectionTitle3()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_3;
+        instance.sectionTitle3();
+        instance.sectionTitle3_();
+        instance.sectionTitle( level, null );
+        instance.sectionTitle_( level );
+    }
+
+    /**
+     * Test of sectionTitle4 method, of class SinkAdapter.
+     */
+    public void testSectionTitle4()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_4;
+        instance.sectionTitle4();
+        instance.sectionTitle4_();
+        instance.sectionTitle( level, null );
+        instance.sectionTitle_( level );
+    }
+
+    /**
+     * Test of sectionTitle5 method, of class SinkAdapter.
+     */
+    public void testSectionTitle5()
+    {
+        final int level = SinkAdapter.SECTION_LEVEL_5;
+        instance.sectionTitle5();
+        instance.sectionTitle5_();
+        instance.sectionTitle( level, null );
+        instance.sectionTitle_( level );
+    }
+
+    /**
+     * Test of paragraph method, of class SinkAdapter.
+     */
+    public void testParagraph()
+    {
+        instance.paragraph();
+        instance.paragraph( null );
+        instance.paragraph_();
+    }
+
+    /**
+     * Test of verbatim method, of class SinkAdapter.
+     */
+    public void testVerbatim()
+    {
+        instance.verbatim( null );
+        instance.verbatim( false );
+        instance.verbatim_();
+    }
+
+    /**
+     * Test of definedTerm method, of class SinkAdapter.
+     */
+    public void testDefinedTerm()
+    {
+        instance.definedTerm();
+        instance.definedTerm( null );
+        instance.definedTerm_();
+    }
+
+    /**
+     * Test of figureCaption method, of class SinkAdapter.
+     */
+    public void testFigureCaption()
+    {
+        instance.figureCaption();
+        instance.figureCaption( null );
+        instance.figureCaption_();
+    }
+
+    /**
+     * Test of tableCell method, of class SinkAdapter.
+     */
+    public void testTableCell()
+    {
+        instance.tableCell();
+        instance.tableCell( (SinkEventAttributes) null );
+        instance.tableCell( (String) null );
+        instance.tableCell_();
+    }
+
+    /**
+     * Test of tableHeaderCell method, of class SinkAdapter.
+     */
+    public void testTableHeaderCell()
+    {
+        instance.tableHeaderCell();
+        instance.tableHeaderCell( (SinkEventAttributes) null );
+        instance.tableHeaderCell( (String) null );
+        instance.tableHeaderCell_();
+    }
+
+    /**
+     * Test of tableCaption method, of class SinkAdapter.
+     */
+    public void testTableCaption()
+    {
+        instance.tableCaption();
+        instance.tableCaption( null );
+        instance.tableCaption_();
+    }
+
+    /**
+     * Test of figureGraphics method, of class SinkAdapter.
+     */
+    public void testFigureGraphics()
+    {
+        String name = "";
+        instance.figureGraphics( name );
+        instance.figureGraphics( name, null );
+    }
+
+    /**
+     * Test of horizontalRule method, of class SinkAdapter.
+     */
+    public void testHorizontalRule()
+    {
+        instance.horizontalRule();
+        instance.horizontalRule( null );
+    }
+
+    /**
+     * Test of pageBreak method, of class SinkAdapter.
+     */
+    public void testPageBreak()
+    {
+        instance.pageBreak();
+    }
+
+    /**
+     * Test of anchor method, of class SinkAdapter.
+     */
+    public void testAnchor()
+    {
+        String name = "";
+        instance.anchor( name );
+        instance.anchor( name, null );
+        instance.anchor_();
+    }
+
+    /**
+     * Test of link method, of class SinkAdapter.
+     */
+    public void testLink()
+    {
+        String name = "";
+        instance.link( name );
+        instance.link( name, null );
+        instance.link_();
+    }
+
+    /**
+     * Test of italic method, of class SinkAdapter.
+     */
+    public void testItalic()
+    {
+        instance.italic();
+        instance.italic_();
+    }
+
+    /**
+     * Test of bold method, of class SinkAdapter.
+     */
+    public void testBold()
+    {
+        instance.bold();
+        instance.bold_();
+    }
+
+    /**
+     * Test of monospaced method, of class SinkAdapter.
+     */
+    public void testMonospaced()
+    {
+        instance.monospaced();
+        instance.monospaced_();
+    }
+
+    /**
+     * Test of lineBreak method, of class SinkAdapter.
+     */
+    public void testLineBreaks()
+    {
+        instance.lineBreak();
+        instance.lineBreak( null );
+    }
+
+    /**
+     * Test of nonBreakingSpace method, of class SinkAdapter.
+     */
+    public void testNonBreakingSpace()
+    {
+        instance.nonBreakingSpace();
+    }
+
+    /**
+     * Test of text method, of class SinkAdapter.
+     */
+    public void testText()
+    {
+        String text = "";
+        instance.text( text );
+        instance.text( text, null );
+    }
+
+    /**
+     * Test of rawText method, of class SinkAdapter.
+     */
+    public void testRawText()
+    {
+        String text = "";
+        instance.rawText( text );
+    }
+
+    /**
+     * Test of comment method, of class SinkAdapter.
+     */
+    public void testComment()
+    {
+        instance.comment( "" );
+    }
+
+    /**
+     * Test of flush method, of class SinkAdapter.
+     */
+    public void testFlush()
+    {
+        instance.flush();
+    }
+
+    /**
+     * Test of close method, of class SinkAdapter.
+     */
+    public void testClose()
+    {
+        instance.close();
+    }
+
+    /**
+     * Test of section method, of class SinkAdapter.
+     */
+    public void testSection()
+    {
+        int level = 0;
+        instance.section( level, null );
+        instance.section_( level );
+    }
+
+    /**
+     * Test of unknown method, of class SinkAdapter.
+     */
+    public void testUnknown()
+    {
+        String name = "";
+        Object[] requiredParams = null;
+        instance.unknown( name, requiredParams, null );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventAttributeSetTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventAttributeSetTest.java
new file mode 100644
index 0000000..53534aa
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventAttributeSetTest.java
@@ -0,0 +1,281 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Enumeration;
+
+import javax.swing.text.AttributeSet;
+
+import junit.framework.TestCase;
+
+/**
+ * Test SinkEventAttributeSet.
+ *
+ * @author ltheussl
+ */
+public class SinkEventAttributeSetTest extends TestCase
+{
+
+    private SinkEventAttributeSet sinkEventAttributeSet;
+
+    /**
+     * @throws java.lang.Exception if any.
+     */
+    protected void setUp()
+            throws Exception
+    {
+        super.setUp();
+        this.sinkEventAttributeSet = new SinkEventAttributeSet();
+    }
+
+    /**
+     * Test of constructors, of class SinkEventAttributeSet.
+     */
+    public void testConstructor()
+    {
+        try
+        {
+            SinkEventAttributeSet aset = new SinkEventAttributeSet( new String[] {"key"} );
+            fail( "missing attribute value!" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+    /**
+     * Test of isEmpty method, of class SinkEventAttributeSet.
+     */
+    public void testIsEmpty()
+    {
+        assertTrue( sinkEventAttributeSet.isEmpty() );
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.BOLD );
+        assertFalse( sinkEventAttributeSet.isEmpty() );
+    }
+
+    /**
+     * Test of getAttributeCount method, of class SinkEventAttributeSet.
+     */
+    public void testGetAttributeCount()
+    {
+        assertEquals( 0, sinkEventAttributeSet.getAttributeCount() );
+        sinkEventAttributeSet.addAttribute( "name1", "value1" );
+        assertEquals( 1, sinkEventAttributeSet.getAttributeCount() );
+        sinkEventAttributeSet.removeAttribute( "name2" );
+        assertEquals( 1, sinkEventAttributeSet.getAttributeCount() );
+        sinkEventAttributeSet.removeAttribute( "name1" );
+        assertEquals( 0, sinkEventAttributeSet.getAttributeCount() );
+
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.BOLD );
+        assertEquals( 1, sinkEventAttributeSet.getAttributeCount() );
+        sinkEventAttributeSet.removeAttributes( SinkEventAttributeSet.BOXED );
+        assertEquals( 1, sinkEventAttributeSet.getAttributeCount() );
+        sinkEventAttributeSet.removeAttributes( SinkEventAttributeSet.BOLD );
+        assertEquals( 0, sinkEventAttributeSet.getAttributeCount() );
+    }
+
+    /**
+     * Test of isDefined method, of class SinkEventAttributeSet.
+     */
+    public void testIsDefined()
+    {
+        assertFalse( sinkEventAttributeSet.isDefined( SinkEventAttributes.DECORATION ) );
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.BOXED );
+        assertTrue( sinkEventAttributeSet.isDefined( SinkEventAttributes.DECORATION ) );
+    }
+
+    /**
+     * Test of isEqual method, of class SinkEventAttributeSet.
+     */
+    public void testIsEqual()
+    {
+        SinkEventAttributes instance = new SinkEventAttributeSet( SinkEventAttributeSet.BOLD );
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.BOLD );
+        assertTrue( instance.isEqual( sinkEventAttributeSet ) );
+        instance.addAttributes( SinkEventAttributeSet.BOXED );
+        assertFalse( instance.isEqual( sinkEventAttributeSet ) );
+    }
+
+    /**
+     * Test of equals method, of class SinkEventAttributeSet.
+     */
+    public void testEquals()
+    {
+        assertFalse( sinkEventAttributeSet.equals( null ) );
+        assertTrue( sinkEventAttributeSet.equals( sinkEventAttributeSet ) );
+
+        SinkEventAttributes instance = new SinkEventAttributeSet( SinkEventAttributeSet.BOLD );
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.BOLD );
+        assertTrue( instance.equals( sinkEventAttributeSet ) );
+        instance.addAttributes( SinkEventAttributeSet.BOXED );
+        assertFalse( instance.equals( sinkEventAttributeSet ) );
+    }
+
+    /**
+     * Test of copyAttributes method, of class SinkEventAttributeSet.
+     */
+    public void testCopyAttributes()
+    {
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.ITALIC );
+        AttributeSet instance = sinkEventAttributeSet.copyAttributes();
+        assertTrue( instance.isEqual( sinkEventAttributeSet ) );
+    }
+
+    /**
+     * Test of getAttributeNames method, of class SinkEventAttributeSet.
+     */
+    public void testGetAttributeNames()
+    {
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.UNDERLINE );
+        Enumeration result = sinkEventAttributeSet.getAttributeNames();
+        assertEquals( "decoration", result.nextElement() );
+        assertFalse( result.hasMoreElements() );
+    }
+
+    /**
+     * Test of getAttribute method, of class SinkEventAttributeSet.
+     */
+    public void testGetAttribute()
+    {
+        sinkEventAttributeSet.addAttribute( "key", "value" );
+        assertTrue( sinkEventAttributeSet.getAttribute( "key" ).equals( "value" ) );
+        assertNull( sinkEventAttributeSet.getAttribute( "bla" ) );
+    }
+
+    /**
+     * Test of containsAttribute method, of class SinkEventAttributeSet.
+     */
+    public void testContainsAttribute()
+    {
+        sinkEventAttributeSet.addAttribute( "key", "value" );
+        assertTrue( sinkEventAttributeSet.containsAttribute( "key", "value" ) );
+        assertFalse( sinkEventAttributeSet.containsAttribute( "key", "valu" ) );
+    }
+
+    /**
+     * Test of containsAttributes method, of class SinkEventAttributeSet.
+     */
+    public void testContainsAttributes()
+    {
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.JUSTIFY );
+        assertTrue( sinkEventAttributeSet.containsAttributes( SinkEventAttributeSet.JUSTIFY ) );
+        assertFalse( sinkEventAttributeSet.containsAttributes( SinkEventAttributeSet.BOXED ) );
+    }
+
+    /**
+     * Test of addAttribute method, of class SinkEventAttributeSet.
+     */
+    public void testAddAttribute()
+    {
+        assertFalse( sinkEventAttributeSet.containsAttribute( "key", "value" ) );
+        sinkEventAttributeSet.addAttribute( "key", "value" );
+        assertTrue( sinkEventAttributeSet.containsAttribute( "key", "value" ) );
+        sinkEventAttributeSet.removeAttribute( "key" );
+        assertFalse( sinkEventAttributeSet.containsAttribute( "key", "value" ) );
+    }
+
+    /**
+     * Test of add/removeAttributes methods, of class SinkEventAttributeSet.
+     */
+    public void testAddAttributes()
+    {
+        assertFalse( sinkEventAttributeSet.containsAttributes( SinkEventAttributeSet.JUSTIFY ) );
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.JUSTIFY );
+        assertTrue( sinkEventAttributeSet.containsAttributes( SinkEventAttributeSet.JUSTIFY ) );
+
+        sinkEventAttributeSet.removeAttributes( SinkEventAttributeSet.JUSTIFY );
+        assertFalse( sinkEventAttributeSet.containsAttributes( SinkEventAttributeSet.JUSTIFY ) );
+
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.JUSTIFY );
+        sinkEventAttributeSet.removeAttributes( SinkEventAttributeSet.JUSTIFY.getAttributeNames() );
+        assertFalse( sinkEventAttributeSet.containsAttributes( SinkEventAttributeSet.JUSTIFY ) );
+
+        sinkEventAttributeSet.setResolveParent( SinkEventAttributeSet.JUSTIFY );
+        assertTrue( sinkEventAttributeSet.containsAttributes( SinkEventAttributeSet.JUSTIFY ) );
+
+        sinkEventAttributeSet.removeAttributes( (AttributeSet) null ); // should do nothing
+    }
+
+    /**
+     * Test of getResolveParent method, of class SinkEventAttributeSet.
+     */
+    public void testGetResolveParent()
+    {
+        assertNull( sinkEventAttributeSet.getResolveParent() );
+        sinkEventAttributeSet.setResolveParent( SinkEventAttributeSet.CENTER );
+        assertNotNull( sinkEventAttributeSet.getResolveParent() );
+    }
+
+    /**
+     * Test of clone method, of class SinkEventAttributeSet.
+     */
+    public void testClone()
+    {
+        Object result = sinkEventAttributeSet.clone();
+        assertTrue( sinkEventAttributeSet.equals( result ) );
+
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.MONOSPACED );
+        assertFalse( sinkEventAttributeSet.equals( result ) );
+
+        result = sinkEventAttributeSet.clone();
+        assertTrue( sinkEventAttributeSet.equals( result ) );
+        sinkEventAttributeSet.setResolveParent( SinkEventAttributeSet.CENTER );
+        //assertFalse( sinkEventAttributeSet.equals( result ) );
+
+        result = sinkEventAttributeSet.clone();
+        assertTrue( sinkEventAttributeSet.equals( result ) );
+        sinkEventAttributeSet.setResolveParent( SinkEventAttributeSet.BOXED );
+        //assertFalse( sinkEventAttributeSet.equals( result ) );
+    }
+
+    /**
+     * Test of hashCode method, of class SinkEventAttributeSet.
+     */
+    public void testHashCode()
+    {
+        int oldValue = sinkEventAttributeSet.hashCode();
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.BOLD );
+        int newValue = sinkEventAttributeSet.hashCode();
+        assertFalse( oldValue == newValue );
+
+        oldValue = newValue;
+        sinkEventAttributeSet.setResolveParent( SinkEventAttributeSet.CENTER );
+        newValue = sinkEventAttributeSet.hashCode();
+        assertFalse( oldValue == newValue );
+    }
+
+    /**
+     * Test of toString method, of class SinkEventAttributeSet.
+     */
+    public void testToString()
+    {
+        String expected = "";
+        assertEquals( expected, sinkEventAttributeSet.toString() );
+
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.BOXED );
+        expected = " decoration=boxed";
+        assertEquals( expected, sinkEventAttributeSet.toString() );
+
+        sinkEventAttributeSet.addAttributes( SinkEventAttributeSet.CENTER );
+        expected = " decoration=boxed align=center";
+        assertEquals( expected, sinkEventAttributeSet.toString() );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventElement.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventElement.java
new file mode 100644
index 0000000..51b4177
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventElement.java
@@ -0,0 +1,86 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+/**
+ * A single sink event, used for testing purposes in order to check
+ * the order and effect of some parser events.
+ *
+ * @author ltheussl
+ * @version $Id: SinkEventElement.java 773849 2009-05-12 11:03:08Z vsiveton $
+ * @since 1.1
+ */
+public class SinkEventElement
+{
+    /** The name of the sink event, ie the sink method name. */
+    private final String methodName;
+
+    /** The array of arguments to the sink method. */
+    private final Object[] args;
+
+    /**
+     * A SinkEventElement is characterized by the method name and associated array of arguments.
+     *
+     * @param name The name of the sink event, ie the sink method name.
+     * @param arguments The array of arguments to the sink method.
+     *      For a no-arg element this may be null or an empty array.
+     */
+    public SinkEventElement( String name, Object[] arguments )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Element name can't be null!" );
+        }
+
+        this.methodName = name;
+        this.args = arguments;
+    }
+
+    /**
+     * Return the name of the this event.
+     *
+     * @return The name of the sink event.
+     */
+    public String getName()
+    {
+        return this.methodName;
+    }
+
+    /**
+     * Return the array of arguments to the sink method.
+     *
+     * @return the array of arguments to the sink method.
+     */
+    public Object[] getArgs()
+    {
+        return this.args;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @since 1.1.1
+     */
+    public String toString()
+    {
+        return ToStringBuilder.reflectionToString( this );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventTestingSink.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventTestingSink.java
new file mode 100644
index 0000000..ce5e36f
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkEventTestingSink.java
@@ -0,0 +1,823 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This sink is used for testing purposes in order to check wether
+ * the input of some parser is well-formed.
+ *
+ * @author ltheussl
+ * @version $Id: SinkEventTestingSink.java 759581 2009-03-28 20:40:16Z ltheussl $
+ * @since 1.1
+ */
+public class SinkEventTestingSink
+    extends AbstractSink
+{
+    /** The list of sink events. */
+    private final List events = new LinkedList();
+
+    /**
+     * Return the collected list of SinkEventElements.
+     *
+     * @return the collected list of SinkEventElements.
+     */
+    public List getEventList()
+    {
+        return this.events;
+    }
+
+    /** Clears the list of sink events. */
+    public void reset()
+    {
+        this.events.clear();
+    }
+
+      //
+     // sink methods
+    //
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        addEvent( "head" );
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        addEvent( "head_" );
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        addEvent( "body" );
+    }
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        addEvent( "body_" );
+    }
+
+    /** {@inheritDoc} */
+    public void section1()
+    {
+        addEvent( "section1" );
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        addEvent( "section1_" );
+    }
+
+    /** {@inheritDoc} */
+    public void section2()
+    {
+        addEvent( "section2" );
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        addEvent( "section2_" );
+    }
+
+    /** {@inheritDoc} */
+    public void section3()
+    {
+        addEvent( "section3" );
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        addEvent( "section3_" );
+    }
+
+    /** {@inheritDoc} */
+    public void section4()
+    {
+        addEvent( "section4" );
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        addEvent( "section4_" );
+    }
+
+    /** {@inheritDoc} */
+    public void section5()
+    {
+        addEvent( "section5" );
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        addEvent( "section5_" );
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        addEvent( "list" );
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        addEvent( "list_" );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        addEvent( "listItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        addEvent( "listItem_" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        addEvent( "numberedList", new Object[] {new Integer( numbering )} );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        addEvent( "numberedList_" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        addEvent( "numberedListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        addEvent( "numberedListItem_" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList()
+    {
+        addEvent( "definitionList" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList_()
+    {
+        addEvent( "definitionList_" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        addEvent( "definitionListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem_()
+    {
+        addEvent( "definitionListItem_" );
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        addEvent( "definition" );
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        addEvent( "definition_" );
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        addEvent( "figure" );
+    }
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        addEvent( "figure_" );
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        addEvent( "table" );
+    }
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        addEvent( "table_" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        addEvent( "tableRows", new Object[] {justification, new Boolean( grid )} );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        addEvent( "tableRows_" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        addEvent( "tableRow" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        addEvent( "tableRow_" );
+    }
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        addEvent( "title" );
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        addEvent( "title_" );
+    }
+
+    /** {@inheritDoc} */
+    public void author()
+    {
+        addEvent( "author" );
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        addEvent( "author_" );
+    }
+
+    /** {@inheritDoc} */
+    public void date()
+    {
+        addEvent( "date" );
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        addEvent( "date_" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle()
+    {
+        addEvent( "sectionTitle" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_()
+    {
+        addEvent( "sectionTitle_" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        addEvent( "sectionTitle1" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        addEvent( "sectionTitle1_" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        addEvent( "sectionTitle2" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        addEvent( "sectionTitle2_" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        addEvent( "sectionTitle3" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        addEvent( "sectionTitle3_" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        addEvent( "sectionTitle4" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        addEvent( "sectionTitle4_" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        addEvent( "sectionTitle5" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        addEvent( "sectionTitle5_" );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph()
+    {
+        addEvent( "paragraph" );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        addEvent( "paragraph_" );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        addEvent( "verbatim", new Object[] {new Boolean( boxed )} );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        addEvent( "verbatim_" );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        addEvent( "definedTerm" );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm_()
+    {
+        addEvent( "definedTerm_" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption()
+    {
+        addEvent( "figureCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        addEvent( "figureCaption_" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        addEvent( "tableCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        addEvent( "tableCell", new Object[] {width} );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        addEvent( "tableCell_" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        addEvent( "tableHeaderCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        addEvent( "tableHeaderCell", new Object[] {width} );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        addEvent( "tableHeaderCell_" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption()
+    {
+        addEvent( "tableCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption_()
+    {
+        addEvent( "tableCaption_" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        addEvent( "figureGraphics", new Object[] {name} );
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        addEvent( "horizontalRule" );
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        addEvent( "pageBreak" );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        addEvent( "anchor", new Object[] {name} );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor_()
+    {
+        addEvent( "anchor_" );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        addEvent( "link", new Object[] {name} );
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        addEvent( "link_" );
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        addEvent( "italic" );
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        addEvent( "italic_" );
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        addEvent( "bold" );
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        addEvent( "bold_" );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        addEvent( "monospaced" );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        addEvent( "monospaced_" );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        addEvent( "lineBreak" );
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        addEvent( "nonBreakingSpace" );
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        addEvent( "text", new Object[] {text} );
+    }
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        addEvent( "rawText", new Object[] {text} );
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+        addEvent( "comment", new Object[] {comment} );
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        addEvent( "flush" );
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        addEvent( "close" );
+    }
+
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        addEvent( "head", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        addEvent( "title", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        addEvent( "author", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        addEvent( "date", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        addEvent( "body", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void section( int level, SinkEventAttributes attributes )
+    {
+        addEvent( "section" + level, new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void section_( int level )
+    {
+        addEvent( "section" + level + "_" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        addEvent( "sectionTitle" + level, new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+
+        addEvent( "sectionTitle" + level + "_" );
+    }
+
+    /** {@inheritDoc} */
+    public void list( SinkEventAttributes attributes )
+    {
+        addEvent( "list", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        addEvent( "listItem", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        addEvent( "numberedList", new Object[] {new Integer( numbering ), attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        addEvent( "numberedListItem", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        addEvent( "definitionList", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem( SinkEventAttributes attributes )
+    {
+        addEvent( "definitionListItem", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void definition( SinkEventAttributes attributes )
+    {
+        addEvent( "definition", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        addEvent( "definedTerm", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void figure( SinkEventAttributes attributes )
+    {
+        addEvent( "figure", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        addEvent( "figureCaption", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        addEvent( "figureGraphics", new Object[] {src, attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        addEvent( "table", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        addEvent( "tableRow", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        addEvent( "tableCell", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        addEvent( "tableHeaderCell", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        addEvent( "tableCaption", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        addEvent( "paragraph", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        addEvent( "verbatim", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        addEvent( "horizontalRule", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        addEvent( "anchor", new Object[] {name, attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        addEvent( "link", new Object[] {name, attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        addEvent( "lineBreak", new Object[] {attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        addEvent( "text", new Object[] {text, attributes} );
+    }
+
+    /** {@inheritDoc} */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        addEvent( "unknown", new Object[] {name, requiredParams, attributes} );
+    }
+
+      //
+     // private
+    //
+
+    /**
+     * Adds a no-arg event to the list of events.
+     *
+     * @param string the name of the event.
+     */
+    private void addEvent( String string )
+    {
+        addEvent( string, null );
+    }
+
+    /**
+     * Adds an event to the list of events.
+     *
+     * @param string the name of the event.
+     * @param arguments The array of arguments to the sink method.
+     */
+    private void addEvent( String string, Object[] arguments )
+    {
+        events.add( new SinkEventElement( string, arguments ) );
+    }
+
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkTestDocument.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkTestDocument.java
new file mode 100644
index 0000000..0596cce
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkTestDocument.java
@@ -0,0 +1,604 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Static methods to generate standard Doxia sink events.
+ *
+ * @version $Id: SinkTestDocument.java 779902 2009-05-29 09:32:47Z ltheussl $
+ */
+public class SinkTestDocument
+{
+
+    /** Private constructor. */
+    private SinkTestDocument()
+    {
+        // do not instantiate
+    }
+
+    /**
+     * Dumps a full model that mimics aptconvert's test.apt,
+     * into the specified sink. The sink is flushed but not closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generate( Sink sink )
+    {
+        generateHead( sink );
+
+        sink.body();
+
+        // TODO: what is this supposed to do?
+        //sink.sectionTitle();
+        //sink.text( "Section Title" );
+        //sink.sectionTitle_();
+
+        sink.paragraph();
+        sink.text( "Paragraph 1, line 1. Paragraph 1, line 2." );
+        sink.paragraph_();
+
+        sink.paragraph();
+        sink.text( "Paragraph 2, line 1. Paragraph 2, line 2." );
+        sink.paragraph_();
+
+        sink.section1();
+        sink.sectionTitle1();
+        sink.text( "Section title" );
+        sink.sectionTitle1_();
+
+        sink.section2();
+        sink.sectionTitle2();
+        sink.text( "Sub-section title" );
+        sink.sectionTitle2_();
+
+        sink.section3();
+        sink.sectionTitle3();
+        sink.text( "Sub-sub-section title" );
+        sink.sectionTitle3_();
+
+        sink.section4();
+        sink.sectionTitle4();
+        sink.text( "Sub-sub-sub-section title" );
+        sink.sectionTitle4_();
+
+        sink.section5();
+        sink.sectionTitle5();
+        sink.text( "Sub-sub-sub-sub-section title" );
+        sink.sectionTitle5_();
+
+        generateList( sink );
+
+        sink.verbatim( SinkEventAttributeSet.BOXED );
+        sink.text( "Verbatim text not contained in list item 3" );
+        sink.verbatim_();
+
+        generateNumberedList( sink );
+
+        sink.paragraph();
+        sink.text( "List numbering schemes: [[1]], [[a]], [[A]], [[i]], [[I]]." );
+        sink.paragraph_();
+
+        generateDefinitionList( sink );
+
+        sink.paragraph();
+        sink.text( "--- instead of +-- suppresses the box around verbatim text." );
+        sink.paragraph_();
+
+        generateFigure( sink );
+
+        generateTable( sink );
+
+        sink.paragraph();
+        sink.text( "No grid, no caption:" );
+        sink.paragraph_();
+
+        generateNoGridTable( sink );
+
+        generateHeaderTable( sink );
+
+        generateHorizontalRule( sink );
+
+        generatePageBreak( sink );
+
+        generateFonts( sink );
+
+        generateAnchors( sink );
+
+        generateLineBreak( sink );
+
+        generateNonBreakingSpace( sink );
+
+        generateSpecialCharacters( sink );
+
+        sink.comment( "A comment!" );
+
+        sink.section5_();
+        sink.section4_();
+        sink.section3_();
+        sink.section2_();
+        sink.section1_();
+
+        sink.body_();
+
+        sink.flush();
+    }
+
+    /**
+     * Dumps a header with title, author and date elements
+     * into the specified sink. The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateHead( Sink sink )
+    {
+        sink.head();
+
+        sink.title();
+        sink.text( "Title" );
+        sink.title_();
+
+        sink.author();
+        sink.text( "Author" );
+        sink.author_();
+
+        sink.date();
+        sink.text( "Date" );
+        sink.date_();
+
+        sink.head_();
+    }
+
+    /**
+     * Dumps a list into the specified sink. The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateList( Sink sink )
+    {
+        sink.list();
+
+        sink.listItem();
+        sink.text( "List item 1." );
+        sink.listItem_();
+
+        sink.listItem();
+        sink.text( "List item 2." );
+        sink.paragraph();
+        sink.text( "Paragraph contained in list item 2." );
+        sink.paragraph_();
+
+        sink.list();
+
+        sink.listItem();
+        sink.text( "Sub-list item 1." );
+        sink.listItem_();
+
+        sink.listItem();
+        sink.text( "Sub-list item 2." );
+        sink.listItem_();
+
+        sink.list_();
+
+        sink.listItem_();
+
+        sink.listItem();
+        sink.text( "List item 3. Force end of list:" );
+        sink.listItem_();
+
+        sink.list_();
+    }
+
+    /**
+     * Dumps a numbered list into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateNumberedList( Sink sink )
+    {
+        sink.numberedList( Sink.NUMBERING_DECIMAL );
+
+        sink.numberedListItem();
+        sink.text( "Numbered item 1." );
+
+        sink.numberedList( Sink.NUMBERING_UPPER_ALPHA );
+
+        sink.numberedListItem();
+        sink.text( "Numbered item A." );
+        sink.numberedListItem_();
+
+        sink.numberedListItem();
+        sink.text( "Numbered item B." );
+        sink.numberedListItem_();
+
+        sink.numberedList_();
+
+        sink.numberedListItem_();
+
+        sink.numberedListItem();
+        sink.text( "Numbered item 2." );
+        sink.numberedListItem_();
+
+        sink.numberedList_();
+    }
+
+    /**
+     * Dumps a definition list into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateDefinitionList( Sink sink )
+    {
+        String eol = System.getProperty( "line.separator" );
+
+        sink.definitionList();
+
+        sink.definitionListItem();
+        sink.definedTerm();
+        sink.text( "Defined term 1" );
+        sink.definedTerm_();
+        sink.definition();
+        sink.text( "of definition list." );
+        sink.definition_();
+        sink.definitionListItem_();
+
+        sink.definitionListItem();
+        sink.definedTerm();
+        sink.text( "Defined term 2" );
+        sink.definedTerm_();
+        sink.definition();
+        sink.text( "of definition list." );
+        sink.verbatim( SinkEventAttributeSet.BOXED );
+        sink.text( "Verbatim text" + eol + "                        in a box        " );
+        sink.verbatim_();
+        sink.definition_();
+        sink.definitionListItem_();
+
+        sink.definitionList_();
+    }
+
+    /**
+     * Dumps a figure with figure caption into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateFigure( Sink sink )
+    {
+        sink.figure( null );
+
+        sink.figureGraphics( "figure.png", null );
+
+        sink.figureCaption( null );
+        sink.text( "Figure caption", null );
+        sink.figureCaption_();
+
+        sink.figure_();
+    }
+
+    /**
+     * Dumps a table with grid and caption into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateTable( Sink sink )
+    {
+        int[] justify =
+        {
+             Sink.JUSTIFY_CENTER, Sink.JUSTIFY_LEFT, Sink.JUSTIFY_RIGHT
+        };
+
+        sink.table();
+
+        sink.tableRows( justify, true );
+
+        sink.tableRow();
+        sink.tableCell();
+        sink.text( "Centered" );
+        sink.lineBreak();
+        sink.text( "cell 1,1" );
+        sink.tableCell_();
+        sink.tableCell();
+        sink.text( "Left-aligned" );
+        sink.lineBreak();
+        sink.text( "cell 1,2" );
+        sink.tableCell_();
+        sink.tableCell();
+        sink.text( "Right-aligned" );
+        sink.lineBreak();
+        sink.text( "cell 1,3" );
+        sink.tableCell_();
+        sink.tableRow_();
+
+        sink.tableRow();
+        sink.tableCell();
+        sink.text( "cell 2,1" );
+        sink.tableCell_();
+        sink.tableCell();
+        sink.text( "cell 2,2" );
+        sink.tableCell_();
+        sink.tableCell();
+        sink.text( "cell 2,3" );
+        sink.tableCell_();
+        sink.tableRow_();
+
+        sink.tableRows_();
+
+        sink.tableCaption();
+        sink.text( "Table caption" );
+        sink.tableCaption_();
+
+        sink.table_();
+    }
+
+    /**
+     * Dumps a table without grid into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateNoGridTable( Sink sink )
+    {
+        int[] justify =
+        {
+             Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER
+        };
+
+        sink.table();
+
+        sink.tableRows( justify, false );
+
+        sink.tableRow();
+        sink.tableCell();
+        sink.text( "cell" );
+        sink.tableCell_();
+        sink.tableCell();
+        sink.text( "cell" );
+        sink.tableCell_();
+        sink.tableRow_();
+
+        sink.tableRow();
+        sink.tableCell();
+        sink.text( "cell" );
+        sink.tableCell_();
+        sink.tableCell();
+        sink.text( "cell" );
+        sink.tableCell_();
+        sink.tableRow_();
+
+        sink.tableRows_();
+
+        sink.table_();
+    }
+
+    /**
+     * Dumps a table with a header row into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateHeaderTable( Sink sink )
+    {
+        int[] justify =
+        {
+             Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER
+        };
+
+        sink.table();
+
+        sink.tableRows( justify, true );
+
+        sink.tableRow();
+        sink.tableHeaderCell();
+        sink.text( "header" );
+        sink.tableHeaderCell_();
+        sink.tableHeaderCell();
+        sink.text( "header" );
+        sink.tableHeaderCell_();
+        sink.tableRow_();
+
+        sink.tableRow();
+        sink.tableCell();
+        sink.text( "cell" );
+        sink.tableCell_();
+        sink.tableCell();
+        sink.text( "cell" );
+        sink.tableCell_();
+        sink.tableRow_();
+
+        sink.tableRows_();
+
+        sink.table_();
+    }
+
+
+
+    /**
+     * Dumps a paragraph with italic, bold and monospaced text
+     * into the specified sink. The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateFonts( Sink sink )
+    {
+        sink.paragraph();
+
+        sink.italic();
+        sink.text( "Italic" );
+        sink.italic_();
+        sink.text( " font. " );
+
+        sink.bold();
+        sink.text( "Bold" );
+        sink.bold_();
+        sink.text( " font. " );
+
+        sink.monospaced();
+        sink.text( "Monospaced" );
+        sink.monospaced_();
+        sink.text( " font." );
+
+        sink.paragraph_();
+    }
+
+    /**
+     * Dumps a paragraph with anchor and link elements
+     * into the specified sink. The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateAnchors( Sink sink )
+    {
+        sink.paragraph();
+
+        sink.anchor( "Anchor" );
+        sink.text( "Anchor" );
+        sink.anchor_();
+
+        sink.text( ". Link to " );
+        sink.link( "#Anchor" );
+        sink.text( "Anchor" );
+        sink.link_();
+
+        sink.text( ". Link to " );
+        sink.link( "http://www.pixware.fr" );
+        sink.text( "http://www.pixware.fr" );
+        sink.link_();
+
+        sink.text( ". Link to " );
+        sink.link( "#Anchor" );
+        sink.text( "showing alternate text" );
+        sink.link_();
+
+        sink.text( ". Link to " );
+        sink.link( "http://www.pixware.fr" );
+        sink.text( "Pixware home page" );
+        sink.link_();
+
+        sink.text( "." );
+
+        sink.paragraph_();
+    }
+
+    /**
+     * Dumps a horizontal rule block into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateHorizontalRule( Sink sink )
+    {
+        sink.paragraph();
+        sink.text( "Horizontal line:" );
+        sink.paragraph_();
+        sink.horizontalRule();
+    }
+
+    /**
+     * Dumps a pageBreak block into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generatePageBreak( Sink sink )
+    {
+        sink.pageBreak();
+        sink.paragraph();
+        sink.text( "New page." );
+        sink.paragraph_();
+    }
+
+    /**
+     * Dumps a lineBreak block into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateLineBreak( Sink sink )
+    {
+        sink.paragraph();
+        sink.text( "Force line" );
+        sink.lineBreak();
+        sink.text( "break." );
+        sink.paragraph_();
+    }
+
+    /**
+     * Dumps a nonBreakingSpace block into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateNonBreakingSpace( Sink sink )
+    {
+        sink.paragraph();
+        sink.text( "Non" );
+        sink.nonBreakingSpace();
+        sink.text( "breaking" );
+        sink.nonBreakingSpace();
+        sink.text( "space." );
+        sink.paragraph_();
+    }
+
+    /**
+     * Dumps a special character block into the specified sink.
+     * The sink is not flushed or closed.
+     *
+     * @param sink The sink to receive the events.
+     */
+    public static void generateSpecialCharacters( Sink sink )
+    {
+        sink.paragraph();
+        sink.text( "Escaped special characters:" );
+        sink.lineBreak();
+        sink.text( "~" );
+        sink.lineBreak();
+        sink.text( "=" );
+        sink.lineBreak();
+        sink.text( "-" );
+        sink.lineBreak();
+        sink.text( "+" );
+        sink.lineBreak();
+        sink.text( "*" );
+        sink.lineBreak();
+        sink.text( "[" );
+        sink.lineBreak();
+        sink.text( "]" );
+        sink.lineBreak();
+        sink.text( "<" );
+        sink.lineBreak();
+        sink.text( ">" );
+        sink.lineBreak();
+        sink.text( "{" );
+        sink.lineBreak();
+        sink.text( "}" );
+        sink.lineBreak();
+        sink.text( "\\" );
+        sink.paragraph_();
+
+        sink.paragraph();
+        sink.text( "Copyright symbol:" );
+        sink.lineBreak();
+        sink.text( "\u00a9" );
+        sink.paragraph_();
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkUtilsTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkUtilsTest.java
new file mode 100644
index 0000000..40c4144
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/SinkUtilsTest.java
@@ -0,0 +1,85 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.MutableAttributeSet;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author ltheussl
+ */
+public class SinkUtilsTest
+        extends TestCase
+{
+
+    /**
+     * Test of getAttributeString method, of class SinkUtils.
+     */
+    public void testGetAttributeString()
+    {
+        assertEquals( "", SinkUtils.getAttributeString( null ) );
+
+        AttributeSet att = new SinkEventAttributeSet( SinkEventAttributeSet.BOXED );
+        String expResult = " decoration=\"boxed\"";
+        String result = SinkUtils.getAttributeString( att );
+        assertEquals( expResult, result );
+
+        SinkEventAttributes at = new SinkEventAttributeSet( SinkEventAttributeSet.BOLD );
+        at.addAttributes( att );
+        expResult = " style=\"bold\" decoration=\"boxed\"";
+        result = SinkUtils.getAttributeString( at );
+        assertEquals( expResult, result );
+
+        att = new SinkEventAttributeSet( new String[] {"color", "red", "margin-left", "20px"} );
+
+        at = new SinkEventAttributeSet();
+        at.addAttribute( SinkEventAttributeSet.STYLE, att );
+        expResult = " style=\"color: red; margin-left: 20px\"";
+        result = SinkUtils.getAttributeString( at );
+        assertEquals( expResult, result );
+    }
+
+    /**
+     * Test of filterAttributes method, of class SinkUtils.
+     */
+    public void testFilterAttributes()
+    {
+        assertNull( SinkUtils.filterAttributes( null, null ) );
+
+        AttributeSet attributes = new SinkEventAttributeSet( 1 );
+        String[] valids = null;
+
+        MutableAttributeSet result = SinkUtils.filterAttributes( attributes, valids );
+        assertEquals( 0, result.getAttributeCount() );
+
+        valids = new String[] {};
+        result = SinkUtils.filterAttributes( attributes, valids );
+        assertEquals( 0, result.getAttributeCount() );
+
+        result = SinkUtils.filterAttributes( SinkEventAttributeSet.BOLD, SinkUtils.SINK_BASE_ATTRIBUTES );
+        assertEquals( 1, result.getAttributeCount() );
+
+        result = SinkUtils.filterAttributes( SinkEventAttributeSet.CENTER, SinkUtils.SINK_BASE_ATTRIBUTES );
+        assertEquals( 0, result.getAttributeCount() );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/TextSink.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/TextSink.java
new file mode 100644
index 0000000..ad13036
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/TextSink.java
@@ -0,0 +1,832 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * A simple text-based implementation of the <code>Sink</code> interface.
+ * Useful for testing purposes.
+ */
+public class TextSink
+    extends AbstractSink
+{
+
+    /** For writing the result. */
+    private final Writer out;
+
+    /** Constructor.
+     * @param writer The writer for writing the result.
+     */
+    public TextSink( Writer writer )
+    {
+        this.out = writer;
+    }
+
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        writeln( "begin:head" );
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        writeln( "end:head" );
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        writeln( "begin:body" );
+    }
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        writeln( "end:body" );
+    }
+
+    /** {@inheritDoc} */
+    public void section1()
+    {
+        write( "begin:section1" );
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        writeln( "end:section1" );
+    }
+
+    /** {@inheritDoc} */
+    public void section2()
+    {
+        write( "begin:section2" );
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        writeln( "end:section2" );
+    }
+
+    /** {@inheritDoc} */
+    public void section3()
+    {
+        write( "begin:section3" );
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        writeln( "end:section3" );
+    }
+
+    /** {@inheritDoc} */
+    public void section4()
+    {
+        write( "begin:section4" );
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        writeln( "end:section4" );
+    }
+
+    /** {@inheritDoc} */
+    public void section5()
+    {
+        write( "begin:section5" );
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        writeln( "end:section5" );
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        writeln( "begin:list" );
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        writeln( "end:list" );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        write( "begin:listItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        writeln( "end:listItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        writeln( "begin:numberedList, numbering: " + numbering );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        writeln( "end:numberedList" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        write( "begin:numberedListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        writeln( "end:numberedListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList()
+    {
+        writeln( "begin:definitionList" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList_()
+    {
+        writeln( "end:definitionList" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        write( "begin:definitionListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem_()
+    {
+        writeln( "end:definitionListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        write( "begin:definition" );
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        writeln( "end:definition" );
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        write( "begin:figure" );
+    }
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        writeln( "end:figure" );
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        writeln( "begin:table" );
+    }
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        writeln( "end:table" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        writeln( "begin:tableRows" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        writeln( "end:tableRows" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        write( "begin:tableRow" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        writeln( "end:tableRow" );
+    }
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        write( "begin:title" );
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        writeln( "end:title" );
+    }
+
+    /** {@inheritDoc} */
+    public void author()
+    {
+        write( "begin:author" );
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        writeln( "end:author" );
+    }
+
+    /** {@inheritDoc} */
+    public void date()
+    {
+        write( "begin:date" );
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        writeln( "end:date" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle()
+    {
+        write( "begin:sectionTitle" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_()
+    {
+        writeln( "end:sectionTitle" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        write( "begin:sectionTitle1" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        writeln( "end:sectionTitle1" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        write( "begin:sectionTitle2" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        writeln( "end:sectionTitle2" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        write( "begin:sectionTitle3" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        writeln( "end:sectionTitle3" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        write( "begin:sectionTitle4" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        writeln( "end:sectionTitle4" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        write( "begin:sectionTitle5" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        writeln( "end:sectionTitle5" );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph()
+    {
+        write( "begin:paragraph" );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        writeln( "end:paragraph" );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        write( "begin:verbatim, boxed: " + boxed );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        writeln( "end:verbatim" );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        write( "begin:definedTerm" );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm_()
+    {
+        writeln( "end:definedTerm" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption()
+    {
+        write( "begin:figureCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        writeln( "end:figureCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        write( "begin:tableCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        write( "begin:tableCell, width: " + width );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        writeln( "end:tableCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        write( "begin:tableHeaderCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        write( "begin:tableHeaderCell, width: " + width );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        writeln( "end:tableHeaderCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption()
+    {
+        write( "begin:tableCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption_()
+    {
+        writeln( "end:tableCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        write( "figureGraphics, name: " + name );
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        write( "horizontalRule" );
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        write( "pageBreak" );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        write( "begin:anchor, name: " + name  );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor_()
+    {
+        writeln( "end:anchor" );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        write( "begin:link, name: " + name  );
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        writeln( "end:link" );
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        write( "begin:italic" );
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        writeln( "end:italic" );
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        write( "begin:bold" );
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        writeln( "end:bold" );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        write( "begin:monospaced" );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        writeln( "end:monospaced" );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        write( "lineBreak" );
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        write( "nonBreakingSpace" );
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        write( "text: " + text );
+    }
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        write( "rawText: " + text );
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+        write( "comment: " + comment );
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        try
+        {
+            out.flush();
+        }
+        catch ( IOException e )
+        {
+            getLog().warn( "Could not flush sink: " + e.getMessage(), e );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        try
+        {
+            out.close();
+        }
+        catch ( IOException e )
+        {
+            getLog().warn( "Could not close sink: " + e.getMessage(), e );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        head();
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        title();
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        author();
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        date();
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        body();
+    }
+
+    /** {@inheritDoc} */
+    public void section( int level, SinkEventAttributes attributes )
+    {
+        write( "begin:section" + level );
+    }
+
+    /** {@inheritDoc} */
+    public void section_( int level )
+    {
+        writeln( "end:section" + level );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        write( "begin:sectionTitle" + level );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+        writeln( "end:sectionTitle" + level );
+    }
+
+    /** {@inheritDoc} */
+    public void list( SinkEventAttributes attributes )
+    {
+        list();
+    }
+
+    /** {@inheritDoc} */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        listItem();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        numberedList( numbering );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        numberedListItem();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        definitionList();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem( SinkEventAttributes attributes )
+    {
+        definitionListItem();
+    }
+
+    /** {@inheritDoc} */
+    public void definition( SinkEventAttributes attributes )
+    {
+        definition();
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        definedTerm();
+    }
+
+    /** {@inheritDoc} */
+    public void figure( SinkEventAttributes attributes )
+    {
+        write( "begin:figure" + SinkUtils.getAttributeString( attributes ) );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        figureCaption();
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        figureGraphics( src );
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        table();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        tableRow();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        tableCaption();
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        paragraph();
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        boolean boxed = false;
+
+        if ( attributes != null && attributes.isDefined( SinkEventAttributes.DECORATION ) )
+        {
+            boxed = "boxed".equals(
+                attributes.getAttribute( SinkEventAttributes.DECORATION ).toString() );
+        }
+
+        verbatim( boxed );
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        horizontalRule();
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        anchor( name );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        link( name );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        lineBreak();
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        text( text );
+    }
+
+    /** {@inheritDoc} */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        write( "unknown: " + name );
+    }
+
+    /**
+     * Writes the given string + EOL.
+     *
+     * @param text The text to write.
+     */
+    private void write( String text )
+    {
+        try
+        {
+            out.write( text + EOL );
+        }
+        catch ( IOException e )
+        {
+            getLog().warn( "Could not write to sink: " + e.getMessage(), e );
+        }
+    }
+
+    /**
+     * Writes the given string + two EOLs.
+     *
+     * @param text The text to write.
+     */
+    private void writeln( String text )
+    {
+        write( text );
+        write( EOL );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/WellformednessCheckingSink.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/WellformednessCheckingSink.java
new file mode 100644
index 0000000..dad69ce
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/WellformednessCheckingSink.java
@@ -0,0 +1,843 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+
+/**
+ * This sink is used for testing purposes in order to check wether
+ * the input of some parser is well-formed.
+ *
+ * @author <a href="mailto:lars at trieloff.net">Lars Trieloff</a>
+ * @version $Id: WellformednessCheckingSink.java 807164 2009-08-24 11:59:25Z vsiveton $
+ */
+public class WellformednessCheckingSink
+    extends AbstractSink
+{
+    private final Stack elements = new Stack();
+
+    private final List errors = new LinkedList();
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        startElement( "head" );
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        checkWellformedness( "head" );
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        startElement( "body" );
+    }
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        checkWellformedness( "body" );
+    }
+
+    /** {@inheritDoc} */
+    public void section1()
+    {
+        startElement( "section1" );
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        checkWellformedness( "section1" );
+    }
+
+    /** {@inheritDoc} */
+    public void section2()
+    {
+        startElement( "section2" );
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        checkWellformedness( "section2" );
+    }
+
+    /** {@inheritDoc} */
+    public void section3()
+    {
+        startElement( "section3" );
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        checkWellformedness( "section3" );
+    }
+
+    /** {@inheritDoc} */
+    public void section4()
+    {
+        startElement( "section4" );
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        checkWellformedness( "section4" );
+    }
+
+    /** {@inheritDoc} */
+    public void section5()
+    {
+        startElement( "section5" );
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        checkWellformedness( "section5" );
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        startElement( "list" );
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        checkWellformedness( "list" );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        startElement( "listItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        checkWellformedness( "listItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        startElement( "numberedList" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        checkWellformedness( "numberedList" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        startElement( "numberedListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        checkWellformedness( "numberedListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList()
+    {
+        startElement( "definitionList" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList_()
+    {
+        checkWellformedness( "definitionList" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        startElement( "definitionListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem_()
+    {
+        checkWellformedness( "definitionListItem" );
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        startElement( "definition" );
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        checkWellformedness( "definition" );
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        startElement( "figure" );
+    }
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        checkWellformedness( "figure" );
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        startElement( "table" );
+    }
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        checkWellformedness( "table" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        startElement( "tableRows" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        checkWellformedness( "tableRows" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        startElement( "tableRow" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        checkWellformedness( "tableRow" );
+    }
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        startElement( "title" );
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        checkWellformedness( "title" );
+    }
+
+    /** {@inheritDoc} */
+    public void author()
+    {
+        startElement( "author" );
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        checkWellformedness( "author" );
+    }
+
+    /** {@inheritDoc} */
+    public void date()
+    {
+        startElement( "date" );
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        checkWellformedness( "date" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle()
+    {
+        startElement( "sectionTitle" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_()
+    {
+        checkWellformedness( "sectionTitle" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        startElement( "sectionTitle1" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        checkWellformedness( "sectionTitle1" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        startElement( "sectionTitle2" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        checkWellformedness( "sectionTitle2" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        startElement( "sectionTitle3" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        checkWellformedness( "sectionTitle3" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        startElement( "sectionTitle4" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        checkWellformedness( "sectionTitle4" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        startElement( "sectionTitle5" );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        checkWellformedness( "sectionTitle5" );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph()
+    {
+        startElement( "paragraph" );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        checkWellformedness( "paragraph" );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        startElement( "verbatim" );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        checkWellformedness( "verbatim" );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        startElement( "definedTerm" );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm_()
+    {
+        checkWellformedness( "definedTerm" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption()
+    {
+        startElement( "figureCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        checkWellformedness( "figureCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        startElement( "tableCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        startElement( "tableCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        checkWellformedness( "tableCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        startElement( "tableHeaderCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        startElement( "tableHeaderCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        checkWellformedness( "tableHeaderCell" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption()
+    {
+        startElement( "tableCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption_()
+    {
+        checkWellformedness( "tableCaption" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        startElement( "anchor" );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor_()
+    {
+        checkWellformedness( "anchor" );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        startElement( "link" );
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        checkWellformedness( "link" );
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        startElement( "italic" );
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        checkWellformedness( "italic" );
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        startElement( "bold" );
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        checkWellformedness( "bold" );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        startElement( "monospaced" );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        checkWellformedness( "monospaced" );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        this.elements.clear();
+        this.errors.clear();
+    }
+
+    /**
+     * Finds out wether the wellformedness-contraints of the model have been
+     * violated.
+     *
+     * @return false for non-wellformed models
+     */
+    public boolean isWellformed()
+    {
+        return errors.size() == 0;
+    }
+
+    /**
+     * Gets the offending element that breaks the wellformedness as well
+     * as the exepected element.
+     *
+     * @return the expected and acual elements
+     */
+    public String getOffender()
+    {
+        if ( isWellformed() )
+        {
+            return null;
+        }
+
+        return (String) errors.get( errors.size() - 1 );
+    }
+
+    /**
+     * Gets the list of errors found during wellformedness-check
+     *
+     * @return a list of String of error messages
+     */
+    public List getOffenders()
+    {
+        return errors;
+    }
+
+    /**
+     * Checks wether a newly encountered end-tag breaks the wellformedness
+     * of the model.
+     *
+     * @param actual the local-name of the encountered element
+     */
+    private void checkWellformedness( String actual )
+    {
+        String expected = (String) elements.pop();
+
+        if ( !expected.equals( actual ) )
+        {
+            errors.add( "Encountered closing: " + actual + ", expected " + expected );
+        }
+    }
+
+    /**
+     * Starts a new element and puts it on the stack in order to calculate
+     * wellformedness of the model at a later point of time.
+     *
+     * @param string the local-name of the start-tag
+     */
+    private void startElement( String string )
+    {
+        elements.push( string );
+    }
+
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        head();
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        title();
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        author();
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        date();
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        body();
+    }
+
+    /** {@inheritDoc} */
+    public void section( int level, SinkEventAttributes attributes )
+    {
+        startElement( "section" + level );
+    }
+
+    /** {@inheritDoc} */
+    public void section_( int level )
+    {
+        checkWellformedness( "section" + level );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        startElement( "sectionTitle" + level );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+        checkWellformedness( "sectionTitle" + level );
+    }
+
+    /** {@inheritDoc} */
+    public void list( SinkEventAttributes attributes )
+    {
+        list();
+    }
+
+    /** {@inheritDoc} */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        listItem();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        numberedList( numbering );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        numberedListItem();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        definitionList();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem( SinkEventAttributes attributes )
+    {
+        definitionListItem();
+    }
+
+    /** {@inheritDoc} */
+    public void definition( SinkEventAttributes attributes )
+    {
+        definition();
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        definedTerm();
+    }
+
+    /** {@inheritDoc} */
+    public void figure( SinkEventAttributes attributes )
+    {
+        figure();
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        figureCaption();
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        figureGraphics( src );
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        table();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        tableRow();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        tableCaption();
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        paragraph();
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        verbatim( false );
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        horizontalRule();
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        anchor( name );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        link( name );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        lineBreak();
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        text( text );
+    }
+
+    /** {@inheritDoc} */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        // ignore
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/XhtmlBaseSinkTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/XhtmlBaseSinkTest.java
new file mode 100644
index 0000000..1653354
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/XhtmlBaseSinkTest.java
@@ -0,0 +1,950 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.codehaus.plexus.PlexusTestCase;
+
+/**
+ * Test for XhtmlBaseSink.
+ *
+ * @author ltheussl
+ * @version $Id: XhtmlBaseSinkTest.java 926049 2010-03-22 12:40:16Z ltheussl $
+ * @since 1.1
+ */
+public class XhtmlBaseSinkTest
+    extends PlexusTestCase
+{
+    private final SinkEventAttributes attributes =
+            new SinkEventAttributeSet( new String[] {SinkEventAttributes.STYLE, "bold"} );
+    private XhtmlBaseSink sink;
+    private Writer writer;
+
+    /**
+     * Set up the writer.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    protected void setUp()
+            throws Exception
+    {
+        super.setUp();
+        writer =  new StringWriter();
+    }
+
+    /** @throws Exception */
+    public void testSpaceAfterClosingTag()
+        throws Exception
+    {
+        // DOXIA-189
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.paragraph();
+            sink.text( "There should be no space before the " );
+            sink.italic();
+            sink.text( "period" );
+            sink.italic_();
+            sink.text( "." );
+            sink.paragraph_();
+        }
+        finally
+        {
+            if ( sink != null )
+            {
+                sink.close();
+            }
+        }
+
+        String actual = writer.toString();
+        String expected = "<p>There should be no space before the <i>period</i>.</p>";
+
+        assertEquals( expected, actual );
+    }
+
+    /**
+     * @throws Exception if any
+     */
+    public void testNestedTables()
+        throws Exception
+    {
+        // DOXIA-177
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.table();
+            sink.tableRows( new int[] { Sink.JUSTIFY_CENTER }, false );
+            sink.tableRow();
+            sink.tableCell();
+            sink.text( "cell11" );
+            sink.tableCell_();
+            sink.tableCell();
+            sink.text( "cell12" );
+            sink.tableCell_();
+            sink.tableRow_();
+
+            sink.tableRow();
+            sink.tableCell();
+            sink.table( SinkEventAttributeSet.LEFT );
+            sink.tableRows( new int[] { Sink.JUSTIFY_LEFT }, false );
+            sink.tableRow();
+            sink.tableCell();
+            sink.text( "nestedTable1Cell11" );
+            sink.tableCell_();
+            sink.tableCell();
+            sink.text( "nestedTable1Cell12" );
+            sink.tableCell_();
+            sink.tableRow_();
+            sink.tableRow();
+            sink.tableCell();
+
+            sink.table( SinkEventAttributeSet.RIGHT );
+            sink.tableRows( new int[] { Sink.JUSTIFY_RIGHT }, false );
+            sink.tableRow();
+            sink.tableCell();
+            sink.text( "nestedTable2Cell11" );
+            sink.tableCell_();
+            sink.tableCell();
+            sink.text( "nestedTable2Cell12" );
+            sink.tableCell_();
+            sink.tableRow_();
+            sink.tableRow();
+            sink.tableCell();
+            sink.text( "nestedTable2Cell21" );
+            sink.tableCell_();
+            sink.tableCell();
+            sink.text( "nestedTable2Cell22" );
+            sink.tableCell_();
+            sink.tableRow_();
+            sink.tableRows_();
+            sink.tableCaption();
+            sink.text( "caption3" );
+            sink.tableCaption_();
+            sink.table_();
+
+            sink.tableCell_();
+            sink.tableCell();
+            sink.text( "nestedTable1Cell22" );
+            sink.tableCell_();
+            sink.tableRow_();
+            sink.tableRows_();
+            sink.tableCaption();
+            sink.text( "caption2" );
+            sink.tableCaption_();
+            sink.table_();
+
+            sink.tableCell_();
+            sink.tableCell();
+            sink.text( "cell22" );
+            sink.tableCell_();
+            sink.tableRow_();
+            sink.tableRows_();
+            sink.tableCaption();
+            sink.text( "caption1" );
+            sink.tableCaption_();
+            sink.table_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        String actual = writer.toString();
+        assertTrue( actual.indexOf( "<table align=\"center\" border=\"0\" class=\"bodyTable\">"
+            + "<caption>caption1</caption>" ) != 1 );
+        assertTrue( actual.indexOf( "<table border=\"0\" class=\"bodyTable\" align=\"left\">"
+            + "<caption>caption2</caption>" ) != 1 );
+        assertTrue( actual.indexOf( "<table align=\"center\" border=\"0\" class=\"bodyTable\">"
+            + "<caption>caption3</caption>" ) != 1 );
+
+        assertTrue( actual.indexOf( "<td align=\"center\">cell11</td>" ) != 1 );
+        assertTrue( actual.indexOf( "<td align=\"left\">nestedTable1Cell11</td>" ) != 1 );
+        assertTrue( actual.indexOf( "<td align=\"right\">nestedTable2Cell11</td>" ) != 1 );
+        assertTrue( actual.indexOf( "<td align=\"left\">nestedTable1Cell22</td>" ) != 1 );
+        assertTrue( actual.indexOf( "<td align=\"center\">cell22</td>" ) != 1 );
+    }
+
+    /**
+     * Test of section method, of class XhtmlBaseSink.
+     */
+    public void testSection()
+    {
+        final int level = XhtmlBaseSink.SECTION_LEVEL_1;
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.section( level, attributes );
+            sink.sectionTitle( level, attributes );
+            sink.sectionTitle_( level );
+            sink.section_( level );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div class=\"section\" style=\"bold\"><h2 style=\"bold\"></h2></div>", writer.toString() );
+    }
+
+    /**
+     * Test of section1 method, of class XhtmlBaseSink.
+     */
+    public void testSection1()
+    {
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.section1();
+            sink.sectionTitle1();
+            sink.sectionTitle1_();
+            sink.section1_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div class=\"section\"><h2></h2></div>", writer.toString() );
+    }
+
+    /**
+     * Test of section2 method, of class XhtmlBaseSink.
+     */
+    public void testSection2()
+    {
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.section2();
+            sink.sectionTitle2();
+            sink.sectionTitle2_();
+            sink.section2_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div class=\"section\"><h3></h3></div>", writer.toString() );
+    }
+
+    /**
+     * Test of section3 method, of class XhtmlBaseSink.
+     */
+    public void testSection3()
+    {
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.section3();
+            sink.sectionTitle3();
+            sink.sectionTitle3_();
+            sink.section3_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div class=\"section\"><h4></h4></div>", writer.toString() );
+    }
+
+    /**
+     * Test of section4 method, of class XhtmlBaseSink.
+     */
+    public void testSection4()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.section4();
+            sink.sectionTitle4();
+            sink.sectionTitle4_();
+            sink.section4_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div class=\"section\"><h5></h5></div>", writer.toString() );
+    }
+
+    /**
+     * Test of section5 method, of class XhtmlBaseSink.
+     */
+    public void testSection5()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.section5();
+            sink.sectionTitle5();
+            sink.sectionTitle5_();
+            sink.section5_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div class=\"section\"><h6></h6></div>", writer.toString() );
+    }
+
+    /**
+     * Test of list method, of class XhtmlBaseSink.
+     * @throws java.lang.Exception if any.
+     */
+    public void testList()
+            throws Exception
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.list();
+            sink.listItem();
+            sink.listItem_();
+            sink.list_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<ul><li></li></ul>", writer.toString() );
+
+        writer =  new StringWriter();
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.list( attributes );
+            sink.listItem( attributes );
+            sink.listItem_();
+            sink.list_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<ul style=\"bold\"><li style=\"bold\"></li></ul>", writer.toString() );
+    }
+
+    /**
+     * Test of numberedList method, of class XhtmlBaseSink.
+     */
+    public void testNumberedList()
+    {
+        final int numbering = XhtmlBaseSink.NUMBERING_DECIMAL;
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.numberedList( numbering );
+            sink.numberedListItem();
+            sink.numberedListItem_();
+            sink.numberedList_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<ol style=\"list-style-type: decimal\"><li></li></ol>", writer.toString() );
+
+        writer =  new StringWriter();
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.numberedList( numbering, attributes );
+            sink.numberedListItem( attributes );
+            sink.numberedListItem_();
+            sink.numberedList_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<ol style=\"list-style-type: decimal\"><li style=\"bold\"></li></ol>", writer.toString() );
+    }
+
+    /**
+     * Test of definitionList method, of class XhtmlBaseSink.
+     */
+    public void testDefinitionList()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.definitionList();
+            sink.definedTerm();
+            sink.definedTerm_();
+            sink.definition();
+            sink.definition_();
+            sink.definitionList_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<dl><dt></dt><dd></dd></dl>", writer.toString() );
+
+        writer =  new StringWriter();
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.definitionList( attributes );
+            sink.definedTerm( attributes );
+            sink.definedTerm_();
+            sink.definition( attributes );
+            sink.definition_();
+            sink.definitionList_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<dl style=\"bold\"><dt style=\"bold\"></dt><dd style=\"bold\"></dd></dl>", writer.toString() );
+    }
+
+    /**
+     * Test of figure method, of class XhtmlBaseSink.
+     */
+    public void testFigure()
+    {
+        final String src = "src.jpg";
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.figure( attributes );
+            sink.figureGraphics( src, attributes );
+            sink.figureCaption( attributes );
+            sink.figureCaption_();
+            sink.figure_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div style=\"bold\" class=\"figure\">"
+                + "<p align=\"center\"><img src=\"src.jpg\" style=\"bold\" alt=\"\" /></p>"
+                + "<p align=\"center\" style=\"bold\"><i></i></p></div>", writer.toString() );
+    }
+
+    /**
+     * Test of figureGraphics method, of class XhtmlBaseSink.
+     */
+    public void testFigureGraphics()
+    {
+        String src = "source.png";
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.figureGraphics( src, attributes );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<img src=\"source.png\" style=\"bold\" alt=\"\" />", writer.toString() );
+    }
+
+    /**
+     * Test of paragraph method, of class XhtmlBaseSink.
+     */
+    public void testParagraph()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.paragraph();
+            sink.paragraph_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<p></p>", writer.toString() );
+
+        writer =  new StringWriter();
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.paragraph( attributes );
+            sink.paragraph_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<p style=\"bold\"></p>", writer.toString() );
+    }
+
+    /**
+     * Test of verbatim method, of class XhtmlBaseSink.
+     */
+    public void testVerbatim()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.verbatim( true );
+            sink.verbatim_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div class=\"source\"><pre></pre></div>", writer.toString() );
+
+        writer =  new StringWriter();
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.verbatim( attributes );
+            sink.verbatim_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div style=\"bold\"><pre style=\"bold\"></pre></div>", writer.toString() );
+    }
+
+    /**
+     * Test of horizontalRule method, of class XhtmlBaseSink.
+     */
+    public void testHorizontalRule()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.horizontalRule();
+            sink.horizontalRule( attributes );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<hr /><hr style=\"bold\" />", writer.toString() );
+    }
+
+    /**
+     * Test of table method, of class XhtmlBaseSink.
+     */
+    public void testTable()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.table( attributes );
+            sink.table_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "</table>", writer.toString() );
+    }
+
+    /**
+     * Test of tableRows method, of class XhtmlBaseSink.
+     */
+    public void testTableRows()
+    {
+        final int[] justification = null;
+        final boolean grid = false;
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.tableRows( justification, grid );
+            sink.tableRows_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<table border=\"0\" class=\"bodyTable\">", writer.toString() );
+    }
+
+    /**
+     * Test of tableRow method, of class XhtmlBaseSink.
+     */
+    public void testTableRow()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.tableRow( attributes );
+            sink.tableRow_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<tr class=\"a\" style=\"bold\"></tr>", writer.toString() );
+    }
+
+    /**
+     * Test of tableCell method, of class XhtmlBaseSink.
+     */
+    public void testTableCell()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.tableCell( attributes );
+            sink.tableCell_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<td style=\"bold\"></td>", writer.toString() );
+    }
+
+    /**
+     * Test of tableHeaderCell method, of class XhtmlBaseSink.
+     */
+    public void testTableHeaderCell()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.tableHeaderCell( attributes );
+            sink.tableHeaderCell_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<th style=\"bold\"></th>", writer.toString() );
+    }
+
+    /**
+     * Test of tableCaption method, of class XhtmlBaseSink.
+     */
+    public void testTableCaption()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.table();
+            sink.tableRows( null, false );
+            sink.tableCaption( attributes );
+            sink.text( "caption" );
+            sink.tableCaption_();
+            sink.tableRows_();
+            sink.table_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<table border=\"0\" class=\"bodyTable\">" +
+                "<caption style=\"bold\">caption</caption></table>", writer.toString() );
+    }
+
+    /**
+     * Test of anchor method, of class XhtmlBaseSink.
+     */
+    public void testAnchor()
+    {
+        String name = "anchor";
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.anchor( name, attributes );
+            sink.anchor_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<a name=\"anchor\" style=\"bold\"></a>", writer.toString() );
+    }
+
+    /**
+     * Test of link method, of class XhtmlBaseSink.
+     */
+    public void testLink()
+    {
+        final String name = "link.html";
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.link( name, attributes );
+            sink.link_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<a href=\"link.html\" style=\"bold\"></a>", writer.toString() );
+    }
+
+    /**
+     * Test of italic/bold/monospaced method, of class XhtmlBaseSink.
+     */
+    public void testItalic()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.italic();
+            sink.italic_();
+            sink.bold();
+            sink.bold_();
+            sink.monospaced();
+            sink.monospaced_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<i></i><b></b><tt></tt>", writer.toString() );
+    }
+
+    /**
+     * Test of lineBreak/pageBreak/nonBreakingSpace method, of class XhtmlBaseSink.
+     */
+    public void testLineBreak()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.lineBreak( attributes );
+            sink.pageBreak();
+            sink.nonBreakingSpace();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<br style=\"bold\" /><!-- PB --> ", writer.toString() );
+    }
+
+    /**
+     * Test of text method, of class XhtmlBaseSink.
+     */
+    public void testText()
+    {
+        String text = "a text & \u00c6";
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.text( text );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "a text & &#xc6;", writer.toString() );
+
+        writer =  new StringWriter();
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.text( text, attributes );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "a text & &#xc6;", writer.toString() );
+    }
+
+    /**
+     * Test of rawText method, of class XhtmlBaseSink.
+     */
+    public void testRawText()
+    {
+        String text = "raw text";
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.rawText( text );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "raw text", writer.toString() );
+    }
+
+    /**
+     * Test of comment method, of class XhtmlBaseSink.
+     */
+    public void testComment()
+    {
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.comment( "a comment" );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<!-- a comment -->", writer.toString() );
+    }
+
+    /**
+     * Test of unknown method, of class XhtmlBaseSink.
+     */
+    public void testUnknown()
+    {
+        final String name = "unknown";
+        final Object[] requiredParams = null;
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.unknown( name, requiredParams, attributes );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "", writer.toString() );
+    }
+
+    /**
+     * Test entities in attribute values.
+     */
+    public void testAttributeEntities()
+    {
+        final Object[] startTag = new Object[] { new Integer( XhtmlBaseSink.TAG_TYPE_START ) };
+        final Object[] endTag = new Object[] { new Integer( XhtmlBaseSink.TAG_TYPE_END ) };
+        final String script = XhtmlBaseSink.SCRIPT.toString();
+        final SinkEventAttributes src = new SinkEventAttributeSet(
+                new String[] {SinkEventAttributes.SRC.toString(), "http://ex.com/ex.js?v=l&l=e"} );
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+
+            sink.unknown( script, startTag, src );
+            sink.unknown( script, endTag, null );
+
+            sink.figureGraphics( "http://ex.com/ex.jpg?v=l&l=e", src );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        String result = writer.toString();
+
+        assertTrue( result.indexOf( "ex.js?v=l&l=e" ) != -1 );
+        assertTrue( result.indexOf( "ex.jpg?v=l&l=e" ) != -1 );
+    }
+
+    /**
+     * Test of entity.
+     */
+    public void testEntity()
+    {
+        // DOXIA-314
+        String text = "a text '&#x1d7ed;'";
+
+        try
+        {
+            sink = new XhtmlBaseSink( writer );
+            sink.text( text );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "a text '&#x1d7ed;'", writer.toString() );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/sink/render/RenderingContextTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/sink/render/RenderingContextTest.java
new file mode 100644
index 0000000..6782b76
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/sink/render/RenderingContextTest.java
@@ -0,0 +1,63 @@
+package org.apache.maven.doxia.sink.render;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+
+import org.codehaus.plexus.PlexusTestCase;
+
+/**
+ * @author <a href="mailto:olamy at apache.org">olamy</a>
+ * @since 20 oct. 07
+ * @version $Id: RenderingContextTest.java 747735 2009-02-25 10:43:09Z ltheussl $
+ */
+public class RenderingContextTest
+    extends PlexusTestCase
+{
+
+    /**
+     * Test getRelativePath() with various file names.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    public void testFileNameWithDot()
+        throws Exception
+    {
+        File baseDir = new File( getBasedir() + File.separatorChar + "test" + File.separatorChar + "resources" );
+        String docName = "file.with.dot.in.name.xml";
+
+        RenderingContext renderingContext = new RenderingContext( baseDir, docName, "", "xml" );
+        assertEquals( "file.with.dot.in.name.html", renderingContext.getOutputName() );
+        assertEquals( ".", renderingContext.getRelativePath() );
+
+        docName = "index.xml.vm";
+
+        renderingContext = new RenderingContext( baseDir, docName, "", "xml" );
+        assertEquals( "index.html", renderingContext.getOutputName() );
+        assertEquals( ".", renderingContext.getRelativePath() );
+
+        docName = "download.apt.vm";
+
+        renderingContext = new RenderingContext( baseDir, docName, "", "apt" );
+        assertEquals( "download.html", renderingContext.getOutputName() );
+        assertEquals( ".", renderingContext.getRelativePath() );
+    }
+
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/util/ByLineReaderSourceTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/util/ByLineReaderSourceTest.java
new file mode 100644
index 0000000..85f00b0
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/util/ByLineReaderSourceTest.java
@@ -0,0 +1,66 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringReader;
+
+import org.apache.maven.doxia.parser.ParseException;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Unit test for {@link org.apache.maven.doxia.util.ByLineReaderSource}.
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 1, 2005
+ */
+public class ByLineReaderSourceTest extends TestCase
+{
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testUse() throws ParseException
+    {
+        ByLineReaderSource r = new ByLineReaderSource(
+            new StringReader( "1 \n2\n3" ) );
+        assertEquals( -1, r.getLineNumber() );
+        assertEquals( "", r.getName() );
+
+        assertEquals( "1 ", r.getNextLine() );
+        assertEquals( "2", r.getNextLine() );
+        r.ungetLine();
+        assertEquals( "2", r.getNextLine() );
+        r.ungetLine();
+        try
+        {
+            r.ungetLine();
+            fail();
+        }
+        catch ( IllegalStateException e )
+        {
+            // ok;
+        }
+        assertEquals( "2", r.getNextLine() );
+        assertEquals( "3", r.getNextLine() );
+        assertEquals( null, r.getNextLine() );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/util/DoxiaUtilsTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/util/DoxiaUtilsTest.java
new file mode 100644
index 0000000..9f661c3
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/util/DoxiaUtilsTest.java
@@ -0,0 +1,242 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.text.ParseException;
+
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.codehaus.plexus.PlexusTestCase;
+
+/**
+ * Test case for <code>DoxiaUtils</code>.
+ *
+ * @author ltheussl
+ * @version $Id: DoxiaUtilsTest.java 781180 2009-06-02 21:40:15Z vsiveton $
+ */
+public class DoxiaUtilsTest
+    extends PlexusTestCase
+{
+    /**
+     * Verify the expected results.
+     */
+    public void testIsInternalLink()
+    {
+        String link = "#anchor";
+        assertTrue( "Should be an internal link: " + link,
+            DoxiaUtils.isInternalLink( link ) );
+
+        link = "http://maven.apache.org/index.html#anchor";
+        assertFalse( "Should NOT be an internal link: " + link,
+            DoxiaUtils.isInternalLink( link ) );
+
+        link = "./index.html";
+        assertFalse( "Should NOT be an internal link: " + link,
+            DoxiaUtils.isInternalLink( link ) );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testIsExternalLink()
+    {
+        String link = "http://maven.apache.org/";
+        assertTrue( "Should be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "https://maven.apache.org/";
+        assertTrue( "Should be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "HTTPS://MAVEN.APACHE.ORG/";
+        assertTrue( "Should be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "ftp:/maven.apache.org/";
+        assertTrue( "Should be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "mailto:maven at apache.org";
+        assertTrue( "Should be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "file:/index.html";
+        assertTrue( "Should be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "resource_type://domain:port/filepathname?query_string#anchor";
+        assertTrue( "Should be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "example.pdf";
+        assertFalse( "Should NOT be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "./index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "../index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        // Windows style separators "\" are not allowed
+
+        link = "file:\\index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = ".\\index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+
+        link = "..\\index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            DoxiaUtils.isExternalLink( link ) );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testIsLocalLink()
+    {
+        String link = "index.html";
+        assertTrue( "Should be a local link: " + link,
+            DoxiaUtils.isLocalLink( link ) );
+
+        link = "./index.html";
+        assertTrue( "Should be a local link: " + link,
+            DoxiaUtils.isLocalLink( link ) );
+
+        link = "../index.html";
+        assertTrue( "Should be a local link: " + link,
+            DoxiaUtils.isLocalLink( link ) );
+
+        link = "#anchor";
+        assertFalse( "Should NOT be a local link: " + link,
+            DoxiaUtils.isLocalLink( link ) );
+
+        link = "http://maven.apache.org/";
+        assertFalse( "Should NOT be a local link: " + link,
+            DoxiaUtils.isLocalLink( link ) );
+
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testEncodeId()
+    {
+        assertEquals( DoxiaUtils.encodeId( null ), null );
+        assertEquals( DoxiaUtils.encodeId( "" ), "a" );
+        assertEquals( DoxiaUtils.encodeId( " " ), "a" );
+        assertEquals( DoxiaUtils.encodeId( " _ " ), "a_" );
+        assertEquals( DoxiaUtils.encodeId( "1" ), "a1" );
+        assertEquals( DoxiaUtils.encodeId( "1anchor" ), "a1anchor" );
+        assertEquals( DoxiaUtils.encodeId( "_anchor" ), "a_anchor" );
+        assertEquals( DoxiaUtils.encodeId( "a b-c123 " ), "a_b-c123" );
+        assertEquals( DoxiaUtils.encodeId( "   anchor" ), "anchor" );
+        assertEquals( DoxiaUtils.encodeId( "myAnchor" ), "myAnchor" );
+        assertEquals( DoxiaUtils.encodeId( "my&Anchor" ), "my%26Anchor" );
+        assertEquals( DoxiaUtils.encodeId( "H\u00E5kon" ), "H%c3%a5kon" );
+        assertEquals( DoxiaUtils.encodeId( "H\u00E5kon", true ), "Hkon" );
+        assertEquals( DoxiaUtils.encodeId( "Theu\u00DFl" ), "Theu%c3%9fl" );
+        assertEquals( DoxiaUtils.encodeId( "Theu\u00DFl", true ), "Theul" );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testIsValidId()
+    {
+        assertFalse( DoxiaUtils.isValidId( null ) );
+        assertFalse( DoxiaUtils.isValidId( "" ) );
+        assertFalse( DoxiaUtils.isValidId( " " ) );
+        assertFalse( DoxiaUtils.isValidId( " _ " ) );
+        assertFalse( DoxiaUtils.isValidId( "1" ) );
+        assertFalse( DoxiaUtils.isValidId( "1anchor" ) );
+        assertFalse( DoxiaUtils.isValidId( "_anchor" ) );
+        assertFalse( DoxiaUtils.isValidId( "a b-c123 " ) );
+        assertFalse( DoxiaUtils.isValidId( "   anchor" ) );
+        assertFalse( DoxiaUtils.isValidId( "my&Anchor" ) );
+        assertTrue( DoxiaUtils.isValidId( "myAnchor" ) );
+        assertTrue( DoxiaUtils.isValidId( "a_" ) );
+        assertTrue( DoxiaUtils.isValidId( "a-" ) );
+        assertTrue( DoxiaUtils.isValidId( "a:" ) );
+        assertTrue( DoxiaUtils.isValidId( "a." ) );
+        assertTrue( DoxiaUtils.isValidId( "index.html" ) );
+        assertFalse( DoxiaUtils.isValidId( "Theu\u00DFl" ) );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testParseDate()
+    {
+        final int year = 1973;
+        final int month = 1;
+        final int day = 27;
+
+        try
+        {
+            final Date feb27 = new GregorianCalendar( year, month, day ).getTime();
+            assertEquals( feb27, DoxiaUtils.parseDate( "27.02.1973" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "27. 02. 1973" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "1973-02-27" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "1973/02/27" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "27 Feb 1973" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "27 Feb. 1973" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "Feb. 27, 1973" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "Feb 27, '73" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "February 27, 1973" ) );
+            assertEquals( feb27, DoxiaUtils.parseDate( "19730227" ) );
+
+            assertEquals( new GregorianCalendar( year, 0, 1 ).getTime(), DoxiaUtils.parseDate( "1973" ) );
+
+            final Date feb1 = new GregorianCalendar( year, 1, 1 ).getTime();
+            assertEquals( feb1, DoxiaUtils.parseDate( "February 1973" ) );
+            assertEquals( feb1, DoxiaUtils.parseDate( "Feb. 1973" ) );
+            assertEquals( feb1, DoxiaUtils.parseDate( "February '73" ) );
+            assertEquals( feb1, DoxiaUtils.parseDate( "Feb. '73" ) );
+
+            assertNotNull( DoxiaUtils.parseDate( "Today" ) );
+            assertNotNull( DoxiaUtils.parseDate( "NOW" ) );
+        }
+        catch ( ParseException ex )
+        {
+            fail( ex.getMessage() );
+        }
+
+        try
+        {
+            DoxiaUtils.parseDate( "yesterday" ).getTime();
+            fail();
+        }
+        catch ( ParseException ex )
+        {
+            assertNotNull( ex );
+        }
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/util/HtmlToolsTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/util/HtmlToolsTest.java
new file mode 100644
index 0000000..8db9257
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/util/HtmlToolsTest.java
@@ -0,0 +1,159 @@
+package org.apache.maven.doxia.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.net.URLEncoder;
+import java.util.Locale;
+
+import org.codehaus.plexus.PlexusTestCase;
+
+/**
+ * Test case for <code>HtmlTools</code>.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: HtmlToolsTest.java 778640 2009-05-26 09:58:50Z vsiveton $
+ */
+public class HtmlToolsTest
+    extends PlexusTestCase
+{
+    /**
+     * Verify the expected results.
+     */
+    public void testEscapeHTML()
+    {
+        assertEquals( HtmlTools.escapeHTML( null ), "" );
+        assertEquals( HtmlTools.escapeHTML( "" ), "" );
+        assertEquals( HtmlTools.escapeHTML( "\u0009" ), "\u0009" );
+        assertEquals( HtmlTools.escapeHTML( "\u0001" ), "\u0001" );
+
+        // Predefined entities
+        assertEquals( HtmlTools.escapeHTML( "<" ), "<" );
+        assertEquals( HtmlTools.escapeHTML( ">" ), ">" );
+        assertEquals( HtmlTools.escapeHTML( "&" ), "&" );
+        assertEquals( HtmlTools.escapeHTML( "\"" ), """ );
+        assertEquals( HtmlTools.escapeHTML( "\'" ), "'" );
+        assertEquals( HtmlTools.escapeHTML( "\'", false ), "\'" );
+
+        // xml mode
+        assertEquals( HtmlTools.escapeHTML( "&" ), "&amp;" );
+        assertEquals( HtmlTools.escapeHTML( "\u00e4", true ), "\u00e4" );
+        assertEquals( HtmlTools.escapeHTML( "\u00e4", false ), "&#xe4;" );
+        assertEquals( HtmlTools.escapeHTML( "\u0159", false ), "&#x159;" );
+        assertEquals( HtmlTools.escapeHTML( "\uD835\uDFED", false ), "&#x1d7ed;" );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testUnescapeHTML()
+    {
+        assertNull( HtmlTools.unescapeHTML( null ) );
+        assertEquals( "", HtmlTools.unescapeHTML( "" ) );
+        assertEquals( "\u0009", HtmlTools.unescapeHTML( "\u0009" ) );
+        assertEquals( "\u0001", HtmlTools.unescapeHTML( "\u0001" ) );
+        assertEquals( "<", HtmlTools.unescapeHTML( "<" ) );
+        assertEquals( ">", HtmlTools.unescapeHTML( ">" ) );
+        assertEquals( "&", HtmlTools.unescapeHTML( "&" ) );
+        assertEquals( "\"", HtmlTools.unescapeHTML( """ ) );
+        assertEquals( "'", HtmlTools.unescapeHTML( "'" ) );
+        assertEquals( "\'", HtmlTools.unescapeHTML( "'", true ) );
+        assertEquals( "&", HtmlTools.unescapeHTML( "&amp;" ) );
+        assertEquals( "<Français>", HtmlTools.unescapeHTML( "&lt;Fran&ccedil;ais&gt;" ) );
+        assertEquals( "\u0159", HtmlTools.unescapeHTML( "&#x159;" ) );
+        assertEquals( "\uD808\uDF45", HtmlTools.unescapeHTML( "&#x12345;" ) );
+        assertEquals( "\uD835\uDFED", HtmlTools.unescapeHTML( "&#x1d7ed;" ) );
+        assertEquals( "\uD808\uDF45\uD835\uDFED", HtmlTools.unescapeHTML( "&#x12345;&#x1d7ed;" ) );
+        assertEquals( "&#x1d7ed &#x1d7ed", HtmlTools.unescapeHTML( "&#x1d7ed &#x1d7ed" ) );
+        assertEquals( "&#x1d7ed \uD835\uDFED", HtmlTools.unescapeHTML( "&#x1d7ed &#x1d7ed;" ) );
+        assertEquals( "&#xQWER;", HtmlTools.unescapeHTML( "&#xQWER;" ) );
+        assertEquals( "\u00E5", HtmlTools.unescapeHTML( "å" ) );
+        assertEquals( "<>&\"\u00E5\u0159\uD835\uDFED",
+                      HtmlTools.unescapeHTML( "<>&"å&#x159;&#x1d7ed;" ) );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testEncodeId()
+    {
+        assertEquals( HtmlTools.encodeId( null ), null );
+        assertEquals( HtmlTools.encodeId( "" ), "a" );
+        assertEquals( HtmlTools.encodeId( " " ), "a" );
+        assertEquals( HtmlTools.encodeId( " _ " ), "a_" );
+        assertEquals( HtmlTools.encodeId( "1" ), "a1" );
+        assertEquals( HtmlTools.encodeId( "1anchor" ), "a1anchor" );
+        assertEquals( HtmlTools.encodeId( "_anchor" ), "a_anchor" );
+        assertEquals( HtmlTools.encodeId( "a b-c123 " ), "a_b-c123" );
+        assertEquals( HtmlTools.encodeId( "   anchor" ), "anchor" );
+        assertEquals( HtmlTools.encodeId( "myAnchor" ), "myAnchor" );
+        assertEquals( HtmlTools.encodeId( "H\u00E5kon" ), "Hkon" );
+        assertEquals( HtmlTools.encodeId( "Theu\u00DFl" ), "Theul" );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testEncodeURL()
+        throws Exception
+    {
+        assertNull( HtmlTools.encodeURL( null ) );
+        assertEquals( HtmlTools.encodeURL( "" ), "" );
+        assertEquals( HtmlTools.encodeURL( "http://www.example.com/?This is a simple test." ),
+                      "http://www.example.com/?This%20is%20a%20simple%20test." );
+
+        assertEquals( HtmlTools.encodeURL( "http://www.example.com/?This is a simple & short test." ),
+                      "http://www.example.com/?This%20is%20a%20simple%20&%20short%20test." );
+
+        String url = "\uD808\uDF45";
+        assertEquals( HtmlTools.encodeURL( url ), URLEncoder.encode( url, "UTF-8" ).toLowerCase( Locale.ENGLISH ) );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testIsId()
+    {
+        assertFalse( HtmlTools.isId( null ) );
+        assertFalse( HtmlTools.isId( "" ) );
+        assertFalse( HtmlTools.isId( " " ) );
+        assertFalse( HtmlTools.isId( " _ " ) );
+        assertFalse( HtmlTools.isId( "1" ) );
+        assertFalse( HtmlTools.isId( "1anchor" ) );
+        assertFalse( HtmlTools.isId( "_anchor" ) );
+        assertFalse( HtmlTools.isId( "a b-c123 " ) );
+        assertFalse( HtmlTools.isId( "   anchor" ) );
+        assertTrue( HtmlTools.isId( "myAnchor" ) );
+        assertTrue( HtmlTools.isId( "a_" ) );
+        assertTrue( HtmlTools.isId( "a-" ) );
+        assertTrue( HtmlTools.isId( "a:" ) );
+        assertTrue( HtmlTools.isId( "a." ) );
+        assertFalse( HtmlTools.isId( "Theu\u00DFl" ) );
+    }
+
+    /**
+     * Verify the expected results.
+     */
+    public void testGetHtmlTag()
+    {
+        assertNull( HtmlTools.getHtmlTag( "" ) );
+        assertNull( HtmlTools.getHtmlTag( "weirdHtmlTag" ) );
+        assertNotNull( HtmlTools.getHtmlTag( "strong" ) );
+    }
+}
diff --git a/doxia-core/src/test/java/org/apache/maven/doxia/xsd/AbstractXmlValidatorTest.java b/doxia-core/src/test/java/org/apache/maven/doxia/xsd/AbstractXmlValidatorTest.java
new file mode 100644
index 0000000..b646b39
--- /dev/null
+++ b/doxia-core/src/test/java/org/apache/maven/doxia/xsd/AbstractXmlValidatorTest.java
@@ -0,0 +1,547 @@
+package org.apache.maven.doxia.xsd;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.maven.doxia.parser.AbstractXmlParser;
+import org.codehaus.plexus.PlexusTestCase;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.SelectorUtils;
+import org.codehaus.plexus.util.xml.XmlUtil;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Abstract class to validate XML files with DTD or XSD mainly for Doxia namespaces.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AbstractXmlValidatorTest.java 784395 2009-06-13 14:00:03Z vsiveton $
+ * @since 1.0
+ */
+public abstract class AbstractXmlValidatorTest
+    extends PlexusTestCase
+{
+    /** The vm line separator */
+    protected static final String EOL = System.getProperty( "line.separator" );
+
+    /** Simple cache mechanism to load test documents. */
+    private static final Map CACHE_DOXIA_TEST_DOCUMENTS = new Hashtable();
+
+    /** Maven resource in the doxia-test-docs-XXX.jar */
+    private static final String MAVEN_RESOURCE_PATH = "META-INF/maven/org.apache.maven.doxia/doxia-test-docs/";
+
+    /** XMLReader to validate xml file */
+    private XMLReader xmlReader;
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+    }
+
+    /** {@inheritDoc} */
+    protected void tearDown()
+        throws Exception
+    {
+        super.tearDown();
+
+        xmlReader = null;
+    }
+
+    /**
+     * Validate tests documents with DTD or XSD using xerces.
+     *
+     * @throws Exception if any
+     * @see #addNamespaces(String)
+     * @see #getTestDocuments()
+     */
+    public void testValidateFiles()
+        throws Exception
+    {
+        for ( Iterator it = getTestDocuments().entrySet().iterator(); it.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) it.next();
+
+            if ( getContainer().getLogger().isDebugEnabled() )
+            {
+                getContainer().getLogger().debug( "Validate '" + entry.getKey() + "'" );
+            }
+
+            List errors = parseXML( entry.getValue().toString() );
+
+            for ( Iterator it2 = errors.iterator(); it2.hasNext(); )
+            {
+                ErrorMessage error = (ErrorMessage) it2.next();
+
+                if ( isFailErrorMessage( error.getMessage() ) )
+                {
+                    fail( entry.getKey() + EOL + error.toString() );
+                }
+                else
+                {
+                    if ( getContainer().getLogger().isDebugEnabled() )
+                    {
+                        getContainer().getLogger().debug( entry.getKey() + EOL + error.toString() );
+                    }
+                }
+            }
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Protected methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * @return a non null patterns to includes specific test files.
+     * @see AbstractXmlValidatorTest#getTestDocuments()
+     */
+    protected abstract String[] getIncludes();
+
+    /**
+     * @param content xml content not null
+     * @return xml content with the wanted Doxia namespace
+     */
+    protected abstract String addNamespaces( String content );
+
+    /**
+     * @return a map of test resources filtered by patterns from {@link #getIncludes()}.
+     * @throws IOException if any
+     * @see #getIncludes()
+     * @see #getAllTestDocuments()
+     */
+    protected Map getTestDocuments()
+        throws IOException
+    {
+        if ( getIncludes() == null )
+        {
+            return Collections.EMPTY_MAP;
+        }
+
+        Map testDocs = getAllTestDocuments();
+        Map ret = new Hashtable();
+        ret.putAll( testDocs );
+        for ( Iterator it = testDocs.keySet().iterator(); it.hasNext(); )
+        {
+            String key = it.next().toString();
+
+            for ( int i = 0; i < getIncludes().length; i++ )
+            {
+                if ( !SelectorUtils.matchPath( getIncludes()[i], key.toLowerCase( Locale.ENGLISH ) ) )
+                {
+                    ret.remove( key );
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Find test resources in the <code>doxia-test-docs-XXX.jar</code> or in an IDE project.
+     *
+     * @return a map of test resources defined as follow:
+     * <ul>
+     *   <li>key, the full url of test documents,
+     *      i.e. <code>jar:file:/.../doxia-test-docs-XXX.jar!/path/to/resource</code></li>
+     *   <li>value, the content for the resource defined by the key</li>
+     * </ul>
+     * @throws IOException if any
+     */
+    protected static Map getAllTestDocuments()
+        throws IOException
+    {
+        if ( CACHE_DOXIA_TEST_DOCUMENTS != null && !CACHE_DOXIA_TEST_DOCUMENTS.isEmpty() )
+        {
+            return CACHE_DOXIA_TEST_DOCUMENTS;
+        }
+
+        URL testJar = AbstractXmlValidatorTest.class.getClassLoader().getResource( MAVEN_RESOURCE_PATH );
+        if ( testJar == null )
+        {
+            // maybe in an IDE project
+            testJar = AbstractXmlValidatorTest.class.getClassLoader().getResource( "doxia-site" );
+
+            if ( testJar == null )
+            {
+                throw new RuntimeException(
+                        "Could not find the Doxia test documents artefact i.e. doxia-test-docs-XXX.jar" );
+            }
+        }
+
+        if ( testJar.toString().startsWith( "jar" ))
+            {
+            JarURLConnection conn = (JarURLConnection) testJar.openConnection();
+            JarFile jarFile = conn.getJarFile();
+            for ( Enumeration e = jarFile.entries(); e.hasMoreElements(); )
+            {
+                JarEntry entry = (JarEntry) e.nextElement();
+
+                if ( entry.getName().startsWith( "META-INF" ) )
+                {
+                    continue;
+                }
+                if ( entry.isDirectory() )
+                {
+                    continue;
+                }
+
+                InputStream in = null;
+                try
+                {
+                    in = AbstractXmlValidatorTest.class.getClassLoader().getResource( entry.getName() ).openStream();
+                    String content = IOUtil.toString( in, "UTF-8" );
+                    CACHE_DOXIA_TEST_DOCUMENTS.put( "jar:" + conn.getJarFileURL() + "!/" + entry.getName(), content );
+                }
+                finally
+                {
+                    IOUtil.close( in );
+                }
+            }
+        }
+        else
+        {
+            // IDE projects
+            File testDocsDir = new File( testJar.getFile() ).getParentFile();
+
+            List files = FileUtils.getFiles( testDocsDir, "**/*.*", FileUtils.getDefaultExcludesAsString(), true );
+            for ( Iterator it = files.iterator(); it.hasNext();)
+            {
+                File file = new File( it.next().toString() );
+
+                if ( file.getAbsolutePath().indexOf( "META-INF" ) != -1 )
+                {
+                    continue;
+                }
+
+                Reader reader = null;
+                if ( XmlUtil.isXml( file ))
+                {
+                    reader = ReaderFactory.newXmlReader( file );
+                }
+                else
+                {
+                    reader = ReaderFactory.newReader( file, "UTF-8" );
+                }
+
+                String content = IOUtil.toString( reader );
+                CACHE_DOXIA_TEST_DOCUMENTS.put( file.toURI().toString(), content );
+            }
+        }
+
+        return CACHE_DOXIA_TEST_DOCUMENTS;
+    }
+
+    /**
+     * Filter fail message.
+     *
+     * @param message not null
+     * @return <code>true</code> if the given message will fail the test.
+     * @since 1.1.1
+     */
+    protected boolean isFailErrorMessage( String message )
+    {
+        if ( message
+                    .indexOf( "schema_reference.4: Failed to read schema document 'http://www.w3.org/2001/xml.xsd'" ) == -1
+            && message.indexOf( "cvc-complex-type.4: Attribute 'alt' must appear on element 'img'." ) == -1
+            && message.indexOf( "cvc-complex-type.2.4.a: Invalid content starting with element" ) == -1
+            && message.indexOf( "cvc-complex-type.2.4.a: Invalid content was found starting with element" ) == -1
+            && message.indexOf( "cvc-datatype-valid.1.2.1:" ) == -1 // Doxia allow space
+            && message.indexOf( "cvc-attribute.3:" ) == -1 ) // Doxia allow space
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    // ----------------------------------------------------------------------
+    // Private methods
+    // ----------------------------------------------------------------------
+
+    private XMLReader getXMLReader()
+    {
+        if ( xmlReader == null )
+        {
+            try
+            {
+                xmlReader = XMLReaderFactory.createXMLReader( "org.apache.xerces.parsers.SAXParser" );
+                xmlReader.setFeature( "http://xml.org/sax/features/validation", true );
+                xmlReader.setFeature( "http://apache.org/xml/features/validation/schema", true );
+                xmlReader.setErrorHandler( new MessagesErrorHandler() );
+                xmlReader.setEntityResolver( new AbstractXmlParser.CachedFileEntityResolver() );
+            }
+            catch ( SAXNotRecognizedException e )
+            {
+                throw new AssertionFailedError( "SAXNotRecognizedException: " + e.getMessage() );
+            }
+            catch ( SAXNotSupportedException e )
+            {
+                throw new AssertionFailedError( "SAXNotSupportedException: " + e.getMessage() );
+            }
+            catch ( SAXException e )
+            {
+                throw new AssertionFailedError( "SAXException: " + e.getMessage() );
+            }
+        }
+
+        ( (MessagesErrorHandler) xmlReader.getErrorHandler() ).clearMessages();
+
+        return xmlReader;
+    }
+
+    /**
+     * @param xmlContent
+     * @return a list of ErrorMessage
+     * @throws IOException is any
+     * @throws SAXException if any
+     */
+    private List parseXML( String xmlContent )
+        throws IOException, SAXException
+    {
+        xmlContent = addNamespaces( xmlContent );
+
+        MessagesErrorHandler errorHandler = (MessagesErrorHandler) getXMLReader().getErrorHandler();
+
+        getXMLReader().parse( new InputSource( new StringReader( xmlContent ) ) );
+
+        return errorHandler.getMessages();
+    }
+
+    private static class ErrorMessage
+        extends DefaultHandler
+    {
+        private final String level;
+        private final String publicID;
+        private final String systemID;
+        private final int lineNumber;
+        private final int columnNumber;
+        private final String message;
+
+        public ErrorMessage( String level, String publicID, String systemID, int lineNumber, int columnNumber,
+                             String message )
+        {
+            super();
+            this.level = level;
+            this.publicID = publicID;
+            this.systemID = systemID;
+            this.lineNumber = lineNumber;
+            this.columnNumber = columnNumber;
+            this.message = message;
+        }
+
+        /**
+         * @return the level
+         */
+        protected String getLevel()
+        {
+            return level;
+        }
+
+        /**
+         * @return the publicID
+         */
+        protected String getPublicID()
+        {
+            return publicID;
+        }
+        /**
+         * @return the systemID
+         */
+        protected String getSystemID()
+        {
+            return systemID;
+        }
+        /**
+         * @return the lineNumber
+         */
+        protected int getLineNumber()
+        {
+            return lineNumber;
+        }
+        /**
+         * @return the columnNumber
+         */
+        protected int getColumnNumber()
+        {
+            return columnNumber;
+        }
+        /**
+         * @return the message
+         */
+        protected String getMessage()
+        {
+            return message;
+        }
+
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            StringBuffer sb = new StringBuffer();
+
+            sb.append( level ).append( EOL );
+            sb.append( "  Public ID: " ).append( publicID ).append( EOL );
+            sb.append( "  System ID: " ).append( systemID ).append( EOL );
+            sb.append( "  Line number: " ).append( lineNumber ).append( EOL );
+            sb.append( "  Column number: " ).append( columnNumber ).append( EOL );
+            sb.append( "  Message: " ).append( message ).append( EOL );
+
+            return sb.toString();
+        }
+
+        /** {@inheritDoc} */
+        public int hashCode()
+        {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + columnNumber;
+            result = prime * result + ( ( level == null ) ? 0 : level.hashCode() );
+            result = prime * result + lineNumber;
+            result = prime * result + ( ( message == null ) ? 0 : message.hashCode() );
+            result = prime * result + ( ( publicID == null ) ? 0 : publicID.hashCode() );
+            result = prime * result + ( ( systemID == null ) ? 0 : systemID.hashCode() );
+            return result;
+        }
+
+        /** {@inheritDoc} */
+        public boolean equals( Object obj )
+        {
+            if ( this == obj )
+                return true;
+            if ( obj == null )
+                return false;
+            if ( getClass() != obj.getClass() )
+                return false;
+            ErrorMessage other = (ErrorMessage) obj;
+            if ( columnNumber != other.columnNumber )
+                return false;
+            if ( level == null )
+            {
+                if ( other.level != null )
+                    return false;
+            }
+            else if ( !level.equals( other.level ) )
+                return false;
+            if ( lineNumber != other.lineNumber )
+                return false;
+            if ( message == null )
+            {
+                if ( other.message != null )
+                    return false;
+            }
+            else if ( !message.equals( other.message ) )
+                return false;
+            if ( publicID == null )
+            {
+                if ( other.publicID != null )
+                    return false;
+            }
+            else if ( !publicID.equals( other.publicID ) )
+                return false;
+            if ( systemID == null )
+            {
+                if ( other.systemID != null )
+                    return false;
+            }
+            else if ( !systemID.equals( other.systemID ) )
+                return false;
+            return true;
+        }
+    }
+
+    private static class MessagesErrorHandler
+        extends DefaultHandler
+    {
+        private final List messages;
+
+        public MessagesErrorHandler()
+        {
+            messages = new ArrayList();
+        }
+
+        /** {@inheritDoc} */
+        public void warning( SAXParseException e )
+            throws SAXException
+        {
+            addMessage( "Warning", e );
+        }
+
+        /** {@inheritDoc} */
+        public void error( SAXParseException e )
+            throws SAXException
+        {
+            addMessage( "Error", e );
+        }
+
+        /** {@inheritDoc} */
+        public void fatalError( SAXParseException e )
+            throws SAXException
+        {
+            addMessage( "Fatal error", e );
+        }
+
+        private void addMessage( String pre, SAXParseException e )
+        {
+            ErrorMessage error =
+                new ErrorMessage( pre, e.getPublicId(), e.getSystemId(), e.getLineNumber(), e.getColumnNumber(),
+                                  e.getMessage() );
+
+            messages.add( error );
+        }
+
+        protected List getMessages()
+        {
+            return messages;
+        }
+
+        protected void clearMessages()
+        {
+            messages.clear();
+        }
+    }
+}
diff --git a/doxia-core/src/test/resources/macro/snippet/testSnippet.txt b/doxia-core/src/test/resources/macro/snippet/testSnippet.txt
new file mode 100644
index 0000000..05d2289
--- /dev/null
+++ b/doxia-core/src/test/resources/macro/snippet/testSnippet.txt
@@ -0,0 +1,14 @@
+
+preamble
+
+SNIPPET START firstId
+first snippet
+SNIPPET END   firstId
+
+interlude
+
+another snippet to start: secondId
+second snippet
+another snippet to end:   secondId
+
+conclusion
diff --git a/doxia-logging-api/pom.xml b/doxia-logging-api/pom.xml
new file mode 100644
index 0000000..2267734
--- /dev/null
+++ b/doxia-logging-api/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-logging-api</artifactId>
+  <name>Doxia :: Logging API</name>
+  <description>Doxia Logging API.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-container-default</artifactId>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/Log.java b/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/Log.java
new file mode 100644
index 0000000..7efdbfd
--- /dev/null
+++ b/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/Log.java
@@ -0,0 +1,196 @@
+package org.apache.maven.doxia.logging;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * This interface supplies the API for providing feedback to the user from
+ * a Parser or Sink, using standard <code>Doxia</code> channels.
+ * <br/>
+ * There should be no big surprises here, although you may notice that the methods accept
+ * <code>java.lang.CharSequence</code> rather than <code>java.lang.String</code>. This is provided mainly as a
+ * convenience, to enable developers to pass things like <code>java.lang.StringBuffer</code> directly into the logger,
+ * rather than formatting first by calling <code>toString()</code>.
+ * <br/>
+ * Based on <code>org.apache.maven.plugin.logging.Log</code>.
+ *
+ * @author jdcasey
+ * @author ltheussl
+ * @version $Id: Log.java 785531 2009-06-17 09:47:59Z ltheussl $
+ * @since 1.1
+ */
+public interface Log
+{
+    /** Typecode for debugging messages. */
+    int LEVEL_DEBUG = 0;
+
+    /** Typecode for informational messages. */
+    int LEVEL_INFO = 1;
+
+    /** Typecode for warning messages. */
+    int LEVEL_WARN = 2;
+
+    /** Typecode for error messages. */
+    int LEVEL_ERROR = 3;
+
+    /** Typecode for fatal error messages. */
+    int LEVEL_FATAL = 4;
+
+    /** Typecode for disabled log levels. */
+    int LEVEL_DISABLED = 5;
+
+    /**
+     * Set the current log level.
+     *
+     * @param level the log level to set.
+     */
+    void setLogLevel( int level );
+
+    /**
+     * <p>isDebugEnabled.</p>
+     *
+     * @return true if the <b>debug</b> error level is enabled.
+     */
+    boolean isDebugEnabled();
+
+    /**
+     * Send a message to the user in the <b>debug</b> error level.
+     *
+     * @param content the message to log.
+     */
+    void debug( CharSequence content );
+
+    /**
+     * Send a message (and accompanying exception) to the user in the <b>debug</b> error level.
+     * <br/>
+     * The error's stacktrace will be output when this error level is enabled.
+     *
+     * @param content the message to log.
+     * @param error the error to log.
+     */
+    void debug( CharSequence content, Throwable error );
+
+    /**
+     * Send an exception to the user in the <b>debug</b> error level.
+     * <br/>
+     * The stack trace for this exception will be output when this error level is enabled.
+     *
+     * @param error the error to log.
+     */
+    void debug( Throwable error );
+
+    /**
+     * <p>isInfoEnabled.</p>
+     *
+     * @return true if the <b>info</b> error level is enabled.
+     */
+    boolean isInfoEnabled();
+
+    /**
+     * Send a message to the user in the <b>info</b> error level.
+     *
+     * @param content the message to log.
+     */
+    void info( CharSequence content );
+
+    /**
+     * Send a message (and accompanying exception) to the user in the <b>info</b> error level.
+     * <br/>
+     * The error's stacktrace will be output when this error level is enabled.
+     *
+     * @param content the message to log.
+     * @param error the error to log.
+     */
+    void info( CharSequence content, Throwable error );
+
+    /**
+     * Send an exception to the user in the <b>info</b> error level.
+     * <br/>
+     * The stack trace for this exception will be output when this error level is enabled.
+     *
+     * @param error the error to log.
+     */
+    void info( Throwable error );
+
+    /**
+     * <p>isWarnEnabled.</p>
+     *
+     * @return true if the <b>warn</b> error level is enabled.
+     */
+    boolean isWarnEnabled();
+
+    /**
+     * Send a message to the user in the <b>warn</b> error level.
+     *
+     * @param content the message to log.
+     */
+    void warn( CharSequence content );
+
+    /**
+     * Send a message (and accompanying exception) to the user in the <b>warn</b> error level.
+     * <br/>
+     * The error's stacktrace will be output when this error level is enabled.
+     *
+     * @param content the message to log.
+     * @param error the error to log.
+     */
+    void warn( CharSequence content, Throwable error );
+
+    /**
+     * Send an exception to the user in the <b>warn</b> error level.
+     * <br/>
+     * The stack trace for this exception will be output when this error level is enabled.
+     *
+     * @param error the error to log.
+     */
+    void warn( Throwable error );
+
+    /**
+     * <p>isErrorEnabled.</p>
+     *
+     * @return true if the <b>error</b> error level is enabled.
+     */
+    boolean isErrorEnabled();
+
+    /**
+     * Send a message to the user in the <b>error</b> error level.
+     *
+     * @param content the message to log.
+     */
+    void error( CharSequence content );
+
+    /**
+     * Send a message (and accompanying exception) to the user in the <b>error</b> error level.
+     * <br/>
+     * The error's stacktrace will be output when this error level is enabled.
+     *
+     * @param content the message to log.
+     * @param error the error to log.
+     */
+    void error( CharSequence content, Throwable error );
+
+    /**
+     * Send an exception to the user in the <b>error</b> error level.
+     * <br/>
+     * The stack trace for this exception will be output when this error level is enabled.
+     *
+     * @param error the error to log.
+     */
+    void error( Throwable error );
+}
diff --git a/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/LogEnabled.java b/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/LogEnabled.java
new file mode 100644
index 0000000..5a2de82
--- /dev/null
+++ b/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/LogEnabled.java
@@ -0,0 +1,40 @@
+package org.apache.maven.doxia.logging;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+/**
+ * An interface for <code>Doxia</code> components (mainly Sink, Parser and Macro) that need the ability to log.
+ * <br/>
+ * Based on <code>org.codehaus.plexus.logging.LogEnabled</code>.
+ *
+ * @author ltheussl
+ * @version $Id: LogEnabled.java 733395 2009-01-10 23:09:40Z ltheussl $
+ * @since 1.1
+ */
+public interface LogEnabled
+{
+    /**
+     * Enable a <code>Doxia</code> logger for this <code>Doxia</code> component.
+     *
+     * @param log a Log.
+     */
+    void enableLogging( Log log );
+}
diff --git a/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/PlexusLoggerWrapper.java b/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/PlexusLoggerWrapper.java
new file mode 100644
index 0000000..8c61b52
--- /dev/null
+++ b/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/PlexusLoggerWrapper.java
@@ -0,0 +1,181 @@
+package org.apache.maven.doxia.logging;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.codehaus.plexus.logging.Logger;
+
+/**
+ * Wrap a Plexus logger into a Doxia logger.
+ * Based on org.apache.maven.plugin.logging.Log.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: PlexusLoggerWrapper.java 785531 2009-06-17 09:47:59Z ltheussl $
+ * @since 1.1
+ */
+public class PlexusLoggerWrapper
+    implements Log
+{
+    private final Logger logger;
+
+    /**
+     * <p>Constructor for PlexusLoggerWrapper.</p>
+     *
+     * @param logger the Plexus logger to wrap.
+     */
+    public PlexusLoggerWrapper( Logger logger )
+    {
+        this.logger = logger;
+    }
+
+    /** {@inheritDoc} */
+    public void setLogLevel( int level )
+    {
+        if ( level <= LEVEL_DEBUG )
+        {
+            logger.setThreshold( Logger.LEVEL_DEBUG );
+        }
+        else if ( level <= LEVEL_INFO )
+        {
+            logger.setThreshold( Logger.LEVEL_INFO );
+        }
+        else if ( level <= LEVEL_WARN )
+        {
+            logger.setThreshold( Logger.LEVEL_WARN );
+        }
+        else if ( level <= LEVEL_ERROR )
+        {
+            logger.setThreshold( Logger.LEVEL_ERROR );
+        }
+        else
+        {
+            logger.setThreshold( Logger.LEVEL_DISABLED );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void debug( CharSequence content )
+    {
+        logger.debug( toString( content ) );
+    }
+
+    /** {@inheritDoc} */
+    public void debug( CharSequence content, Throwable error )
+    {
+        logger.debug( toString( content ), error );
+    }
+
+    /** {@inheritDoc} */
+    public void debug( Throwable error )
+    {
+        logger.debug( "", error );
+    }
+
+    /** {@inheritDoc} */
+    public void info( CharSequence content )
+    {
+        logger.info( toString( content ) );
+    }
+
+    /** {@inheritDoc} */
+    public void info( CharSequence content, Throwable error )
+    {
+        logger.info( toString( content ), error );
+    }
+
+    /** {@inheritDoc} */
+    public void info( Throwable error )
+    {
+        logger.info( "", error );
+    }
+
+    /** {@inheritDoc} */
+    public void warn( CharSequence content )
+    {
+        logger.warn( toString( content ) );
+    }
+
+    /** {@inheritDoc} */
+    public void warn( CharSequence content, Throwable error )
+    {
+        logger.warn( toString( content ), error );
+    }
+
+    /** {@inheritDoc} */
+    public void warn( Throwable error )
+    {
+        logger.warn( "", error );
+    }
+
+    /** {@inheritDoc} */
+    public void error( CharSequence content )
+    {
+        logger.error( toString( content ) );
+    }
+
+    /** {@inheritDoc} */
+    public void error( CharSequence content, Throwable error )
+    {
+        logger.error( toString( content ), error );
+    }
+
+    /** {@inheritDoc} */
+    public void error( Throwable error )
+    {
+        logger.error( "", error );
+    }
+
+    /** {@inheritDoc} */
+    public boolean isDebugEnabled()
+    {
+        return logger.isDebugEnabled();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isInfoEnabled()
+    {
+        return logger.isInfoEnabled();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isWarnEnabled()
+    {
+        return logger.isWarnEnabled();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isErrorEnabled()
+    {
+        return logger.isErrorEnabled();
+    }
+
+    // ----------------------------------------------------------------------
+    // Private methods
+    // ----------------------------------------------------------------------
+
+    private String toString( CharSequence content )
+    {
+        if ( content == null )
+        {
+            return "";
+        }
+
+        return content.toString();
+    }
+}
diff --git a/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/SystemStreamLog.java b/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/SystemStreamLog.java
new file mode 100644
index 0000000..86af71b
--- /dev/null
+++ b/doxia-logging-api/src/main/java/org/apache/maven/doxia/logging/SystemStreamLog.java
@@ -0,0 +1,239 @@
+package org.apache.maven.doxia.logging;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Logger with "standard" output and error output stream. The log prefix is voluntarily in lower case.
+ * <br/>
+ * Based on <code>org.apache.maven.plugin.logging.SystemStreamLog</code>.
+ *
+ * @author jdcasey
+ * @author ltheussl
+ * @version $Id: SystemStreamLog.java 733395 2009-01-10 23:09:40Z ltheussl $
+ * @since 1.1
+ */
+public class SystemStreamLog
+    implements Log
+{
+    private static final String EOL = System.getProperty( "line.separator" );
+
+    private int currentLevel = LEVEL_INFO;
+
+    /** {@inheritDoc} */
+    public void setLogLevel( int level )
+    {
+        if ( level <= LEVEL_DEBUG )
+        {
+            currentLevel = LEVEL_DEBUG;
+        }
+        else if ( level <= LEVEL_INFO )
+        {
+            currentLevel = LEVEL_INFO;
+        }
+        else if ( level <= LEVEL_WARN )
+        {
+            currentLevel = LEVEL_WARN;
+        }
+        else if ( level <= LEVEL_ERROR )
+        {
+            currentLevel = LEVEL_ERROR;
+        }
+        else
+        {
+            currentLevel = LEVEL_DISABLED;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void debug( CharSequence content )
+    {
+        if ( isDebugEnabled() )
+        {
+            print( "debug", content );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void debug( CharSequence content, Throwable error )
+    {
+        if ( isDebugEnabled() )
+        {
+            print( "debug", content, error );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void debug( Throwable error )
+    {
+        if ( isDebugEnabled() )
+        {
+            print( "debug", error );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void info( CharSequence content )
+    {
+        if ( isInfoEnabled() )
+        {
+            print( "info", content );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void info( CharSequence content, Throwable error )
+    {
+        if ( isInfoEnabled() )
+        {
+            print( "info", content, error );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void info( Throwable error )
+    {
+        if ( isInfoEnabled() )
+        {
+            print( "info", error );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void warn( CharSequence content )
+    {
+        if ( isWarnEnabled() )
+        {
+            print( "warn", content );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void warn( CharSequence content, Throwable error )
+    {
+        if ( isWarnEnabled() )
+        {
+            print( "warn", content, error );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void warn( Throwable error )
+    {
+        if ( isWarnEnabled() )
+        {
+            print( "warn", error );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void error( CharSequence content )
+    {
+        if ( isErrorEnabled() )
+        {
+            System.err.println( "[error] " + content.toString() );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void error( CharSequence content, Throwable error )
+    {
+        if ( isErrorEnabled() )
+        {
+            StringWriter sWriter = new StringWriter();
+            PrintWriter pWriter = new PrintWriter( sWriter );
+
+            error.printStackTrace( pWriter );
+
+            System.err.println( "[error] " + content.toString()
+                + EOL + EOL + sWriter.toString() );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void error( Throwable error )
+    {
+        if ( isErrorEnabled() )
+        {
+            StringWriter sWriter = new StringWriter();
+            PrintWriter pWriter = new PrintWriter( sWriter );
+
+            error.printStackTrace( pWriter );
+
+            System.err.println( "[error] " + sWriter.toString() );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public boolean isDebugEnabled()
+    {
+        return ( currentLevel <= LEVEL_DEBUG );
+    }
+
+    /** {@inheritDoc} */
+    public boolean isInfoEnabled()
+    {
+        return ( currentLevel <= LEVEL_INFO );
+    }
+
+    /** {@inheritDoc} */
+    public boolean isWarnEnabled()
+    {
+        return ( currentLevel <= LEVEL_WARN );
+    }
+
+    /** {@inheritDoc} */
+    public boolean isErrorEnabled()
+    {
+        return ( currentLevel <= LEVEL_ERROR );
+    }
+
+      //
+     // private
+    //
+
+    private void print( String prefix, CharSequence content )
+    {
+        System.out.println( "[" + prefix + "] " + content.toString() );
+    }
+
+    private void print( String prefix, Throwable error )
+    {
+        StringWriter sWriter = new StringWriter();
+        PrintWriter pWriter = new PrintWriter( sWriter );
+
+        error.printStackTrace( pWriter );
+
+        System.out.println( "[" + prefix + "] " + sWriter.toString() );
+    }
+
+    private void print( String prefix, CharSequence content, Throwable error )
+    {
+        StringWriter sWriter = new StringWriter();
+        PrintWriter pWriter = new PrintWriter( sWriter );
+
+        error.printStackTrace( pWriter );
+
+        System.out.println( "[" + prefix + "] " + content.toString()
+            + EOL + EOL + sWriter.toString() );
+    }
+}
diff --git a/doxia-logging-api/src/site/site.xml b/doxia-logging-api/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-logging-api/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-maven-plugin/pom.xml b/doxia-maven-plugin/pom.xml
new file mode 100644
index 0000000..9eca795
--- /dev/null
+++ b/doxia-maven-plugin/pom.xml
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.doxia</groupId>
+    <artifactId>doxia</artifactId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-maven-plugin</artifactId>
+  <packaging>maven-plugin</packaging>
+
+  <name>Doxia :: Maven Plugin</name>
+  <description>A Maven plugin for Doxia.</description>
+
+  <prerequisites>
+    <maven>2.0.2</maven>
+  </prerequisites>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>doxia-book</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>doxia-core</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>doxia-logging-api</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+
+    <!-- doxia modules ordered -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-apt</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-docbook-simple</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-itext</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-latex</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-xdoc</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-xhtml</artifactId>
+      <version>${projectVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-decoration-model</artifactId>
+      <version>1.1.2</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-doxia-tools</artifactId>
+      <version>1.0.1</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.maven.doxia</groupId>
+          <artifactId>doxia-decoration-model</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-plugin-plugin</artifactId>
+          <version>2.5.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-shade-plugin</artifactId>
+          <version>1.2.2</version>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <!-- Backward compatibility with Maven 2.0.x (MNG-3402) -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <finalName>${project.build.finalName}</finalName>
+              <createDependencyReducedPom>false</createDependencyReducedPom>
+              <keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
+              <transformers>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer" />
+              </transformers>
+              <artifactSet>
+                <includes>
+                  <include>org.apache.maven.doxia:doxia-sink-api</include>
+                  <include>org.apache.maven.doxia:doxia-logging-api</include>
+                </includes>
+              </artifactSet>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <configuration>
+          <comparisonVersion>1.1</comparisonVersion>
+          <excludes>
+            <!-- exclude shaded packages -->
+            <exclude>org/apache/maven/doxia/logging/**</exclude>
+            <exclude>org/apache/maven/doxia/sink/**</exclude>
+            <exclude>org/codehaus/doxia/sink/**</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generated-helpmojo</id>
+            <goals>
+              <goal>helpmojo</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <version>2.5.1</version>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
diff --git a/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/Book.java b/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/Book.java
new file mode 100644
index 0000000..710a7e8
--- /dev/null
+++ b/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/Book.java
@@ -0,0 +1,97 @@
+package org.apache.maven.doxia.plugin;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+/**
+ * A model for a Book.
+ *
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: Book.java 740545 2009-02-04 01:03:50Z vsiveton $
+ * @since 1.0
+ */
+public class Book
+{
+    /** Path to the book descriptor file. */
+    private String descriptor;
+
+    /** The list of formats to produce. */
+    private List formats;
+
+    /** The base directory of source files. */
+    private String directory;
+
+    /** Files to include. */
+    private List includes;
+
+    /** Files to exclude. */
+    private List excludes;
+
+    /**
+     * Returns the path to the book descriptor file.
+     *
+     * @return the book descriptor file.
+     */
+    public String getDescriptor()
+    {
+        return descriptor;
+    }
+
+    /**
+     * Returns the list of {@link Format}s to produce.
+     *
+     * @return the list of formats.
+     */
+    public List getFormats()
+    {
+        return formats;
+    }
+
+    /**
+     * Returns the base directory of source files.
+     *
+     * @return the base directory.
+     */
+    public String getDirectory()
+    {
+        return directory;
+    }
+
+    /**
+     * Returns the list of files to include.
+     *
+     * @return the list of files to include.
+     */
+    public List getIncludes()
+    {
+        return includes;
+    }
+
+    /**
+     * Returns the list of files to exclude.
+     *
+     * @return the list of files to exclude.
+     */
+    public List getExcludes()
+    {
+        return excludes;
+    }
+}
diff --git a/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/DoxiaRenderBooksMojo.java b/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/DoxiaRenderBooksMojo.java
new file mode 100644
index 0000000..d1246b7
--- /dev/null
+++ b/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/DoxiaRenderBooksMojo.java
@@ -0,0 +1,336 @@
+package org.apache.maven.doxia.plugin;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.book.BookDoxia;
+import org.apache.maven.doxia.book.BookDoxiaException;
+import org.apache.maven.doxia.book.InvalidBookDescriptorException;
+import org.apache.maven.doxia.book.model.BookModel;
+import org.apache.maven.doxia.book.services.validation.ValidationResult;
+import org.apache.maven.doxia.tools.SiteTool;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * A Mojo to create books in different output formats.
+ *
+ * @goal render-books
+ * @author <a href="mailto:trygvis at inamo.no">Trygve Laugstøl</a>
+ * @version $Id: DoxiaRenderBooksMojo.java 805701 2009-08-19 08:36:28Z ltheussl $
+ * @since 1.0
+ */
+public class DoxiaRenderBooksMojo
+    extends AbstractMojo
+{
+    /** System EOL. */
+    private static final String LINE_SEPARATOR = System.getProperty( "line.separator" );
+
+    // ----------------------------------------------------------------------
+    // Mojo components
+    // ----------------------------------------------------------------------
+
+    /**
+     * BookDoxia component
+     *
+     * @component
+     */
+    private BookDoxia bookDoxia;
+
+    /**
+     * SiteTool.
+     *
+     * @component
+     */
+    protected SiteTool siteTool;
+
+    // ----------------------------------------------------------------------
+    // Mojo parameters
+    // ----------------------------------------------------------------------
+
+    /**
+     * A list of books.
+     *
+     * @parameter
+     * @required
+     */
+    private List books;
+
+    /**
+     * Base directory of the project.
+     *
+     * @parameter default-value="${basedir}"
+     */
+    private File basedir;
+
+    /**
+     * Directory containing the generated project docs.
+     *
+     * @parameter default-value="${project.build.directory}/generated-site"
+     */
+    private File generatedDocs;
+
+    /**
+     * A comma separated list of locales supported by Maven. The first valid token will be the default Locale
+     * for this instance of the Java Virtual Machine.
+     *
+     * @parameter default-value="${locales}"
+     */
+    protected String locales;
+
+    /**
+     * Specifies the input encoding.
+     *
+     * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}"
+     */
+    private String inputEncoding;
+
+    /**
+     * Specifies the output encoding.
+     *
+     * @parameter expression="${outputEncoding}" default-value="${project.reporting.outputEncoding}"
+     */
+    private String outputEncoding;
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     *
+     * Executes the Mojo.
+     */
+    public void execute()
+        throws MojoExecutionException, MojoFailureException
+    {
+        for ( Iterator it = books.iterator(); it.hasNext(); )
+        {
+            Book book = (Book) it.next();
+
+            // ----------------------------------------------------------------------
+            // Validate
+            // ----------------------------------------------------------------------
+
+            if ( StringUtils.isEmpty( book.getDescriptor() ) )
+            {
+                throw new MojoFailureException( "Invalid configuration: "
+                    + "The book is required to have a descriptor set." );
+            }
+
+            if ( StringUtils.isEmpty( book.getDirectory() ) )
+            {
+                throw new MojoFailureException( "Invalid configuration: "
+                    + "The book is required to have a directory set." );
+            }
+
+            if ( book.getFormats() == null || book.getFormats().size() == 0 )
+            {
+                throw new MojoFailureException( "Invalid configuration: "
+                    + "The book is required to have at least one format set." );
+            }
+
+            // ----------------------------------------------------------------------
+            //
+            // ----------------------------------------------------------------------
+
+            File descriptor = new File( basedir, book.getDescriptor() );
+
+            String includes = "";
+
+            if ( book.getIncludes() != null )
+            {
+                for ( Iterator j = book.getIncludes().iterator(); j.hasNext(); )
+                {
+                    String include = (String) j.next();
+
+                    includes += include + ",";
+                }
+            }
+            else
+            {
+                includes = "**/*";
+            }
+
+            String excludes = "";
+
+            if ( book.getExcludes() != null )
+            {
+                for ( Iterator j = book.getExcludes().iterator(); j.hasNext(); )
+                {
+                    String exclude = (String) j.next();
+
+                    excludes += exclude + ",";
+                }
+            }
+
+            // ----------------------------------------------------------------------
+            // Find all the files to pass to the renderer.
+            // ----------------------------------------------------------------------
+
+            if ( getLog().isDebugEnabled() )
+            {
+                getLog().debug( "Locating files to include in the book:" );
+                getLog().debug( "Basedir: " + basedir );
+                getLog().debug( "Includes: " + includes );
+                getLog().debug( "Excludes: " + excludes );
+            }
+
+            List files;
+
+            try
+            {
+                files = FileUtils.getFiles( new File( basedir, book.getDirectory() ), includes, excludes );
+            }
+            catch ( IOException e )
+            {
+                throw new MojoExecutionException( "Error while looking for input files. " + "Basedir="
+                    + basedir.getAbsolutePath() + ", " + "includes=" + includes + ", " + "excludes=" + excludes, e );
+            }
+
+            // -----------------------------------------------------------------------
+            // Load the model
+            // -----------------------------------------------------------------------
+
+            BookModel bookModel;
+
+            try
+            {
+                bookModel = bookDoxia.loadBook( descriptor );
+            }
+            catch ( InvalidBookDescriptorException e )
+            {
+                throw new MojoFailureException( "Invalid book descriptor: " + LINE_SEPARATOR
+                    + formatResult( e.getValidationResult() ) );
+            }
+            catch ( BookDoxiaException e )
+            {
+                throw new MojoExecutionException( "Error while loading the book descriptor", e );
+            }
+
+            // -----------------------------------------------------------------------
+            // Render the book in all the formats
+            // -----------------------------------------------------------------------
+
+            List localesList = siteTool.getAvailableLocales( locales );
+
+            // Default is first in the list
+            Locale defaultLocale = (Locale) localesList.get( 0 );
+            Locale.setDefault( defaultLocale );
+
+            for ( Iterator iterator = localesList.iterator(); iterator.hasNext(); )
+            {
+                Locale locale = (Locale) iterator.next();
+
+                for ( Iterator j = book.getFormats().iterator(); j.hasNext(); )
+                {
+                    Format format = (Format) j.next();
+
+                    File outputDirectory = new File( generatedDocs, format.getId() );
+                    File directory = new File( outputDirectory + "/" + locale.toString(), bookModel.getId() );
+
+                    if ( locale.equals( defaultLocale ) )
+                    {
+                        directory = new File( outputDirectory, bookModel.getId() );
+                    }
+
+                    try
+                    {
+                        bookDoxia.renderBook( bookModel, format.getId(), files, directory, locale,
+                                              getInputEncoding(), getOutputEncoding() );
+                    }
+                    catch ( BookDoxiaException e )
+                    {
+                        throw new MojoExecutionException( "Error while generating book in format '"
+                            + format.getId() + "'.", e );
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Gets the input files encoding.
+     *
+     * @return The input files encoding, never <code>null</code>.
+     * @since 1.1
+     */
+    protected String getInputEncoding()
+    {
+        return ( inputEncoding == null ) ? ReaderFactory.ISO_8859_1 : inputEncoding;
+    }
+
+    /**
+     * Gets the effective reporting output files encoding.
+     *
+     * @return The effective reporting output file encoding, never <code>null</code>.
+     * @since 1.1
+     */
+    protected String getOutputEncoding()
+    {
+        return ( outputEncoding == null ) ? ReaderFactory.UTF_8 : outputEncoding;
+    }
+
+    /**
+     * Returns a formatted message of a ValidationResult.
+     *
+     * @param result the ValidationResult to format.
+     * @return the formatted result.
+     */
+    private String formatResult( ValidationResult result )
+    {
+        StringBuffer buffer = new StringBuffer();
+
+        if ( result.getErrors().size() > 0 )
+        {
+            buffer.append( "Validation errors:" );
+
+            for ( Iterator it = result.getErrors().iterator(); it.hasNext(); )
+            {
+                String error = (String) it.next();
+
+                buffer.append( LINE_SEPARATOR ).append( " " ).append( error );
+            }
+        }
+
+        if ( result.getWarnings().size() > 0 )
+        {
+            buffer.append( "Validation warnings:" );
+
+            for ( Iterator it = result.getWarnings().iterator(); it.hasNext(); )
+            {
+                String error = (String) it.next();
+
+                buffer.append( LINE_SEPARATOR ).append( " " ).append( error );
+            }
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/Format.java b/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/Format.java
new file mode 100644
index 0000000..f31aa80
--- /dev/null
+++ b/doxia-maven-plugin/src/main/java/org/apache/maven/doxia/plugin/Format.java
@@ -0,0 +1,43 @@
+package org.apache.maven.doxia.plugin;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * A model for a supported output format.
+ *
+ * @author <a href="mailto:trygve.laugstol at objectware.no">Trygve Laugstøl</a>
+ * @version $Id: Format.java 740545 2009-02-04 01:03:50Z vsiveton $
+ * @since 1.0
+ */
+public class Format
+{
+    /** A unique identifier for the format. */
+    private String id;
+
+    /**
+     * Returns the (unique) identifier of this format.
+     *
+     * @return the identifier.
+     */
+    public String getId()
+    {
+        return id;
+    }
+}
diff --git a/doxia-maven-plugin/src/site/apt/index.apt b/doxia-maven-plugin/src/site/apt/index.apt
new file mode 100644
index 0000000..0f74e89
--- /dev/null
+++ b/doxia-maven-plugin/src/site/apt/index.apt
@@ -0,0 +1,66 @@
+ -----
+ Doxia Maven Plugin
+ -----
+ Lukas Theussl
+ -----
+
+~~ Licensed to the Apache Software Foundation (ASF) under one
+~~ or more contributor license agreements.  See the NOTICE file
+~~ distributed with this work for additional information
+~~ regarding copyright ownership.  The ASF licenses this file
+~~ to you under the Apache License, Version 2.0 (the
+~~ "License"); you may not use this file except in compliance
+~~ with the License.  You may obtain a copy of the License at
+~~
+~~   http://www.apache.org/licenses/LICENSE-2.0
+~~
+~~ Unless required by applicable law or agreed to in writing,
+~~ software distributed under the License is distributed on an
+~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+~~ KIND, either express or implied.  See the License for the
+~~ specific language governing permissions and limitations
+~~ under the License.
+
+~~ NOTE: For help with the syntax of this file, see:
+~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Introduction
+
+ Doxia allows you to write books like user manuals and guides in any format supported by Doxia. Combined with the
+ Doxia Book Maven you are able to include the manuals directly in your generated site with links to the off-line
+ friendly formats like XDoc, PDF, RTF and LaTeX.
+
+* Goals Overview
+
+ The Doxia Maven Plugin has one goal:
+
+ * {{{./render-books-mojo.html}doxia:render-books}} to create books in different output formats.
+
+ First you need a simple book descriptor which is used to specify the layout of your book, i.e.
+ the ordering of the sections and the names for the chapters.
+ See {{{http://maven.apache.org/doxia/book/index.html}The Book Descriptor Reference}}
+ for a reference to the descriptor.
+
+* Usage
+
+  General instructions on how to use the Doxia Maven Plugin can be found on the {{{./usage.html}usage page}}. Some more
+  specific use cases are described in the examples given below. Last but not least, users occasionally contribute
+  additional examples, tips or errata to the
+  {{{http://docs.codehaus.org/display/MAVENUSER/PDF+Plugin}plugin's wiki page}}.
+
+  In case you still have questions regarding the plugin's usage, please have a look at the {{{./faq.html}FAQ}} and feel
+  free to contact the {{{./mail-lists.html}user mailing list}}. The posts to the mailing list are archived and could
+  already contain the answer to your question as part of an older thread. Hence, it is also worth browsing/searching
+  the {{{./mail-lists.html}mail archive}}.
+
+  If you feel like the plugin is missing a feature or has a defect, you can fill a feature request or bug report in our
+  {{{./issue-tracking.html}issue tracker}}. When creating a new issue, please provide a comprehensive description of your
+  concern. Especially for fixing bugs it is crucial that the developers can reproduce your problem. For this reason,
+  entire debug logs, POMs or most preferably little demo projects attached to the issue are very much appreciated.
+  Of course, patches are welcome, too. Contributors can check out the project from our
+  {{{./source-repository.html}source repository}} and will find supplementary information in the
+  {{{http://maven.apache.org/guides/development/guide-helping.html}guide to helping with Maven}}.
+
+* Examples
+
+ An Xdoc output example which has been rendered into this site can be viewed {{{http://maven.apache.org/doxia/doxia-example-book/index.html}here}}.
diff --git a/doxia-maven-plugin/src/site/apt/usage.apt.vm b/doxia-maven-plugin/src/site/apt/usage.apt.vm
new file mode 100644
index 0000000..ef0724b
--- /dev/null
+++ b/doxia-maven-plugin/src/site/apt/usage.apt.vm
@@ -0,0 +1,49 @@
+ -----
+ Usage
+ -----
+ Lukas Theussl
+ -----
+
+Usage
+
+ Below is a sample pom.xml illustrate how to use it.
+
++------------------------------------------------------
+      <plugin>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-maven-plugin</artifactId>
+        <version>${project.version}</version>
+        <executions>
+          <execution>
+            <phase>pre-site</phase>
+            <goals>
+              <goal>render-books</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <books>
+            <book>
+              <directory>src/books/example-book</directory>
+              <descriptor>src/books/example-book.xml</descriptor>
+              <formats>
+                <format>
+                  <id>latex</id>
+                </format>
+                <format>
+                  <id>xdoc</id>
+                </format>
+                <format>
+                  <id>pdf</id>
+                </format>
+                <format>
+                  <id>rtf</id>
+                </format>
+              </formats>
+            </book>
+          </books>
+        </configuration>
+      </plugin>
++------------------------------------------------------
+
+ See also the sample given on the main Doxia {{{http://maven.apache.org/doxia/book/index.html}site}}.
diff --git a/doxia-maven-plugin/src/site/site.xml b/doxia-maven-plugin/src/site/site.xml
new file mode 100644
index 0000000..ee59541
--- /dev/null
+++ b/doxia-maven-plugin/src/site/site.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 http://maven.apache.org/xsd/decoration-1.0.0.xsd"
+  name="Doxia Maven plugin">
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu name="Overview">
+      <item name="Introduction" href="index.html"/>
+      <item name="Goals" href="plugin-info.html"/>
+      <item name="Usage" href="usage.html"/>
+      <!--<item name="FAQ" href="faq.html"/>-->
+    </menu>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
diff --git a/doxia-modules/doxia-module-apt/pom.xml b/doxia-modules/doxia-module-apt/pom.xml
new file mode 100644
index 0000000..010c559
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-apt</artifactId>
+
+  <name>Doxia :: APT Module</name>
+  <description>A Doxia module for Almost Plain Text source documents.</description>
+
+<!-- START SNIPPET: test -->
+    <!-- test snippet -->
+<!-- END SNIPPET: test -->
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-maven-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/main/components/components.xml</descriptor>
+            <descriptor>target/generated-resources/plexus/META-INF/plexus/components.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>merge-descriptors</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-apt/src/main/components/components.xml b/doxia-modules/doxia-module-apt/src/main/components/components.xml
new file mode 100644
index 0000000..e4a8dd9
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/components/components.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<component-set>
+  <components>
+    <component>
+      <role>org.apache.maven.doxia.parser.Parser</role>
+      <role-hint>apt</role-hint>
+      <implementation>org.apache.maven.doxia.module.apt.AptParser</implementation>
+      <description></description>
+      <requirements>
+        <requirement>
+          <role>org.apache.maven.doxia.macro.manager.MacroManager</role>
+          <field-name>macroManager</field-name>
+        </requirement>
+      </requirements>
+    </component>
+  </components>
+</component-set>
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptMarkup.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptMarkup.java
new file mode 100644
index 0000000..d01141d
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptMarkup.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.markup.TextMarkup;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * This interface defines all markups and syntaxes used by the <b>APT</b> format.
+ *
+ * @see <a href="http://maven.apache.org/doxia/references/apt-format.html">http://maven.apache.org/doxia/references/apt-format.html</a>
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AptMarkup.java 657808 2008-05-19 12:11:53Z ltheussl $
+ * @since 1.0
+ */
+public interface AptMarkup
+    extends TextMarkup
+{
+    // ----------------------------------------------------------------------
+    // Markup separators
+    // ----------------------------------------------------------------------
+
+    /** APT backslash markup char: '\\' */
+    char BACKSLASH = '\\';
+
+    /** APT comment markup char: '~' */
+    char COMMENT = '~';
+
+    /** APT numbering decimal markup char: '1' */
+    char NUMBERING = '1';
+
+    /** APT numbering lower alpha markup char: 'a' */
+    char NUMBERING_LOWER_ALPHA_CHAR = 'a';
+
+    /** APT numbering lower roman markup char: 'i' */
+    char NUMBERING_LOWER_ROMAN_CHAR = 'i';
+
+    /** APT numbering upper alpha markup char: 'A' */
+    char NUMBERING_UPPER_ALPHA_CHAR = 'A';
+
+    /** APT numbering upper roman markup char: 'I' */
+    char NUMBERING_UPPER_ROMAN_CHAR = 'I';
+
+    /** APT page break markup char: '\f' */
+    char PAGE_BREAK = '\f';
+
+    /** APT percent markup char: '%' */
+    char PERCENT = '%';
+
+    /** APT tab markup char: '\t' */
+    char TAB = '\t';
+
+    // ----------------------------------------------------------------------
+    // Markup syntax
+    // ----------------------------------------------------------------------
+
+    /** Syntax for the anchor end: "}" */
+    String ANCHOR_END_MARKUP = String.valueOf( RIGHT_CURLY_BRACKET );
+
+    /** Syntax for the anchor start: "{" */
+    String ANCHOR_START_MARKUP = String.valueOf( LEFT_CURLY_BRACKET );
+
+    /** Syntax for the bold style end: ">>" */
+    String BOLD_END_MARKUP = StringUtils.repeat( String.valueOf( GREATER_THAN ), 2 );
+
+    /** Syntax for the bold style start: "<<" */
+    String BOLD_START_MARKUP = StringUtils.repeat( String.valueOf( LESS_THAN ), 2 );
+
+    /** Syntax for the boxed verbatim start: "+------+" */
+    String BOXED_VERBATIM_START_MARKUP = String.valueOf( PLUS )
+        + StringUtils.repeat( String.valueOf( MINUS ), 6 ) + String.valueOf( PLUS );
+
+    /** Syntax for the header start: " -----" */
+    String HEADER_START_MARKUP = String.valueOf( SPACE ) + StringUtils.repeat( String.valueOf( MINUS ), 5 );
+
+    /** Syntax for the horizontal rule: "========" */
+    String HORIZONTAL_RULE_MARKUP = StringUtils.repeat( String.valueOf( EQUAL ), 8 );
+
+    /** Syntax for the italic style end: ">" */
+    String ITALIC_END_MARKUP = String.valueOf( GREATER_THAN );
+
+    /** Syntax for the italic style start: "<" */
+    String ITALIC_START_MARKUP = String.valueOf( LESS_THAN );
+
+    /** Syntax for the link end: "}}" */
+    String LINK_END_MARKUP = StringUtils.repeat( String.valueOf( RIGHT_CURLY_BRACKET ), 2 );
+
+    /** Syntax for the link start: "{{{" */
+    String LINK_START_1_MARKUP = StringUtils.repeat( String.valueOf( LEFT_CURLY_BRACKET ), 3 );
+
+    /** Syntax for the link start: "}" */
+    String LINK_START_2_MARKUP = String.valueOf( RIGHT_CURLY_BRACKET );
+
+    /** Syntax for the list end: "[]" */
+    String LIST_END_MARKUP = String.valueOf( LEFT_SQUARE_BRACKET ) + String.valueOf( RIGHT_SQUARE_BRACKET );
+
+    /** Syntax for the list start: "*" */
+    String LIST_START_MARKUP = String.valueOf( STAR );
+
+    /** Syntax for the mono-spaced style end: ">>>" */
+    String MONOSPACED_END_MARKUP = StringUtils.repeat( String.valueOf( GREATER_THAN ), 3 );
+
+    /** Syntax for the mono-spaced style start: "<<<" */
+    String MONOSPACED_START_MARKUP = StringUtils.repeat( String.valueOf( LESS_THAN ), 3 );
+
+    /** Syntax for the non boxed verbatim start: "------" */
+    String NON_BOXED_VERBATIM_START_MARKUP = StringUtils.repeat( String.valueOf( MINUS ), 6 );
+
+    /** Syntax for the non breaking space: "\ " */
+    String NON_BREAKING_SPACE_MARKUP = String.valueOf( BACKSLASH ) + String.valueOf( SPACE );
+
+    /** Syntax for the page break: "\f" */
+    String PAGE_BREAK_MARKUP = String.valueOf( PAGE_BREAK );
+
+    /** Syntax for the section title start: "*" */
+    String SECTION_TITLE_START_MARKUP = String.valueOf( STAR );
+
+    /** Syntax for the table cell start: "|" */
+    String TABLE_CELL_SEPARATOR_MARKUP = String.valueOf( PIPE );
+
+    /** Syntax for the table column, centered style: "-*" */
+    String TABLE_COL_CENTERED_ALIGNED_MARKUP = StringUtils.repeat( String.valueOf( MINUS ), 2 )
+        + String.valueOf( STAR );
+
+    /** Syntax for the table column, left style: "-+" */
+    String TABLE_COL_LEFT_ALIGNED_MARKUP = StringUtils.repeat( String.valueOf( MINUS ), 2 )
+        + String.valueOf( PLUS );
+
+    /** Syntax for the table column, right style: "-:" */
+    String TABLE_COL_RIGHT_ALIGNED_MARKUP = StringUtils.repeat( String.valueOf( MINUS ), 2 )
+        + String.valueOf( COLON );
+
+    /** Syntax for the table row end: "|" */
+    String TABLE_ROW_SEPARATOR_MARKUP = String.valueOf( PIPE );
+
+    /** Syntax for the table row start: "*--" */
+    String TABLE_ROW_START_MARKUP = String.valueOf( STAR ) + StringUtils.repeat( String.valueOf( MINUS ), 2 );
+
+    /** Syntax for the boxed verbatim end: "+------+" */
+    String BOXED_VERBATIM_END_MARKUP = BOXED_VERBATIM_START_MARKUP;
+
+    /** Syntax for the non boxed verbatim end: "------" */
+    String NON_BOXED_VERBATIM_END_MARKUP = NON_BOXED_VERBATIM_START_MARKUP;
+}
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParseException.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParseException.java
new file mode 100644
index 0000000..b2a18db
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParseException.java
@@ -0,0 +1,96 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Wraps an exception when parsing apt source documents.
+ *
+ * @version $Id: AptParseException.java 638290 2008-03-18 09:45:22Z bentmann $
+ * @since 1.0
+ */
+public class AptParseException
+    extends ParseException
+{
+    /** serialVersionUID */
+    static final long serialVersionUID = 1694654412921168623L;
+
+    /**
+     * Construct a new AptParseException with the specified detail message.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     */
+    public AptParseException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Construct a new AptParseException with the specified detail message and cause.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     */
+    public AptParseException( String message, Exception e )
+    {
+        super( message, e );
+    }
+
+    /**
+     * Construct a new AptParseException with the specified cause, detail message,
+     * filename, line number and column number.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     * @param name Name of a file that couldn't be parsed.
+     * This can later be retrieved by the getFileName() method.
+     * @param line The line number where the parsing failed.
+     * This can later be retrieved by the getLineNumber() method.
+     * @param column The column number where the parsing failed.
+     * This can later be retrieved by the getColumnNumber() method.
+     */
+    public AptParseException( String message, Exception e, String name, int line, int column )
+    {
+        super( e, message, name, line, column );
+    }
+
+    /**
+     * Construct a new AptParseException with the specified detail message and cause.
+     *
+     * @param message The detailed message.
+     * This can later be retrieved by the <code>Throwable.getMessage()</code> method.
+     * @param e the cause. This can be retrieved later by the <code>Throwable.getCause()</code> method.
+     * (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
+     * @param line The line number where the parsing failed.
+     * This can later be retrieved by the getLineNumber() method.
+     * @param column The column number where the parsing failed.
+     * This can later be retrieved by the getColumnNumber() method.
+     */
+    public AptParseException( String message, Exception e, int line, int column )
+    {
+        super( message, e, line, column );
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java
new file mode 100644
index 0000000..f5266ef
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java
@@ -0,0 +1,2980 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.macro.manager.MacroNotFoundException;
+import org.apache.maven.doxia.parser.AbstractTextParser;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkAdapter;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.util.DoxiaUtils;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+/**
+ * The APT parser.
+ * <br/>
+ * Based on the <a href="http://www.xmlmind.com/aptconvert.html">APTconvert</a> project.
+ *
+ * @version $Id: AptParser.java 946932 2010-05-21 08:36:30Z ltheussl $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="apt"
+ */
+public class AptParser
+    extends AbstractTextParser
+    implements AptMarkup
+{
+    /** Title event id */
+    private static final int TITLE = 0;
+
+    /** Section 1 event id */
+    private static final int SECTION1 = 1;
+
+    /** Section 2 event id */
+    private static final int SECTION2 = 2;
+
+    /** Section 3 event id */
+    private static final int SECTION3 = 3;
+
+    /** Section 4 event id */
+    private static final int SECTION4 = 4;
+
+    /** Section 5 event id */
+    private static final int SECTION5 = 5;
+
+    /** Paragraph event id */
+    private static final int PARAGRAPH = 6;
+
+    /** Verbatim event id */
+    private static final int VERBATIM = 7;
+
+    /** Figure event id */
+    private static final int FIGURE = 8;
+
+    /** Table event id */
+    private static final int TABLE = 9;
+
+    /** List event id */
+    private static final int LIST_ITEM = 10;
+
+    /** Numbered list event id */
+    private static final int NUMBERED_LIST_ITEM = 11;
+
+    /** Definition list event id */
+    private static final int DEFINITION_LIST_ITEM = 12;
+
+    /** Horizontal rule event id */
+    private static final int HORIZONTAL_RULE = 13;
+
+    /** Page break event id */
+    private static final int PG_BREAK = 14;
+
+    /** List break event id */
+    private static final int LIST_BREAK = 15;
+
+    /** Macro event id */
+    private static final int MACRO = 16;
+
+    /** Comment event id. */
+    private static final int COMMENT_BLOCK = 17;
+
+    /** String representations of event ids */
+    private static final String TYPE_NAMES[] = {
+        "TITLE",
+        "SECTION1",
+        "SECTION2",
+        "SECTION3",
+        "SECTION4",
+        "SECTION5",
+        "PARAGRAPH",
+        "VERBATIM",
+        "FIGURE",
+        "TABLE",
+        "LIST_ITEM",
+        "NUMBERED_LIST_ITEM",
+        "DEFINITION_LIST_ITEM",
+        "HORIZONTAL_RULE",
+        "PG_BREAK",
+        "LIST_BREAK",
+        "MACRO",
+        "COMMENT_BLOCK" };
+
+    /** An array of 85 spaces. */
+    protected static final char[] SPACES;
+
+    /** Default tab width. */
+    public static final int TAB_WIDTH = 8;
+
+    // ----------------------------------------------------------------------
+    // Instance fields
+    // ----------------------------------------------------------------------
+
+    /** the AptSource. */
+    private AptSource source;
+
+    /** a block of AptSource. */
+    private Block block;
+
+    /** blockFileName. */
+    private String blockFileName;
+
+    /** blockLineNumber. */
+    private int blockLineNumber;
+
+    /** sourceContent. */
+    protected String sourceContent;
+
+    /** the sink to receive the events. */
+    protected Sink sink;
+
+    /** a line of AptSource. */
+    protected String line;
+
+    /** Map of warn messages with a String as key to describe the error type and a Set as value.
+     * Using to reduce warn messages. */
+    protected Map warnMessages;
+
+    private static final int NUMBER_OF_SPACES = 85;
+
+    static
+    {
+        SPACES = new char[NUMBER_OF_SPACES];
+
+        for ( int i = 0; i < NUMBER_OF_SPACES; i++ )
+        {
+            SPACES[i] = ' ';
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Public methods
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void parse( Reader source, Sink sink )
+        throws ParseException
+    {
+        init();
+
+        try
+        {
+            StringWriter contentWriter = new StringWriter();
+            IOUtil.copy( source, contentWriter );
+            sourceContent = contentWriter.toString();
+        }
+        catch ( IOException e )
+        {
+            throw new AptParseException( "IOException: " + e.getMessage(), e );
+        }
+
+        try
+        {
+            this.source = new AptReaderSource( new StringReader( sourceContent ) );
+
+            this.sink = sink;
+            sink.enableLogging( getLog() );
+
+            blockFileName = null;
+
+            blockLineNumber = -1;
+
+            // Lookahead line.
+            nextLine();
+
+            // Lookahead block.
+            nextBlock( /*first*/true );
+
+            // traverse comments
+            while ( ( block != null ) && ( block.getType() == COMMENT_BLOCK ) )
+            {
+                block.traverse();
+                nextBlock( /*first*/true );
+            }
+
+            traverseHead();
+
+            traverseBody();
+        }
+        catch ( AptParseException ape )
+        {
+            // TODO handle column number
+            throw new AptParseException( ape.getMessage(), ape, getSourceName(), getSourceLineNumber(), -1 );
+        }
+        finally
+        {
+            logWarnings();
+
+            setSecondParsing( false );
+            init();
+        }
+    }
+
+    /**
+     * Returns the name of the Apt source document.
+     *
+     * @return the source name.
+     */
+    public String getSourceName()
+    {
+        // Use this rather than source.getName() to report errors.
+        return blockFileName;
+    }
+
+    /**
+     * Returns the current line number of the Apt source document.
+     *
+     * @return the line number.
+     */
+    public int getSourceLineNumber()
+    {
+        // Use this rather than source.getLineNumber() to report errors.
+        return blockLineNumber;
+    }
+
+    // ----------------------------------------------------------------------
+    // Protected methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Parse the next line of the Apt source document.
+     *
+     * @throws org.apache.maven.doxia.module.apt.AptParseException if something goes wrong.
+     */
+    protected void nextLine()
+        throws AptParseException
+    {
+        line = source.getNextLine();
+    }
+
+    /**
+     * Parse the given text.
+     *
+     * @param text the text to parse.
+     * @param begin offset.
+     * @param end offset.
+     * @param sink the sink to receive the events.
+     * @throws org.apache.maven.doxia.module.apt.AptParseException if something goes wrong.
+     */
+    protected void doTraverseText( String text, int begin, int end, Sink sink )
+        throws AptParseException
+    {
+        boolean anchor = false;
+        boolean link = false;
+        boolean italic = false;
+        boolean bold = false;
+        boolean monospaced = false;
+        StringBuffer buffer = new StringBuffer( end - begin );
+
+        for ( int i = begin; i < end; ++i )
+        {
+            char c = text.charAt( i );
+            switch ( c )
+            {
+                case BACKSLASH:
+                    if ( i + 1 < end )
+                    {
+                        char escaped = text.charAt( i + 1 );
+                        switch ( escaped )
+                        {
+                            case SPACE:
+                                ++i;
+                                flushTraversed( buffer, sink );
+                                sink.nonBreakingSpace();
+                                break;
+                            case '\r':
+                            case '\n':
+                                ++i;
+                                // Skip white space which may follow a line break.
+                                while ( i + 1 < end && Character.isWhitespace( text.charAt( i + 1 ) ) )
+                                {
+                                    ++i;
+                                }
+                                flushTraversed( buffer, sink );
+                                sink.lineBreak();
+                                break;
+                            case BACKSLASH:
+                            case PIPE:
+                            case COMMENT:
+                            case EQUAL:
+                            case MINUS:
+                            case PLUS:
+                            case STAR:
+                            case LEFT_SQUARE_BRACKET:
+                            case RIGHT_SQUARE_BRACKET:
+                            case LESS_THAN:
+                            case GREATER_THAN:
+                            case LEFT_CURLY_BRACKET:
+                            case RIGHT_CURLY_BRACKET:
+                                ++i;
+                                buffer.append( escaped );
+                                break;
+                            case 'x':
+                                if ( i + 3 < end && isHexChar( text.charAt( i + 2 ) )
+                                    && isHexChar( text.charAt( i + 3 ) ) )
+                                {
+                                    int value = '?';
+                                    try
+                                    {
+                                        value = Integer.parseInt( text.substring( i + 2, i + 4 ), 16 );
+                                    }
+                                    catch ( NumberFormatException e )
+                                    {
+                                        if ( getLog().isDebugEnabled() )
+                                        {
+                                            getLog().debug( "Not a number: " + text.substring( i + 2, i + 4 ) );
+                                        }
+                                    }
+
+                                    i += 3;
+                                    buffer.append( (char) value );
+                                }
+                                else
+                                {
+                                    buffer.append( BACKSLASH );
+                                }
+                                break;
+                            case 'u':
+                                if ( i + 5 < end && isHexChar( text.charAt( i + 2 ) )
+                                    && isHexChar( text.charAt( i + 3 ) ) && isHexChar( text.charAt( i + 4 ) )
+                                    && isHexChar( text.charAt( i + 5 ) ) )
+                                {
+                                    int value = '?';
+                                    try
+                                    {
+                                        value = Integer.parseInt( text.substring( i + 2, i + 6 ), 16 );
+                                    }
+                                    catch ( NumberFormatException e )
+                                    {
+                                        if ( getLog().isDebugEnabled() )
+                                        {
+                                            getLog().debug( "Not a number: " + text.substring( i + 2, i + 6 ) );
+                                        }
+                                    }
+
+                                    i += 5;
+                                    buffer.append( (char) value );
+                                }
+                                else
+                                {
+                                    buffer.append( BACKSLASH );
+                                }
+                                break;
+                            default:
+                                if ( isOctalChar( escaped ) )
+                                {
+                                    int octalChars = 1;
+                                    if ( isOctalChar( charAt( text, end, i + 2 ) ) )
+                                    {
+                                        ++octalChars;
+                                        if ( isOctalChar( charAt( text, end, i + 3 ) ) )
+                                        {
+                                            ++octalChars;
+                                        }
+                                    }
+                                    int value = '?';
+                                    try
+                                    {
+                                        value = Integer.parseInt( text.substring( i + 1, i + 1 + octalChars ), 8 );
+                                    }
+                                    catch ( NumberFormatException e )
+                                    {
+                                        if ( getLog().isDebugEnabled() )
+                                        {
+                                            getLog().debug(
+                                                            "Not a number: "
+                                                                + text.substring( i + 1, i + 1 + octalChars ) );
+                                        }
+                                    }
+
+                                    i += octalChars;
+                                    buffer.append( (char) value );
+                                }
+                                else
+                                {
+                                    buffer.append( BACKSLASH );
+                                }
+                        }
+                    }
+                    else
+                    {
+                        buffer.append( BACKSLASH );
+                    }
+                    break;
+
+                case LEFT_CURLY_BRACKET: /*}*/
+                    if ( !anchor && !link )
+                    {
+                        if ( i + 1 < end && text.charAt( i + 1 ) == LEFT_CURLY_BRACKET /*}*/ )
+                        {
+                            ++i;
+                            link = true;
+                            flushTraversed( buffer, sink );
+
+                            String linkAnchor = null;
+
+                            if ( i + 1 < end && text.charAt( i + 1 ) == LEFT_CURLY_BRACKET /*}*/ )
+                            {
+                                ++i;
+                                StringBuffer buf = new StringBuffer();
+                                i = skipTraversedLinkAnchor( text, i + 1, end, buf );
+                                linkAnchor = buf.toString();
+                            }
+
+                            if ( linkAnchor == null )
+                            {
+                                linkAnchor = getTraversedLink( text, i + 1, end );
+                            }
+
+                            if ( AptUtils.isInternalLink( linkAnchor ) )
+                            {
+                                linkAnchor = "#" + linkAnchor;
+                            }
+
+                            int hashIndex = linkAnchor.indexOf( "#" );
+
+                            if ( hashIndex != -1 && !AptUtils.isExternalLink( linkAnchor ) )
+                            {
+                                String hash = linkAnchor.substring( hashIndex + 1 );
+
+                                if ( hash.endsWith( ".html" ) && !hash.startsWith( "./" ) )
+                                {
+                                    String msg = "Ambiguous link: '" + hash
+                                            + "'. If this is a local link, prepend \"./\"!";
+                                    logMessage( "ambiguousLink", msg );
+                                }
+
+                                if ( !DoxiaUtils.isValidId( hash ) )
+                                {
+                                    linkAnchor =
+                                        linkAnchor.substring( 0, hashIndex ) + "#"
+                                            + DoxiaUtils.encodeId( hash, true );
+
+                                    String msg = "Modified invalid link: '" + hash + "' to '" + linkAnchor + "'";
+                                    logMessage( "modifiedLink", msg );
+                                }
+                            }
+
+                            sink.link( linkAnchor );
+                        }
+                        else
+                        {
+                            anchor = true;
+                            flushTraversed( buffer, sink );
+
+                            String linkAnchor = getTraversedAnchor( text, i + 1, end );
+
+                            linkAnchor = AptUtils.encodeAnchor( linkAnchor );
+
+                            sink.anchor( linkAnchor );
+                        }
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
+
+                case /*{*/RIGHT_CURLY_BRACKET:
+                    if ( link && i + 1 < end && text.charAt( i + 1 ) == /*{*/RIGHT_CURLY_BRACKET )
+                    {
+                        ++i;
+                        link = false;
+                        flushTraversed( buffer, sink );
+                        sink.link_();
+                    }
+                    else if ( anchor )
+                    {
+                        anchor = false;
+                        flushTraversed( buffer, sink );
+                        sink.anchor_();
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
+
+                case LESS_THAN:
+                    if ( !italic && !bold && !monospaced )
+                    {
+                        if ( i + 1 < end && text.charAt( i + 1 ) == LESS_THAN )
+                        {
+                            if ( i + 2 < end && text.charAt( i + 2 ) == LESS_THAN )
+                            {
+                                i += 2;
+                                monospaced = true;
+                                flushTraversed( buffer, sink );
+                                sink.monospaced();
+                            }
+                            else
+                            {
+                                ++i;
+                                bold = true;
+                                flushTraversed( buffer, sink );
+                                sink.bold();
+                            }
+                        }
+                        else
+                        {
+                            italic = true;
+                            flushTraversed( buffer, sink );
+                            sink.italic();
+                        }
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
+
+                case GREATER_THAN:
+                    if ( monospaced && i + 2 < end && text.charAt( i + 1 ) == GREATER_THAN
+                        && text.charAt( i + 2 ) == GREATER_THAN )
+                    {
+                        i += 2;
+                        monospaced = false;
+                        flushTraversed( buffer, sink );
+                        sink.monospaced_();
+                    }
+                    else if ( bold && i + 1 < end && text.charAt( i + 1 ) == GREATER_THAN )
+                    {
+                        ++i;
+                        bold = false;
+                        flushTraversed( buffer, sink );
+                        sink.bold_();
+                    }
+                    else if ( italic )
+                    {
+                        italic = false;
+                        flushTraversed( buffer, sink );
+                        sink.italic_();
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
+
+                default:
+                    if ( Character.isWhitespace( c ) )
+                    {
+                        buffer.append( SPACE );
+
+                        // Skip to the last char of a sequence of white spaces.
+                        while ( i + 1 < end && Character.isWhitespace( text.charAt( i + 1 ) ) )
+                        {
+                            ++i;
+                        }
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+            }
+        }
+
+        if ( monospaced )
+        {
+            throw new AptParseException( "missing '" + MONOSPACED_END_MARKUP + "'" );
+        }
+        if ( bold )
+        {
+            throw new AptParseException( "missing '" + BOLD_END_MARKUP + "'" );
+        }
+        if ( italic )
+        {
+            throw new AptParseException( "missing '" + ITALIC_END_MARKUP + "'" );
+        }
+        if ( link )
+        {
+            throw new AptParseException( "missing '" + LINK_END_MARKUP + "'" );
+        }
+        if ( anchor )
+        {
+            throw new AptParseException( "missing '" + ANCHOR_END_MARKUP + "'" );
+        }
+
+        flushTraversed( buffer, sink );
+    }
+
+    // -----------------------------------------------------------------------
+
+    /**
+     * Returns the character at position i of the given string.
+     *
+     * @param string the string.
+     * @param length length.
+     * @param i offset.
+     * @return the character, or '\0' if i > length.
+     */
+    protected static char charAt( String string, int length, int i )
+    {
+        return ( i < length ) ? string.charAt( i ) : '\0';
+    }
+
+    /**
+     * Skip spaces.
+     *
+     * @param string string.
+     * @param length length.
+     * @param i offset.
+     * @return int.
+     */
+    protected static int skipSpace( String string, int length, int i )
+    {
+        loop: for ( ; i < length; ++i )
+        {
+            switch ( string.charAt( i ) )
+            {
+                case SPACE:
+                case TAB:
+                    break;
+                default:
+                    break loop;
+            }
+        }
+        return i;
+    }
+
+    /**
+     * Replace part of a string.
+     *
+     * @param string the string
+     * @param oldSub the substring to replace
+     * @param newSub the replacement string
+     * @return String
+     */
+    protected static String replaceAll( String string, String oldSub, String newSub )
+    {
+        StringBuffer replaced = new StringBuffer();
+        int oldSubLength = oldSub.length();
+        int begin, end;
+
+        begin = 0;
+        while ( ( end = string.indexOf( oldSub, begin ) ) >= 0 )
+        {
+            if ( end > begin )
+            {
+                replaced.append( string.substring( begin, end ) );
+            }
+            replaced.append( newSub );
+            begin = end + oldSubLength;
+        }
+        if ( begin < string.length() )
+        {
+            replaced.append( string.substring( begin ) );
+        }
+
+        return replaced.toString();
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.sourceContent = null;
+        this.sink = null;
+        this.source = null;
+        this.block = null;
+        this.blockFileName = null;
+        this.blockLineNumber = 0;
+        this.line = null;
+        this.warnMessages = null;
+    }
+
+    // ----------------------------------------------------------------------
+    // Private methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Parse the head of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseHead()
+        throws AptParseException
+    {
+        sink.head();
+
+        if ( block != null && block.getType() == TITLE )
+        {
+            block.traverse();
+            nextBlock();
+        }
+
+        sink.head_();
+    }
+
+    /**
+     * Parse the body of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseBody()
+        throws AptParseException
+    {
+        sink.body();
+
+        if ( block != null )
+        {
+            traverseSectionBlocks();
+        }
+
+        while ( block != null )
+        {
+            traverseSection( 0 );
+        }
+
+        sink.body_();
+    }
+
+    /**
+     * Parse a section of the Apt source document.
+     *
+     * @param level The section level.
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseSection( int level )
+        throws AptParseException
+    {
+        if ( block == null )
+        {
+            return;
+        }
+
+        int type = SECTION1 + level;
+
+        expectedBlock( type );
+
+        switch ( level )
+        {
+            case 0:
+                sink.section1();
+                break;
+            case 1:
+                sink.section2();
+                break;
+            case 2:
+                sink.section3();
+                break;
+            case 3:
+                sink.section4();
+                break;
+            case 4:
+                sink.section5();
+                break;
+            default:
+                break;
+        }
+
+        block.traverse();
+
+        nextBlock();
+
+        traverseSectionBlocks();
+
+        while ( block != null )
+        {
+            if ( block.getType() <= type )
+            {
+                break;
+            }
+
+            traverseSection( level + 1 );
+        }
+
+        switch ( level )
+        {
+            case 0:
+                sink.section1_();
+                break;
+            case 1:
+                sink.section2_();
+                break;
+            case 2:
+                sink.section3_();
+                break;
+            case 3:
+                sink.section4_();
+                break;
+            case 4:
+                sink.section5_();
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Parse the section blocks of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseSectionBlocks()
+        throws AptParseException
+    {
+        loop: while ( block != null )
+        {
+            switch ( block.getType() )
+            {
+                case PARAGRAPH:
+                case VERBATIM:
+                case FIGURE:
+                case TABLE:
+                case HORIZONTAL_RULE:
+                case PG_BREAK:
+                case MACRO:
+                case COMMENT_BLOCK:
+                    block.traverse();
+                    nextBlock();
+                    break;
+
+                case LIST_ITEM:
+                    traverseList();
+                    break;
+
+                case NUMBERED_LIST_ITEM:
+                    traverseNumberedList();
+                    break;
+
+                case DEFINITION_LIST_ITEM:
+                    traverseDefinitionList();
+                    break;
+
+                case LIST_BREAK:
+                    // May be this is a list break which has not been indented
+                    // very precisely.
+                    nextBlock();
+                    break;
+
+                default:
+                    // A section block which starts a new section.
+                    break loop;
+            }
+        }
+    }
+
+    /**
+     * Parse a list of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseList()
+        throws AptParseException
+    {
+        if ( block == null )
+        {
+            return;
+        }
+
+        expectedBlock( LIST_ITEM );
+
+        int listIndent = block.getIndent();
+
+        sink.list();
+
+        sink.listItem();
+
+        block.traverse();
+
+        nextBlock();
+
+        loop: while ( block != null )
+        {
+            int blockIndent = block.getIndent();
+
+            switch ( block.getType() )
+            {
+                case PARAGRAPH:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+                    /*FALLTHROUGH*/
+                case VERBATIM:
+                case MACRO:
+                case FIGURE:
+                case TABLE:
+                case HORIZONTAL_RULE:
+                case PG_BREAK:
+                    block.traverse();
+                    nextBlock();
+                    break;
+
+                case LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    if ( blockIndent > listIndent )
+                    {
+                        traverseList();
+                    }
+                    else
+                    {
+                        sink.listItem_();
+                        sink.listItem();
+                        block.traverse();
+                        nextBlock();
+                    }
+                    break;
+
+                case NUMBERED_LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    traverseNumberedList();
+                    break;
+
+                case DEFINITION_LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    traverseDefinitionList();
+                    break;
+
+                case LIST_BREAK:
+                    if ( blockIndent >= listIndent )
+                    {
+                        nextBlock();
+                    }
+                    /*FALLTHROUGH*/
+                default:
+                    // A block which ends the list.
+                    break loop;
+            }
+        }
+
+        sink.listItem_();
+        sink.list_();
+    }
+
+    /**
+     * Parse a numbered list of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseNumberedList()
+        throws AptParseException
+    {
+        if ( block == null )
+        {
+            return;
+        }
+        expectedBlock( NUMBERED_LIST_ITEM );
+        int listIndent = block.getIndent();
+
+        sink.numberedList( ( (NumberedListItem) block ).getNumbering() );
+        sink.numberedListItem();
+        block.traverse();
+        nextBlock();
+
+        loop: while ( block != null )
+        {
+            int blockIndent = block.getIndent();
+
+            switch ( block.getType() )
+            {
+                case PARAGRAPH:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+                    /*FALLTHROUGH*/
+                case VERBATIM:
+                case FIGURE:
+                case TABLE:
+                case HORIZONTAL_RULE:
+                case PG_BREAK:
+                    block.traverse();
+                    nextBlock();
+                    break;
+
+                case LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    traverseList();
+                    break;
+
+                case NUMBERED_LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    if ( blockIndent > listIndent )
+                    {
+                        traverseNumberedList();
+                    }
+                    else
+                    {
+                        sink.numberedListItem_();
+                        sink.numberedListItem();
+                        block.traverse();
+                        nextBlock();
+                    }
+                    break;
+
+                case DEFINITION_LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    traverseDefinitionList();
+                    break;
+
+                case LIST_BREAK:
+                    if ( blockIndent >= listIndent )
+                    {
+                        nextBlock();
+                    }
+                    /*FALLTHROUGH*/
+                default:
+                    // A block which ends the list.
+                    break loop;
+            }
+        }
+
+        sink.numberedListItem_();
+        sink.numberedList_();
+    }
+
+    /**
+     * Parse a definition list of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void traverseDefinitionList()
+        throws AptParseException
+    {
+        if ( block == null )
+        {
+            return;
+        }
+        expectedBlock( DEFINITION_LIST_ITEM );
+        int listIndent = block.getIndent();
+
+        sink.definitionList();
+        sink.definitionListItem();
+        block.traverse();
+        nextBlock();
+
+        loop: while ( block != null )
+        {
+            int blockIndent = block.getIndent();
+
+            switch ( block.getType() )
+            {
+                case PARAGRAPH:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+                    /*FALLTHROUGH*/
+                case VERBATIM:
+                case FIGURE:
+                case TABLE:
+                case HORIZONTAL_RULE:
+                case PG_BREAK:
+                    block.traverse();
+                    nextBlock();
+                    break;
+
+                case LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    traverseList();
+                    break;
+
+                case NUMBERED_LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    traverseNumberedList();
+                    break;
+
+                case DEFINITION_LIST_ITEM:
+                    if ( blockIndent < listIndent )
+                    {
+                        break loop;
+                    }
+
+                    if ( blockIndent > listIndent )
+                    {
+                        traverseDefinitionList();
+                    }
+                    else
+                    {
+                        sink.definition_();
+                        sink.definitionListItem_();
+                        sink.definitionListItem();
+                        block.traverse();
+                        nextBlock();
+                    }
+                    break;
+
+                case LIST_BREAK:
+                    if ( blockIndent >= listIndent )
+                    {
+                        nextBlock();
+                    }
+                    /*FALLTHROUGH*/
+                default:
+                    // A block which ends the list.
+                    break loop;
+            }
+        }
+
+        sink.definition_();
+        sink.definitionListItem_();
+        sink.definitionList_();
+    }
+
+    /**
+     * Parse the next block of the Apt source document.
+     *
+     * @throws AptParseException if something goes wrong.
+     */
+    private void nextBlock()
+        throws AptParseException
+    {
+        nextBlock( /*first*/false );
+    }
+
+    /**
+     * Parse the next block of the Apt source document.
+     *
+     * @param firstBlock True if this is the first block of the Apt source document.
+     * @throws AptParseException if something goes wrong.
+     */
+    private void nextBlock( boolean firstBlock )
+        throws AptParseException
+    {
+        // Skip open lines.
+        int length, indent, i;
+
+        skipLoop: for ( ;; )
+        {
+            if ( line == null )
+            {
+                block = null;
+                return;
+            }
+
+            length = line.length();
+            indent = 0;
+            lineLoop: for ( i = 0; i < length; ++i )
+            {
+                switch ( line.charAt( i ) )
+                {
+                    case SPACE:
+                        ++indent;
+                        break;
+                    case TAB:
+                        indent += 8;
+                        break;
+                    default:
+                        break skipLoop;
+                }
+            }
+
+            if ( i == length )
+            {
+                nextLine();
+            }
+        }
+
+        blockFileName = source.getName();
+        blockLineNumber = source.getLineNumber();
+        block = null;
+        switch ( line.charAt( i ) )
+        {
+            case STAR:
+                if ( indent == 0 )
+                {
+                    if ( charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
+                    {
+                        block = new Table( indent, line );
+                    }
+                    else if ( charAt( line, length, i + 1 ) == STAR )
+                    {
+                        if ( charAt( line, length, i + 2 ) == STAR )
+                        {
+                            if ( charAt( line, length, i + 3 ) == STAR )
+                            {
+                                block = new Section5( indent, line );
+                            }
+                            else
+                            {
+                                block = new Section4( indent, line );
+                            }
+                        }
+                        else
+                        {
+                            block = new Section3( indent, line );
+                        }
+                    }
+                    else
+                    {
+                        block = new Section2( indent, line );
+                    }
+                }
+                else
+                {
+                    block = new ListItem( indent, line );
+                }
+                break;
+            case LEFT_SQUARE_BRACKET:
+                if ( charAt( line, length, i + 1 ) == RIGHT_SQUARE_BRACKET )
+                {
+                    block = new ListBreak( indent, line );
+                }
+                else
+                {
+                    if ( indent == 0 )
+                    {
+                        block = new Figure( indent, line );
+                    }
+                    else
+                    {
+                        if ( charAt( line, length, i + 1 ) == LEFT_SQUARE_BRACKET )
+                        {
+                            int numbering;
+
+                            switch ( charAt( line, length, i + 2 ) )
+                            {
+                                case NUMBERING_LOWER_ALPHA_CHAR:
+                                    numbering = Sink.NUMBERING_LOWER_ALPHA;
+                                    break;
+                                case NUMBERING_UPPER_ALPHA_CHAR:
+                                    numbering = Sink.NUMBERING_UPPER_ALPHA;
+                                    break;
+                                case NUMBERING_LOWER_ROMAN_CHAR:
+                                    numbering = Sink.NUMBERING_LOWER_ROMAN;
+                                    break;
+                                case NUMBERING_UPPER_ROMAN_CHAR:
+                                    numbering = Sink.NUMBERING_UPPER_ROMAN;
+                                    break;
+                                case NUMBERING:
+                                default:
+                                    // The first item establishes the numbering
+                                    // scheme for the whole list.
+                                    numbering = Sink.NUMBERING_DECIMAL;
+                            }
+
+                            block = new NumberedListItem( indent, line, numbering );
+                        }
+                        else
+                        {
+                            block = new DefinitionListItem( indent, line );
+                        }
+                    }
+                }
+                break;
+            case MINUS:
+                if ( charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
+                {
+                    if ( indent == 0 )
+                    {
+                        block = new Verbatim( indent, line );
+                    }
+                    else
+                    {
+                        if ( firstBlock )
+                        {
+                            block = new Title( indent, line );
+                        }
+                    }
+                }
+                break;
+            case PLUS:
+                if ( indent == 0 && charAt( line, length, i + 1 ) == MINUS && charAt( line, length, i + 2 ) == MINUS )
+                {
+                    block = new Verbatim( indent, line );
+                }
+                break;
+            case EQUAL:
+                if ( indent == 0 && charAt( line, length, i + 1 ) == EQUAL && charAt( line, length, i + 2 ) == EQUAL )
+                {
+                    block = new HorizontalRule( indent, line );
+                }
+                break;
+            case PAGE_BREAK:
+                if ( indent == 0 )
+                {
+                    block = new PageBreak( indent, line );
+                }
+                break;
+            case PERCENT:
+                if ( indent == 0 && charAt( line, length, i + 1 ) == LEFT_CURLY_BRACKET )
+                {
+                    block = new MacroBlock( indent, line );
+                }
+                break;
+            case COMMENT:
+                if ( charAt( line, length, i + 1 ) == COMMENT )
+                {
+                    block = new Comment( line.substring( i + 2 ).trim() );
+                }
+                break;
+            default:
+                break;
+        }
+
+        if ( block == null )
+        {
+            if ( indent == 0 )
+            {
+                block = new Section1( indent, line );
+            }
+            else
+            {
+                block = new Paragraph( indent, line );
+            }
+        }
+    }
+
+    /**
+     * Checks that the current block is of the expected type.
+     *
+     * @param type the expected type.
+     * @throws AptParseException if something goes wrong.
+     */
+    private void expectedBlock( int type )
+        throws AptParseException
+    {
+        int blockType = block.getType();
+
+        if ( blockType != type )
+        {
+            throw new AptParseException( "expected " + TYPE_NAMES[type] + ", found " + TYPE_NAMES[blockType] );
+        }
+    }
+
+    // -----------------------------------------------------------------------
+
+    /**
+     * Determine if c is an octal character.
+     *
+     * @param c the character.
+     * @return boolean
+     */
+    private static boolean isOctalChar( char c )
+    {
+        return ( c >= '0' && c <= '7' );
+    }
+
+    /**
+     * Determine if c is an hex character.
+     *
+     * @param c the character.
+     * @return boolean
+     */
+    private static boolean isHexChar( char c )
+    {
+        return ( ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) );
+    }
+
+    /**
+     * Emits the text so far parsed into the given sink.
+     *
+     * @param buffer A StringBuffer that contains the text to be flushed.
+     * @param sink The sink to receive the text.
+     */
+    private static void flushTraversed( StringBuffer buffer, Sink sink )
+    {
+        if ( buffer.length() > 0 )
+        {
+            sink.text( buffer.toString() );
+            buffer.setLength( 0 );
+        }
+    }
+
+    /**
+     * Parse the given text.
+     *
+     * @param text the text to parse.
+     * @param begin offset.
+     * @param end offset.
+     * @param linkAnchor a StringBuffer.
+     * @return int
+     * @throws AptParseException if something goes wrong.
+     */
+    private static int skipTraversedLinkAnchor( String text, int begin, int end, StringBuffer linkAnchor )
+        throws AptParseException
+    {
+        int i;
+        loop: for ( i = begin; i < end; ++i )
+        {
+            char c = text.charAt( i );
+            switch ( c )
+            {
+                case RIGHT_CURLY_BRACKET:
+                    break loop;
+                case BACKSLASH:
+                    if ( i + 1 < end )
+                    {
+                        ++i;
+                        linkAnchor.append( text.charAt( i ) );
+                    }
+                    else
+                    {
+                        linkAnchor.append( BACKSLASH );
+                    }
+                    break;
+                default:
+                    linkAnchor.append( c );
+            }
+        }
+        if ( i == end )
+        {
+            throw new AptParseException( "missing '" + RIGHT_CURLY_BRACKET + "'" );
+        }
+
+        return i;
+    }
+
+    /**
+     * Parse the given text.
+     *
+     * @param text the text to parse.
+     * @param begin offset.
+     * @param end offset.
+     * @return String
+     * @throws AptParseException if something goes wrong.
+     */
+    private String getTraversedLink( String text, int begin, int end )
+        throws AptParseException
+    {
+        char previous2 = LEFT_CURLY_BRACKET;
+        char previous = LEFT_CURLY_BRACKET;
+        int i;
+
+        for ( i = begin; i < end; ++i )
+        {
+            char c = text.charAt( i );
+            if ( c == RIGHT_CURLY_BRACKET && previous == RIGHT_CURLY_BRACKET && previous2 != BACKSLASH )
+            {
+                break;
+            }
+
+            previous2 = previous;
+            previous = c;
+        }
+        if ( i == end )
+        {
+            throw new AptParseException( "missing '" + LEFT_CURLY_BRACKET + LEFT_CURLY_BRACKET + "'" );
+        }
+
+        return doGetTraversedLink( text, begin, i - 1 );
+    }
+
+    /**
+     * Parse the given text.
+     *
+     * @param text the text to parse.
+     * @param begin offset.
+     * @param end offset.
+     * @return String
+     * @throws AptParseException if something goes wrong.
+     */
+    private String getTraversedAnchor( String text, int begin, int end )
+        throws AptParseException
+    {
+        char previous = LEFT_CURLY_BRACKET;
+        int i;
+
+        for ( i = begin; i < end; ++i )
+        {
+            char c = text.charAt( i );
+            if ( c == RIGHT_CURLY_BRACKET && previous != BACKSLASH )
+            {
+                break;
+            }
+
+            previous = c;
+        }
+        if ( i == end )
+        {
+            throw new AptParseException( "missing '" + RIGHT_CURLY_BRACKET + "'" );
+        }
+
+        return doGetTraversedLink( text, begin, i );
+    }
+
+    /**
+     * Parse the given text.
+     *
+     * @param text the text to parse.
+     * @param begin offset.
+     * @param end offset.
+     * @return String
+     * @throws AptParseException if something goes wrong.
+     */
+    private String doGetTraversedLink( String text, int begin, int end )
+        throws AptParseException
+    {
+        final StringBuffer buffer = new StringBuffer( end - begin );
+
+        Sink linkSink = new SinkAdapter()
+        {
+            /** {@inheritDoc} */
+            public void lineBreak()
+            {
+                buffer.append( SPACE );
+            }
+
+            /** {@inheritDoc} */
+            public void nonBreakingSpace()
+            {
+                buffer.append( SPACE );
+            }
+
+            /** {@inheritDoc} */
+            public void text( String text )
+            {
+                buffer.append( text );
+            }
+        };
+        doTraverseText( text, begin, end, linkSink );
+
+        return buffer.toString().trim();
+    }
+
+    /**
+     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
+     *
+     * @param key not null
+     * @param msg not null
+     * @see #parse(Reader, Sink)
+     * @since 1.1.1
+     */
+    private void logMessage( String key, String msg )
+    {
+        msg = "[APT Parser] " + msg;
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( msg );
+
+            return;
+        }
+
+        if ( warnMessages == null )
+        {
+            warnMessages = new HashMap();
+        }
+
+        Set set = (Set) warnMessages.get( key );
+        if ( set == null )
+        {
+            set = new TreeSet();
+        }
+        set.add( msg );
+        warnMessages.put( key, set );
+    }
+
+    /**
+     * @since 1.1.2
+     */
+    private void logWarnings()
+    {
+        if ( getLog().isWarnEnabled() && this.warnMessages != null && !isSecondParsing() )
+        {
+            for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                Set set = (Set) entry.getValue();
+
+                for ( Iterator it2 = set.iterator(); it2.hasNext(); )
+                {
+                    String msg = (String) it2.next();
+
+                    getLog().warn( msg );
+                }
+            }
+
+            this.warnMessages = null;
+        }
+    }
+
+    // -----------------------------------------------------------------------
+
+    /** A block of an apt source document. */
+    private abstract class Block
+    {
+        /** type. */
+        protected int type;
+
+        /** indent. */
+        protected int indent;
+
+        /** text. */
+        protected String text;
+
+        /** textLength. */
+        protected int textLength;
+
+        /**
+         * Constructor.
+         *
+         * @param type the block type.
+         * @param indent indent.
+         * @throws AptParseException AptParseException
+         */
+        public Block( int type, int indent )
+            throws AptParseException
+        {
+            this( type, indent, null );
+        }
+
+        /**
+         * Constructor.
+         *
+         * @param type type.
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Block( int type, int indent, String firstLine )
+            throws AptParseException
+        {
+            this.type = type;
+            this.indent = indent;
+
+            // Skip first line ---
+            AptParser.this.nextLine();
+
+            if ( firstLine == null )
+            {
+                text = null;
+                textLength = 0;
+            }
+            else
+            {
+                // Read block ---
+                StringBuffer buffer = new StringBuffer( firstLine );
+
+                while ( AptParser.this.line != null )
+                {
+                    String l = AptParser.this.line;
+                    int length = l.length();
+                    int i = 0;
+
+                    i = skipSpace( l, length, i );
+                    if ( i == length )
+                    {
+                        // Stop after open line and skip it.
+                        AptParser.this.nextLine();
+                        break;
+                    }
+                    else if ( ( AptParser.charAt( l, length, i ) == COMMENT
+                            && AptParser.charAt( l, length, i + 1 ) == COMMENT )
+                            || type == COMMENT_BLOCK )
+                    {
+                        // parse comments as separate blocks line by line
+                        break;
+                    }
+
+                    buffer.append( EOL );
+                    buffer.append( l );
+
+                    AptParser.this.nextLine();
+                }
+
+                text = buffer.toString();
+                textLength = text.length();
+            }
+        }
+
+        /**
+         * Return the block type.
+         *
+         * @return int
+         */
+        public final int getType()
+        {
+            return type;
+        }
+
+        /**
+         * Return the block indent.
+         *
+         * @return int
+         */
+        public final int getIndent()
+        {
+            return indent;
+        }
+
+        /**
+         * Parse the block.
+         *
+         * @throws AptParseException if something goes wrong.
+         */
+        public abstract void traverse()
+            throws AptParseException;
+
+        /**
+         * Traverse the text.
+         *
+         * @param begin offset.
+         * @throws AptParseException if something goes wrong.
+         */
+        protected void traverseText( int begin )
+            throws AptParseException
+        {
+            traverseText( begin, text.length() );
+        }
+
+        /**
+         * Traverse the text.
+         *
+         * @param begin offset.
+         * @param end offset.
+         * @throws AptParseException if something goes wrong.
+         */
+        protected void traverseText( int begin, int end )
+            throws AptParseException
+        {
+            AptParser.this.doTraverseText( text, begin, end, AptParser.this.sink );
+        }
+
+        /**
+         * Skip spaces.
+         *
+         * @return int.
+         */
+        protected int skipLeadingBullets()
+        {
+            int i = skipSpaceFrom( 0 );
+            for ( ; i < textLength; ++i )
+            {
+                if ( text.charAt( i ) != STAR )
+                {
+                    break;
+                }
+            }
+            return skipSpaceFrom( i );
+        }
+
+        /**
+         * Skip brackets.
+         *
+         * @param i offset.
+         * @return int.
+         * @throws AptParseException if something goes wrong.
+         */
+        protected int skipFromLeftToRightBracket( int i )
+            throws AptParseException
+        {
+            char previous = LEFT_SQUARE_BRACKET;
+            for ( ++i; i < textLength; ++i )
+            {
+                char c = text.charAt( i );
+                if ( c == RIGHT_SQUARE_BRACKET && previous != BACKSLASH )
+                {
+                    break;
+                }
+                previous = c;
+            }
+            if ( i == textLength )
+            {
+                throw new AptParseException( "missing '" + RIGHT_SQUARE_BRACKET + "'" );
+            }
+
+            return i;
+        }
+
+        /**
+         * Skip spaces.
+         *
+         * @param i offset.
+         * @return int.
+         */
+        protected final int skipSpaceFrom( int i )
+        {
+            return AptParser.skipSpace( text, textLength, i );
+        }
+    }
+
+    /** A ListBreak Block. */
+    private class ListBreak
+        extends AptParser.Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public ListBreak( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( AptParser.LIST_BREAK, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            throw new AptParseException( "internal error: traversing list break" );
+        }
+    }
+
+    /** A Title Block. */
+    private class Title
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Title( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( TITLE, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            StringTokenizer lines = new StringTokenizer( text, EOL );
+            int separator = -1;
+            boolean firstLine = true;
+            boolean title = false;
+            boolean author = false;
+            boolean date = false;
+
+            loop: while ( lines.hasMoreTokens() )
+            {
+                String line = lines.nextToken().trim();
+                int lineLength = line.length();
+
+                if ( AptParser.charAt( line, lineLength, 0 ) == MINUS
+                    && AptParser.charAt( line, lineLength, 1 ) == MINUS
+                    && AptParser.charAt( line, lineLength, 2 ) == MINUS )
+                {
+                    switch ( separator )
+                    {
+                        case 0:
+                            if ( title )
+                            {
+                                AptParser.this.sink.title_();
+                            }
+                            else
+                            {
+                                throw new AptParseException( "missing title" );
+                            }
+                            break;
+                        case 1:
+                            if ( author )
+                            {
+                                AptParser.this.sink.author_();
+                            }
+                            break;
+                        case 2:
+                            // Note that an extra decorative line is allowed
+                            // at the end of the author.
+                            break loop;
+                        default:
+                            break;
+                    }
+
+                    ++separator;
+                    firstLine = true;
+                }
+                else
+                {
+                    if ( firstLine )
+                    {
+                        firstLine = false;
+                        switch ( separator )
+                        {
+                            case 0:
+                                title = true;
+                                AptParser.this.sink.title();
+                                break;
+                            case 1:
+                                author = true;
+                                AptParser.this.sink.author();
+                                break;
+                            case 2:
+                                date = true;
+                                AptParser.this.sink.date();
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                    else
+                    {
+                        // An implicit lineBreak separates title lines.
+                        AptParser.this.sink.lineBreak();
+                    }
+
+                    AptParser.this.doTraverseText( line, 0, lineLength, AptParser.this.sink );
+                }
+            }
+
+            switch ( separator )
+            {
+                case 0:
+                    if ( title )
+                    {
+                        AptParser.this.sink.title_();
+                    }
+                    else
+                    {
+                        throw new AptParseException( "missing title" );
+                    }
+                    break;
+                case 1:
+                    if ( author )
+                    {
+                        AptParser.this.sink.author_();
+                    }
+                    break;
+                case 2:
+                    if ( date )
+                    {
+                        AptParser.this.sink.date_();
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /** A Section Block. */
+    private abstract class Section
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param type type.
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Section( int type, int indent, String firstLine )
+            throws AptParseException
+        {
+            super( type, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            Title();
+            traverseText( skipLeadingBullets() );
+            Title_();
+        }
+
+        /** Start a title. */
+        public abstract void Title();
+
+        /** End a title. */
+        public abstract void Title_();
+    }
+
+    /** A Section1 Block. */
+    private class Section1
+        extends Section
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Section1( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( SECTION1, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void Title()
+        {
+            AptParser.this.sink.sectionTitle1();
+        }
+
+        /** {@inheritDoc} */
+        public void Title_()
+        {
+            AptParser.this.sink.sectionTitle1_();
+        }
+    }
+
+    /** A Section2 Block. */
+    private class Section2
+        extends Section
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Section2( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( SECTION2, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void Title()
+        {
+            AptParser.this.sink.sectionTitle2();
+        }
+
+        /** {@inheritDoc} */
+        public void Title_()
+        {
+            AptParser.this.sink.sectionTitle2_();
+        }
+    }
+
+    /** A Section3 Block. */
+    private class Section3
+        extends Section
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Section3( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( SECTION3, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void Title()
+        {
+            AptParser.this.sink.sectionTitle3();
+        }
+
+        /** {@inheritDoc} */
+        public void Title_()
+        {
+            AptParser.this.sink.sectionTitle3_();
+        }
+    }
+
+    /** A Section4 Block. */
+    private class Section4
+        extends Section
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Section4( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( SECTION4, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void Title()
+        {
+            AptParser.this.sink.sectionTitle4();
+        }
+
+        /** {@inheritDoc} */
+        public void Title_()
+        {
+            AptParser.this.sink.sectionTitle4_();
+        }
+    }
+
+    /** A Section5 Block. */
+    private class Section5
+        extends Section
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Section5( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( SECTION5, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void Title()
+        {
+            AptParser.this.sink.sectionTitle5();
+        }
+
+        /** {@inheritDoc} */
+        public void Title_()
+        {
+            AptParser.this.sink.sectionTitle5_();
+        }
+    }
+
+    /** A Paragraph Block. */
+    private class Paragraph
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Paragraph( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( PARAGRAPH, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            AptParser.this.sink.paragraph();
+            traverseText( skipSpaceFrom( 0 ) );
+            AptParser.this.sink.paragraph_();
+        }
+    }
+
+    /** A Comment Block. */
+    private class Comment
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param line the comment line.
+         * @throws AptParseException AptParseException
+         */
+        public Comment( String line )
+            throws AptParseException
+        {
+            super( COMMENT_BLOCK, 0, line );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            AptParser.this.sink.comment( text );
+        }
+    }
+
+    /** A Verbatim Block. */
+    private class Verbatim
+        extends Block
+    {
+        /** boxed. */
+        private boolean boxed;
+
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Verbatim( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( VERBATIM, indent, null );
+
+            // Read block (first line already skipped) ---
+
+            StringBuffer buffer = new StringBuffer();
+            char firstChar = firstLine.charAt( 0 );
+            boxed = ( firstChar == PLUS );
+
+            while ( AptParser.this.line != null )
+            {
+                String l = AptParser.this.line;
+                int length = l.length();
+
+                if ( AptParser.charAt( l, length, 0 ) == firstChar && AptParser.charAt( l, length, 1 ) == MINUS
+                    && AptParser.charAt( l, length, 2 ) == MINUS )
+                {
+                    AptParser.this.nextLine();
+
+                    break;
+                }
+
+                // Expand tabs ---
+
+                int prevColumn, column;
+
+                column = 0;
+
+                for ( int i = 0; i < length; ++i )
+                {
+                    char c = l.charAt( i );
+
+                    if ( c == TAB )
+                    {
+                        prevColumn = column;
+
+                        column = ( ( column + 1 + TAB_WIDTH - 1 ) / TAB_WIDTH ) * TAB_WIDTH;
+
+                        buffer.append( SPACES, 0, column - prevColumn );
+                    }
+                    else
+                    {
+                        ++column;
+                        buffer.append( c );
+                    }
+                }
+                buffer.append( EOL );
+
+                AptParser.this.nextLine();
+            }
+
+            // The last '\n' is mandatory before the "---" delimeter but is
+            // not part of the verbatim text.
+            textLength = buffer.length();
+
+            if ( textLength > 0 )
+            {
+                --textLength;
+
+                buffer.setLength( textLength );
+            }
+
+            text = buffer.toString();
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            AptParser.this.sink.verbatim( boxed ? SinkEventAttributeSet.BOXED : null );
+            AptParser.this.sink.text( text );
+            AptParser.this.sink.verbatim_();
+        }
+    }
+
+    /** A Figure Block. */
+    private class Figure
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Figure( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( FIGURE, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            AptParser.this.sink.figure();
+
+            int i = skipFromLeftToRightBracket( 0 );
+            AptParser.this.sink.figureGraphics( text.substring( 1, i ) );
+
+            i = skipSpaceFrom( i + 1 );
+            if ( i < textLength )
+            {
+                AptParser.this.sink.figureCaption();
+                traverseText( i );
+                AptParser.this.sink.figureCaption_();
+            }
+
+            AptParser.this.sink.figure_();
+        }
+    }
+
+    /** A Table Block. */
+    private class Table
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public Table( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( TABLE, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            int captionIndex = -1;
+            int nextLineIndex = 0;
+            int init = 2;
+            int[] justification = null;
+            int rows = 0;
+            int columns = 0;
+            StringBuffer[] cells = null;
+            boolean[] headers = null;
+            boolean grid;
+
+            AptParser.this.sink.table();
+
+            while ( nextLineIndex < textLength )
+            {
+                int i = text.indexOf( "*--", nextLineIndex );
+                if ( i < 0 )
+                {
+                    captionIndex = nextLineIndex;
+                    break;
+                }
+
+                String line;
+                i = text.indexOf( '\n', nextLineIndex );
+                if ( i < 0 )
+                {
+                    line = text.substring( nextLineIndex );
+                    nextLineIndex = textLength;
+                }
+                else
+                {
+                    line = text.substring( nextLineIndex, i );
+                    nextLineIndex = i + 1;
+                }
+                int lineLength = line.length();
+
+                if ( line.indexOf( "*--" ) == 0 )
+                {
+                    if ( init == 2 )
+                    {
+                        init = 1;
+                        justification = parseJustification( line, lineLength );
+                        columns = justification.length;
+                        cells = new StringBuffer[columns];
+                        headers = new boolean[columns];
+                        for ( i = 0; i < columns; ++i )
+                        {
+                            cells[i] = new StringBuffer();
+                            headers[i] = false;
+                        }
+                    }
+                    else
+                    {
+                        if ( traverseRow( cells, headers, justification ) )
+                        {
+                            ++rows;
+                        }
+                        justification = parseJustification( line, lineLength );
+                    }
+                }
+                else
+                {
+                    if ( init == 1 )
+                    {
+                        init = 0;
+                        grid = ( AptParser.charAt( line, lineLength, 0 ) == PIPE );
+                        AptParser.this.sink.tableRows( justification, grid );
+                    }
+
+                    line = replaceAll( line, "\\|", "\\u007C" );
+
+                    StringTokenizer cellLines = new StringTokenizer( line, "|", true );
+
+                    i = 0;
+                    boolean processedGrid = false;
+                    while ( cellLines.hasMoreTokens() )
+                    {
+                        String cellLine = cellLines.nextToken();
+                        if ( "|".equals( cellLine ) )
+                        {
+                            if ( processedGrid )
+                            {
+                                headers[i] = true;
+                            }
+                            else
+                            {
+                                processedGrid = true;
+                                headers[i] = false;
+                            }
+                            continue;
+                        }
+                        processedGrid = false;
+                        cellLine = replaceAll( cellLine, "\\", "\\u00A0" ); // linebreak
+                        // Escaped special characters: \~, \=, \-, \+, \*, \[, \], \<, \>, \{, \}, \\.
+                        cellLine = replaceAll( cellLine, "\\u00A0~", "\\~" );
+                        cellLine = replaceAll( cellLine, "\\u00A0=", "\\=" );
+                        cellLine = replaceAll( cellLine, "\\u00A0-", "\\-" );
+                        cellLine = replaceAll( cellLine, "\\u00A0+", "\\+" );
+                        cellLine = replaceAll( cellLine, "\\u00A0*", "\\*" );
+                        cellLine = replaceAll( cellLine, "\\u00A0[", "\\[" );
+                        cellLine = replaceAll( cellLine, "\\u00A0]", "\\]" );
+                        cellLine = replaceAll( cellLine, "\\u00A0<", "\\<" );
+                        cellLine = replaceAll( cellLine, "\\u00A0>", "\\>" );
+                        cellLine = replaceAll( cellLine, "\\u00A0{", "\\{" );
+                        cellLine = replaceAll( cellLine, "\\u00A0}", "\\}" );
+                        cellLine = replaceAll( cellLine, "\\u00A0\\u00A0", "\\\\" );
+                        cellLine = cellLine.trim();
+
+                        StringBuffer cell = cells[i];
+                        if ( cellLine.length() > 0 )
+                        {
+                            // line break in table cells
+                            if ( cell.toString().trim().endsWith( "\\u00A0" ) )
+                            {
+                                cell.append( "\\\n" );
+                            }
+                            else
+                            {
+                                if ( cell.length() != 0 )
+                                {
+                                    // Always add a space for multi line tables cells
+                                    cell.append( " " );
+                                }
+                            }
+
+                            cell.append( cellLine );
+                        }
+
+                        ++i;
+                        if ( i == columns )
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+            if ( rows == 0 )
+            {
+                throw new AptParseException( "no table rows" );
+            }
+            AptParser.this.sink.tableRows_();
+
+            if ( captionIndex >= 0 )
+            {
+                AptParser.this.sink.tableCaption();
+                AptParser.this.doTraverseText( text, captionIndex, textLength, AptParser.this.sink );
+                AptParser.this.sink.tableCaption_();
+            }
+
+            AptParser.this.sink.table_();
+        }
+
+        /**
+         * Parse a table justification line.
+         *
+         * @param jline the justification line.
+         * @param lineLength the length of the line. Must be > 2.
+         * @return int[]
+         * @throws AptParseException if something goes wrong.
+         */
+        private int[] parseJustification( String jline, int lineLength )
+            throws AptParseException
+        {
+            int columns = 0;
+
+            for ( int i = 2 /*Skip '*--'*/; i < lineLength; ++i )
+            {
+                switch ( jline.charAt( i ) )
+                {
+                    case STAR:
+                    case PLUS:
+                    case COLON:
+                        ++columns;
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+            if ( columns == 0 )
+            {
+                throw new AptParseException( "no columns specified" );
+            }
+
+            int[] justification = new int[columns];
+            columns = 0;
+            for ( int i = 2; i < lineLength; ++i )
+            {
+                switch ( jline.charAt( i ) )
+                {
+                    case STAR:
+                        justification[columns++] = Sink.JUSTIFY_CENTER;
+                        break;
+                    case PLUS:
+                        justification[columns++] = Sink.JUSTIFY_LEFT;
+                        break;
+                    case COLON:
+                        justification[columns++] = Sink.JUSTIFY_RIGHT;
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+            return justification;
+        }
+
+        /**
+         * Traverse a table row.
+         *
+         * @param cells The table cells.
+         * @param headers true for header cells.
+         * @param justification the justification for each cell.
+         * @return boolean
+         * @throws AptParseException if something goes wrong.
+         */
+        private boolean traverseRow( StringBuffer[] cells, boolean[] headers, int[] justification )
+            throws AptParseException
+        {
+            // Skip empty row (a decorative line).
+            boolean traversed = false;
+            for ( int i = 0; i < cells.length; ++i )
+            {
+                if ( cells[i].length() > 0 )
+                {
+                    traversed = true;
+                    break;
+                }
+            }
+
+            if ( traversed )
+            {
+                AptParser.this.sink.tableRow();
+                for ( int i = 0; i < cells.length; ++i )
+                {
+                    StringBuffer cell = cells[i];
+
+                    SinkEventAttributes justif;
+                    switch ( justification[i] )
+                    {
+                        case Sink.JUSTIFY_CENTER:
+                            justif = SinkEventAttributeSet.CENTER;
+                            break;
+                        case Sink.JUSTIFY_LEFT:
+                            justif = SinkEventAttributeSet.LEFT;
+                            break;
+                        case Sink.JUSTIFY_RIGHT:
+                            justif = SinkEventAttributeSet.RIGHT;
+                            break;
+                        default:
+                            justif = SinkEventAttributeSet.LEFT;
+                            break;
+                    }
+                    SinkEventAttributeSet event = new SinkEventAttributeSet();
+                    event.addAttributes( justif );
+
+                    if ( headers[i] )
+                    {
+                        AptParser.this.sink.tableHeaderCell( event );
+                    }
+                    else
+                    {
+                        AptParser.this.sink.tableCell( event );
+                    }
+                    if ( cell.length() > 0 )
+                    {
+                        AptParser.this.doTraverseText( cell.toString(), 0, cell.length(), AptParser.this.sink );
+                        cell.setLength( 0 );
+                    }
+                    if ( headers[i] )
+                    {
+                        AptParser.this.sink.tableHeaderCell_();
+                    }
+                    else
+                    {
+                        AptParser.this.sink.tableCell_();
+                    }
+                }
+                AptParser.this.sink.tableRow_();
+            }
+
+            return traversed;
+        }
+    }
+
+    /** A ListItem Block. */
+    private class ListItem
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public ListItem( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( LIST_ITEM, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            traverseText( skipLeadingBullets() );
+        }
+    }
+
+    /** A NumberedListItem Block. */
+    private class NumberedListItem
+        extends Block
+    {
+        /** numbering. */
+        private int numbering;
+
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @param number numbering.
+         * @throws AptParseException AptParseException
+         */
+        public NumberedListItem( int indent, String firstLine, int number )
+            throws AptParseException
+        {
+            super( NUMBERED_LIST_ITEM, indent, firstLine );
+            this.numbering = number;
+        }
+
+        /**
+         * getNumbering.
+         *
+         * @return int
+         */
+        public int getNumbering()
+        {
+            return numbering;
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            traverseText( skipItemNumber() );
+        }
+
+        /**
+         * skipItemNumber.
+         *
+         * @return int
+         * @throws AptParseException AptParseException
+         */
+        private int skipItemNumber()
+            throws AptParseException
+        {
+            int i = skipSpaceFrom( 0 );
+
+            char prevChar = SPACE;
+            for ( ; i < textLength; ++i )
+            {
+                char c = text.charAt( i );
+                if ( c == RIGHT_SQUARE_BRACKET && prevChar == RIGHT_SQUARE_BRACKET )
+                {
+                    break;
+                }
+                prevChar = c;
+            }
+
+            if ( i == textLength )
+            {
+                throw new AptParseException( "missing '" + RIGHT_SQUARE_BRACKET + RIGHT_SQUARE_BRACKET + "'" );
+            }
+
+            return skipSpaceFrom( i + 1 );
+        }
+    }
+
+    /** A DefinitionListItem Block. */
+    private class DefinitionListItem
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public DefinitionListItem( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( DEFINITION_LIST_ITEM, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            int i = skipSpaceFrom( 0 );
+            int j = skipFromLeftToRightBracket( i );
+
+            AptParser.this.sink.definedTerm();
+            traverseText( i + 1, j );
+            AptParser.this.sink.definedTerm_();
+
+            j = skipSpaceFrom( j + 1 );
+            if ( j == textLength )
+            {
+                // TODO: this doesn't handle the case of a dd in a paragraph
+                //throw new AptParseException( "no definition" );
+            }
+
+            AptParser.this.sink.definition();
+            traverseText( j );
+        }
+    }
+
+    /** A HorizontalRule Block. */
+    private class HorizontalRule
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public HorizontalRule( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( HORIZONTAL_RULE, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            AptParser.this.sink.horizontalRule();
+        }
+    }
+
+    /** A PageBreak Block. */
+    private class PageBreak
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public PageBreak( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( PG_BREAK, indent, firstLine );
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            AptParser.this.sink.pageBreak();
+        }
+    }
+
+    /** A MacroBlock Block. */
+    private class MacroBlock
+        extends Block
+    {
+        /**
+         * Constructor.
+         *
+         * @param indent indent.
+         * @param firstLine the first line.
+         * @throws AptParseException AptParseException
+         */
+        public MacroBlock( int indent, String firstLine )
+            throws AptParseException
+        {
+            super( MACRO, indent );
+
+            text = firstLine;
+        }
+
+        /** {@inheritDoc} */
+        public void traverse()
+            throws AptParseException
+        {
+            if ( isSecondParsing() )
+            {
+                return;
+            }
+
+            String s = text;
+
+            s = s.substring( 2, s.length() - 1 );
+
+            s = escapeForMacro( s );
+
+            String[] params = StringUtils.split( s, "|" );
+
+            String macroId = params[0];
+
+            Map parameters = new HashMap();
+
+            for ( int i = 1; i < params.length; i++ )
+            {
+                String[] param = StringUtils.split( params[i], "=" );
+
+                if ( param.length == 1 )
+                {
+                    throw new AptParseException( "Missing 'key=value' pair for macro parameter: " + params[i] );
+                }
+
+                String key = unescapeForMacro( param[0] );
+                String value = unescapeForMacro( param[1] );
+
+                parameters.put( key, value );
+            }
+
+            parameters.put( "sourceContent", sourceContent );
+
+            AptParser aptParser = new AptParser();
+            aptParser.setSecondParsing( true );
+            aptParser.enableLogging( getLog() );
+            parameters.put( "parser", aptParser );
+
+            // getBasedir() does not work in multi-module builds, see DOXIA-373
+            // the basedir should be injected from here, see DOXIA-224
+            MacroRequest request = new MacroRequest( parameters, getBasedir() );
+            try
+            {
+                AptParser.this.executeMacro( macroId, request, sink );
+            }
+            catch ( MacroExecutionException e )
+            {
+                throw new AptParseException( "Unable to execute macro in the APT document", e );
+            }
+            catch ( MacroNotFoundException e )
+            {
+                throw new AptParseException( "Unable to find macro used in the APT document", e );
+            }
+        }
+
+        /**
+         * escapeForMacro
+         *
+         * @param s String
+         * @return String
+         */
+        private String escapeForMacro( String s )
+        {
+            if ( s == null || s.length() < 1 )
+            {
+                return s;
+            }
+
+            String result = s;
+
+            // use some outrageously out-of-place chars for text
+            // (these are device control one/two in unicode)
+            result = StringUtils.replace( result, "\\=", "\u0011" );
+            result = StringUtils.replace( result, "\\|", "\u0012" );
+
+            return result;
+        }
+
+        /**
+         * unescapeForMacro
+         *
+         * @param s String
+         * @return String
+         */
+        private String unescapeForMacro( String s )
+        {
+            if ( s == null || s.length() < 1 )
+            {
+                return s;
+            }
+
+            String result = s;
+
+            result = StringUtils.replace( result, "\u0011", "=" );
+            result = StringUtils.replace( result, "\u0012", "|" );
+
+            return result;
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptReaderSource.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptReaderSource.java
new file mode 100644
index 0000000..2d133cb
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptReaderSource.java
@@ -0,0 +1,107 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.Reader;
+
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * Reader for apt source documents.
+ *
+ * @version $Id: AptReaderSource.java 746982 2009-02-23 12:25:50Z vsiveton $
+ */
+public class AptReaderSource
+    implements AptSource
+{
+    /** A reader. */
+    private LineNumberReader reader;
+
+    /** lineNumber. */
+    private int lineNumber;
+
+    /**
+     * Constructor: intialize reader.
+     *
+     * @param in the reader.
+     */
+    public AptReaderSource( Reader in )
+    {
+        reader = new LineNumberReader( in );
+
+        lineNumber = -1;
+    }
+
+    /** {@inheritDoc} */
+    public String getNextLine()
+        throws AptParseException
+    {
+        if ( reader == null )
+        {
+            return null;
+        }
+
+        String line;
+
+        try
+        {
+            line = reader.readLine();
+            if ( line == null )
+            {
+                reader.close();
+                reader = null;
+            }
+            else
+            {
+                lineNumber = reader.getLineNumber();
+            }
+        }
+        catch ( IOException e )
+        {
+            // TODO handle column number
+            throw new AptParseException( "IOException: " + e.getMessage(), e, lineNumber, -1 );
+        }
+
+        return line;
+    }
+
+    /** {@inheritDoc} */
+    public String getName()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    public int getLineNumber()
+    {
+        return lineNumber;
+    }
+
+    /**
+     * Closes the reader associated with this AptReaderSource.
+     */
+    public void close()
+    {
+        IOUtil.close( reader );
+        reader = null;
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSink.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSink.java
new file mode 100644
index 0000000..7154952
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSink.java
@@ -0,0 +1,1020 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Stack;
+
+import org.apache.maven.doxia.sink.AbstractTextSink;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * APT Sink implementation.
+ * <br/>
+ * <b>Note</b>: The encoding used is UTF-8.
+ *
+ * @author eredmond
+ * @version $Id: AptSink.java 905092 2010-01-31 18:36:36Z hboutemy $
+ * @since 1.0
+ */
+public class AptSink
+    extends AbstractTextSink
+    implements AptMarkup
+{
+    // ----------------------------------------------------------------------
+    // Instance fields
+    // ----------------------------------------------------------------------
+
+    /**  A buffer that holds the current text. */
+    private StringBuffer buffer;
+
+    /**  A buffer that holds the table caption. */
+    private StringBuffer tableCaptionBuffer;
+
+    /**  author. */
+    private String author;
+
+    /**  title. */
+    private String title;
+
+    /**  date. */
+    private String date;
+
+    /** startFlag. */
+    private boolean startFlag;
+
+    /**  tableCaptionFlag. */
+    private boolean tableCaptionFlag;
+
+    /**  headerFlag. */
+    private boolean headerFlag;
+
+    /**  bufferFlag. */
+    private boolean bufferFlag;
+
+    /**  itemFlag. */
+    private boolean itemFlag;
+
+    /**  verbatimFlag. */
+    private boolean verbatimFlag;
+
+    /**  boxed verbatim. */
+    private boolean isBoxed;
+
+    /**  gridFlag for tables. */
+    private boolean gridFlag;
+
+    /**  number of cells in a table. */
+    private int cellCount;
+
+    /**  The writer to use. */
+    private final PrintWriter writer;
+
+    /**  justification of table cells. */
+    private int cellJustif[];
+
+    /**  a line of a row in a table. */
+    private String rowLine;
+
+    /**  listNestingIndent. */
+    private String listNestingIndent;
+
+    /**  listStyles. */
+    private final Stack listStyles;
+
+    // ----------------------------------------------------------------------
+    // Public protected methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Constructor, initialize the Writer and the variables.
+     *
+     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     */
+    protected AptSink( Writer writer )
+    {
+        this.writer = new PrintWriter( writer );
+        this.listStyles = new Stack();
+
+        init();
+    }
+
+    /**
+     * Returns the buffer that holds the current text.
+     *
+     * @return A StringBuffer.
+     */
+    protected StringBuffer getBuffer()
+    {
+        return buffer;
+    }
+
+    /**
+     * Used to determine whether we are in head mode.
+     *
+     * @param headFlag True for head mode.
+     */
+    protected void setHeadFlag( boolean headFlag )
+    {
+        this.headerFlag = headFlag;
+    }
+
+    /**
+     * Reset all variables.
+     *
+     * @deprecated since 1.1.2, use {@link #init()} instead of.
+     */
+    protected void resetState()
+    {
+        init();
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        resetBuffer();
+
+        this.tableCaptionBuffer = new StringBuffer();
+        this.listNestingIndent = "";
+
+        this.author = null;
+        this.title = null;
+        this.date = null;
+        this.startFlag = true;
+        this.tableCaptionFlag = false;
+        this.headerFlag = false;
+        this.bufferFlag = false;
+        this.itemFlag = false;
+        this.verbatimFlag = false;
+        this.isBoxed = false;
+        this.gridFlag = false;
+        this.cellCount = 0;
+        this.cellJustif = null;
+        this.rowLine = null;
+        this.listStyles.clear();
+    }
+
+    /**
+     * Reset the StringBuffer.
+     */
+    protected void resetBuffer()
+    {
+        buffer = new StringBuffer();
+    }
+
+    /**
+     * Reset the TableCaptionBuffer.
+     */
+    protected void resetTableCaptionBuffer()
+    {
+        tableCaptionBuffer = new StringBuffer();
+    }
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        boolean startFlag = this.startFlag;
+
+        init();
+
+        headerFlag = true;
+        this.startFlag = startFlag;
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        headerFlag = false;
+
+        if ( ! startFlag )
+        {
+            write( EOL );
+        }
+        write( HEADER_START_MARKUP + EOL );
+        if ( title != null )
+        {
+            write( " " + title + EOL );
+        }
+        write( HEADER_START_MARKUP + EOL );
+        if ( author != null )
+        {
+            write( " " + author + EOL );
+        }
+        write( HEADER_START_MARKUP + EOL );
+        if ( date != null )
+        {
+            write( " " + date + EOL );
+        }
+        write( HEADER_START_MARKUP + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        if ( buffer.length() > 0 )
+        {
+            title = buffer.toString();
+            resetBuffer();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        if ( buffer.length() > 0 )
+        {
+            author = buffer.toString();
+            resetBuffer();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        if ( buffer.length() > 0 )
+        {
+            date = buffer.toString();
+            resetBuffer();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        write( EOL + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        write( EOL + SECTION_TITLE_START_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        write( EOL + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 2 ) );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        write( EOL + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 3 ) );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        write( EOL + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 4 ) );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        write( EOL + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        listNestingIndent += " ";
+        listStyles.push( LIST_START_MARKUP );
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        if ( listNestingIndent.length() <= 1 )
+        {
+            write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
+        }
+        else
+        {
+            write( EOL );
+        }
+        listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
+        listStyles.pop();
+        itemFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        //if ( !numberedList )
+        //write( EOL + listNestingIndent + "*" );
+        //else
+        numberedListItem();
+        itemFlag = true;
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        write( EOL );
+        itemFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        listNestingIndent += " ";
+        write( EOL );
+
+        String style;
+        switch ( numbering )
+        {
+            case NUMBERING_UPPER_ALPHA:
+                style = String.valueOf( NUMBERING_UPPER_ALPHA_CHAR );
+                break;
+            case NUMBERING_LOWER_ALPHA:
+                style = String.valueOf( NUMBERING_LOWER_ALPHA_CHAR );
+                break;
+            case NUMBERING_UPPER_ROMAN:
+                style = String.valueOf( NUMBERING_UPPER_ROMAN_CHAR );
+                break;
+            case NUMBERING_LOWER_ROMAN:
+                style = String.valueOf( NUMBERING_LOWER_ROMAN_CHAR );
+                break;
+            case NUMBERING_DECIMAL:
+            default:
+                style = String.valueOf( NUMBERING );
+        }
+
+        listStyles.push( style );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        if ( listNestingIndent.length() <= 1 )
+        {
+            write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
+        }
+        else
+        {
+            write( EOL );
+        }
+        listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
+        listStyles.pop();
+        itemFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        String style = (String) listStyles.peek();
+        if ( style.equals( String.valueOf( STAR ) ) )
+        {
+            write( EOL + listNestingIndent + String.valueOf( STAR ) + String.valueOf( SPACE ) );
+        }
+        else
+        {
+            write( EOL + listNestingIndent + String.valueOf( LEFT_SQUARE_BRACKET )
+                + String.valueOf( LEFT_SQUARE_BRACKET ) + style + String.valueOf( RIGHT_SQUARE_BRACKET )
+                + String.valueOf( RIGHT_SQUARE_BRACKET ) + String.valueOf( SPACE ) );
+        }
+        itemFlag = true;
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        write( EOL );
+        itemFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList()
+    {
+        listNestingIndent += " ";
+        listStyles.push( "" );
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList_()
+    {
+        if ( listNestingIndent.length() <= 1 )
+        {
+            write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
+        }
+        else
+        {
+            write( EOL );
+        }
+        listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
+        listStyles.pop();
+        itemFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        write( EOL + " [" );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm_()
+    {
+        write( "] " );
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        itemFlag = true;
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        write( EOL );
+        itemFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        write( EOL + PAGE_BREAK + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph()
+    {
+        if ( itemFlag )
+        {
+            write( EOL + EOL + "  " + listNestingIndent );
+        }
+        else
+        {
+            write( EOL + " " );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        write( EOL + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        verbatimFlag = true;
+        this.isBoxed = boxed;
+        write( EOL );
+        if ( boxed )
+        {
+            write( EOL + BOXED_VERBATIM_START_MARKUP + EOL );
+        }
+        else
+        {
+            write( EOL + NON_BOXED_VERBATIM_START_MARKUP + EOL );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        if ( isBoxed )
+        {
+            write( EOL + BOXED_VERBATIM_END_MARKUP + EOL );
+        }
+        else
+        {
+            write( EOL + NON_BOXED_VERBATIM_END_MARKUP + EOL );
+        }
+        isBoxed = false;
+        verbatimFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        write( EOL + HORIZONTAL_RULE_MARKUP + EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        if ( rowLine != null )
+        {
+            write( rowLine );
+        }
+        rowLine = null;
+
+        if ( tableCaptionBuffer.length() > 0 )
+        {
+            text( tableCaptionBuffer.toString() + EOL );
+        }
+
+        resetTableCaptionBuffer();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows( int justification[], boolean grid )
+    {
+        cellJustif = justification;
+        gridFlag = grid;
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        cellJustif = null;
+        gridFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        bufferFlag = true;
+        cellCount = 0;
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        bufferFlag = false;
+
+        // write out the header row first, then the data in the buffer
+        buildRowLine();
+
+        write( rowLine );
+
+        // TODO: This will need to be more clever, for multi-line cells
+        if ( gridFlag )
+        {
+            write( TABLE_ROW_SEPARATOR_MARKUP );
+        }
+
+        write( buffer.toString() );
+
+        resetBuffer();
+
+        write( EOL );
+
+        // only reset cell count if this is the last row
+        cellCount = 0;
+    }
+
+    /** Construct a table row. */
+    private void buildRowLine()
+    {
+        StringBuffer rLine = new StringBuffer();
+        rLine.append( TABLE_ROW_START_MARKUP );
+
+        for ( int i = 0; i < cellCount; i++ )
+        {
+            if ( cellJustif != null )
+            {
+                switch ( cellJustif[i] )
+                {
+                case 1:
+                    rLine.append( TABLE_COL_LEFT_ALIGNED_MARKUP );
+                    break;
+                case 2:
+                    rLine.append( TABLE_COL_RIGHT_ALIGNED_MARKUP );
+                    break;
+                default:
+                    rLine.append( TABLE_COL_CENTERED_ALIGNED_MARKUP );
+                }
+            }
+            else
+            {
+                rLine.append( TABLE_COL_CENTERED_ALIGNED_MARKUP );
+            }
+        }
+        rLine.append( EOL );
+
+        this.rowLine = rLine.toString();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        tableCell( false );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        tableCell( true );
+    }
+
+    /**
+     * Starts a table cell.
+     *
+     * @param headerRow If this cell is part of a header row.
+     */
+    public void tableCell( boolean headerRow )
+    {
+        if ( headerRow )
+        {
+            buffer.append( TABLE_CELL_SEPARATOR_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        endTableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        endTableCell();
+    }
+
+    /**
+     * Ends a table cell.
+     */
+    private void endTableCell()
+    {
+        buffer.append( TABLE_CELL_SEPARATOR_MARKUP );
+        cellCount++;
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption()
+    {
+        tableCaptionFlag = true;
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption_()
+    {
+        tableCaptionFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        write( EOL );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        write( EOL + "[" + name + "] " );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        write( ANCHOR_START_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor_()
+    {
+        write( ANCHOR_END_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        if ( !headerFlag )
+        {
+            write( LINK_START_1_MARKUP );
+            text( name.startsWith( "#" ) ? name.substring( 1 ) : name );
+            write( LINK_START_2_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        if ( !headerFlag )
+        {
+            write( LINK_END_MARKUP );
+        }
+    }
+
+    /**
+     * A link with a target.
+     *
+     * @param name The name of the link.
+     * @param target The link target.
+     */
+    public void link( String name, String target )
+    {
+        if ( !headerFlag )
+        {
+            write( LINK_START_1_MARKUP );
+            text( target );
+            write( LINK_START_2_MARKUP );
+            text( name );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        if ( !headerFlag )
+        {
+            write( ITALIC_START_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        if ( !headerFlag )
+        {
+            write( ITALIC_END_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        if ( !headerFlag )
+        {
+            write( BOLD_START_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        if ( !headerFlag )
+        {
+            write( BOLD_END_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        if ( !headerFlag )
+        {
+            write( MONOSPACED_START_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        if ( !headerFlag )
+        {
+            write( MONOSPACED_END_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        if ( headerFlag || bufferFlag )
+        {
+            buffer.append( EOL );
+        }
+        else if ( verbatimFlag )
+        {
+            write( EOL );
+        }
+        else
+        {
+            write( "\\" + EOL );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        if ( headerFlag || bufferFlag )
+        {
+            buffer.append( NON_BREAKING_SPACE_MARKUP );
+        }
+        else
+        {
+            write( NON_BREAKING_SPACE_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        if ( tableCaptionFlag )
+        {
+            tableCaptionBuffer.append( text );
+        }
+        else if ( headerFlag || bufferFlag )
+        {
+            buffer.append( text );
+        }
+        else if ( verbatimFlag )
+        {
+            verbatimContent( text );
+        }
+        else
+        {
+            content( text );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        write( text );
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+        rawText( ( startFlag ? "" : EOL ) + COMMENT + COMMENT + SPACE + comment.trim() );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Unkown events just log a warning message but are ignored otherwise.
+     * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        getLog().warn( "[Apt Sink] Unknown Sink event: '" + name + "', ignoring!" );
+    }
+
+    /**
+     * Write text to output.
+     *
+     * @param text The text to write.
+     */
+    protected void write( String text )
+    {
+        startFlag = false;
+        writer.write( unifyEOLs( text ) );
+    }
+
+    /**
+     * Write Apt escaped text to output.
+     *
+     * @param text The text to write.
+     */
+    protected void content( String text )
+    {
+        write( escapeAPT( text ) );
+    }
+
+    /**
+     * Write Apt escaped text to output.
+     *
+     * @param text The text to write.
+     */
+    protected void verbatimContent( String text )
+    {
+        write( escapeAPT( text ) );
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        writer.flush();
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        writer.close();
+
+        init();
+    }
+
+    // ----------------------------------------------------------------------
+    // Private methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Escape special characters in a text in APT:
+     *
+     * <pre>
+     * \~, \=, \-, \+, \*, \[, \], \<, \>, \{, \}, \\
+     * </pre>
+     *
+     * @param text the String to escape, may be null
+     * @return the text escaped, "" if null String input
+     */
+    private static String escapeAPT( String text )
+    {
+        if ( text == null )
+        {
+            return "";
+        }
+
+        int length = text.length();
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = text.charAt( i );
+            switch ( c )
+            { // 0080
+                case '\\':
+                case '~':
+                case '=':
+                case '-':
+                case '+':
+                case '*':
+                case '[':
+                case ']':
+                case '<':
+                case '>':
+                case '{':
+                case '}':
+                    buffer.append( '\\' );
+                    buffer.append( c );
+                    break;
+                default:
+                    buffer.append( c );
+            }
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSinkFactory.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSinkFactory.java
new file mode 100644
index 0000000..9c55726
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSinkFactory.java
@@ -0,0 +1,44 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractTextSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * APT implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AptSinkFactory.java 712574 2008-11-09 22:16:42Z hboutemy $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="apt"
+ */
+public class AptSinkFactory
+    extends AbstractTextSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        // encoding can safely be ignored since it isn't written into the generated APT source
+        return new AptSink( writer );
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSiteModule.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSiteModule.java
new file mode 100644
index 0000000..dd4f11f
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSiteModule.java
@@ -0,0 +1,42 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.site.AbstractSiteModule;
+
+/**
+ * <p>AptSiteModule class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: AptSiteModule.java 782392 2009-06-07 14:14:16Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.module.site.SiteModule" role-hint="apt"
+ */
+public class AptSiteModule
+    extends AbstractSiteModule
+{
+    /**
+     * Default constructor.
+     */
+    public AptSiteModule()
+    {
+        super( "apt", "apt", "apt" );
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSource.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSource.java
new file mode 100644
index 0000000..325aaad
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSource.java
@@ -0,0 +1,52 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * An interface to read apt source documents.
+ *
+ * @version $Id: AptSource.java 746982 2009-02-23 12:25:50Z vsiveton $
+ */
+public interface AptSource
+{
+    /**
+     * Returns a line of the apt source document.
+     *
+     * @return a line of the apt source.
+     * @throws org.apache.maven.doxia.module.apt.AptParseException if the document can't be parsed.
+     */
+    String getNextLine()
+        throws AptParseException;
+
+    /**
+     * Returns the name the apt source document.
+     *
+     * @return the name the apt source document.
+     */
+    String getName();
+
+    /**
+     * Gets the current line number while parsing the document.
+     *
+     * @return the line number.
+     */
+    int getLineNumber();
+}
+
diff --git a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptUtils.java b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptUtils.java
new file mode 100644
index 0000000..4fb532a
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptUtils.java
@@ -0,0 +1,194 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.util.DoxiaUtils;
+
+/**
+ * A collection of utility methods for dealing with APT documents.
+ *
+ * @author ltheussl
+ * @since 1.1
+ * @version $Id: AptUtils.java 747735 2009-02-25 10:43:09Z ltheussl $
+ */
+public class AptUtils
+{
+
+    /**
+     * Replace all characters in a text.
+     *
+     * <pre>
+     * AptTools.encodeFragment( null ) = null
+     * AptTools.encodeFragment( "" ) = ""
+     * AptTools.encodeFragment( "http://www.google.com" ) = "httpwwwgooglecom"
+     * </pre>
+     *
+     * @param text the String to check, may be null.
+     * @return the text with only letter and digit, null if null String input.
+     * @deprecated This method was used for the original apt format, which
+     * removed all non alphanumeric characters from anchors.
+     * Use {@link #encodeAnchor(String)} instead.
+     */
+    public static String encodeFragment( String text )
+    {
+        if ( text == null )
+        {
+            return null;
+        }
+
+        return linkToKey( text );
+    }
+
+    /**
+     * Checks if the given string corresponds to an external URI,
+     * ie is not a link within the same document nor a link to another
+     * document within the same site. This forwards to
+     * {@link org.apache.maven.doxia.util.DoxiaUtils#isExternalLink(String)}.
+     *
+     * @param link The link to check.
+     * @return True if DoxiaUtils.isExternalLink(link) returns true.
+     *
+     * @see org.apache.maven.doxia.util.DoxiaUtils#isExternalLink(String)
+     * @see #isInternalLink(String)
+     * @see #isLocalLink(String)
+     */
+    public static boolean isExternalLink( String link )
+    {
+        return DoxiaUtils.isExternalLink( link );
+    }
+
+    /**
+     * Checks if the given string corresponds to an internal link,
+     * ie it is a link to an anchor within the same document.
+     *
+     * @param link The link to check.
+     * @return True if link is neither an {@link #isExternalLink(String) external}
+     * nor a {@link #isLocalLink(String) local} link.
+     *
+     * @see org.apache.maven.doxia.util.DoxiaUtils#isInternalLink(String)
+     * @see #isExternalLink(String)
+     * @see #isLocalLink(String)
+     */
+    public static boolean isInternalLink( String link )
+    {
+        return ( !isExternalLink( link ) && !isLocalLink( link ) );
+    }
+
+    /**
+     * Checks if the given string corresponds to a relative link to another document
+     * within the same site.
+     *
+     * @param link The link to check.
+     * @return True if the link starts with either "/", "./" or "../".
+     *
+     * @see org.apache.maven.doxia.util.DoxiaUtils#isLocalLink(String)
+     * @see #isExternalLink(String)
+     * @see #isInternalLink(String)
+     */
+    public static boolean isLocalLink( String link )
+    {
+        return ( link.startsWith( "/" ) || link.startsWith( "./" ) || link.startsWith( "../" ) );
+    }
+
+    /**
+     * Transforms the given text such that it can be used as a link.
+     * All non-LetterOrDigit characters are removed and the remaining
+     * characters are transformed to lower-case.
+     *
+     * @param text The text to transform.
+     * @return The text with all non-LetterOrDigit characters removed.
+     * @deprecated This method was used for the original apt format, which
+     * removed all non alphanumeric characters from anchors.
+     * Use {@link #encodeAnchor(String)} instead.
+     */
+    public static String linkToKey( String text )
+    {
+        int length = text.length();
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = text.charAt( i );
+            if ( Character.isLetterOrDigit( c ) )
+            {
+                buffer.append( Character.toLowerCase( c ) );
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * Construct a valid anchor. This is a simplified version of
+     * {@link org.apache.maven.doxia.util.DoxiaUtils#encodeId(String)}
+     * to ensure the anchor is a valid Doxia id.
+     * The procedure is identical to the one in
+     * {@link org.apache.maven.doxia.util.HtmlTools#encodeId(String)}:
+     *
+     * <ol>
+     *   <li> Trim the id</li>
+     *   <li> If the first character is not a letter, prepend the letter 'a'</li>
+     *   <li> Any space is replaced with an underscore '_'</li>
+     *   <li> Remove any non alphanumeric characters  except ':', '_', '.', '-'.</li>
+     * </ol>
+     *
+     * @param id The id to be encoded.
+     * @return The trimmed and encoded id, or null if id is null.
+     */
+    public static String encodeAnchor( String id )
+    {
+        if ( id == null )
+        {
+            return null;
+        }
+
+        id = id.trim();
+
+        int length = id.length();
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = id.charAt( i );
+
+            if ( ( i == 0 ) && ( !Character.isLetter( c ) ) )
+            {
+                buffer.append( 'a' );
+            }
+
+            if ( c == ' ' )
+            {
+                buffer.append( '_' );
+            }
+            else if ( ( Character.isLetterOrDigit( c ) ) || ( c == '-' ) || ( c == '_' ) || ( c == ':' )
+                            || ( c == '.' ) )
+            {
+                buffer.append( c );
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    private AptUtils()
+    {
+        // utility class
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/site/site.xml b/doxia-modules/doxia-module-apt/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptIdentityTest.java b/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptIdentityTest.java
new file mode 100644
index 0000000..8f2637f
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptIdentityTest.java
@@ -0,0 +1,46 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.module.AbstractIdentityTest;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.Sink;
+
+
+/**
+ * Check that piping a full model through an AptParser and an AptSink
+ * leaves the model unchanged. The test is done in AbstractIdentityTest.
+ */
+public class AptIdentityTest extends AbstractIdentityTest
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new AptSink( writer );
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return new AptParser();
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptParserTest.java b/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptParserTest.java
new file mode 100644
index 0000000..636697d
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptParserTest.java
@@ -0,0 +1,588 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Iterator;
+
+import org.apache.maven.doxia.parser.AbstractParserTest;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.parser.ParseException;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: AptParserTest.java 905092 2010-01-31 18:36:36Z hboutemy $
+ */
+public class AptParserTest
+    extends AbstractParserTest
+{
+
+    private AptParser parser;
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        parser = (AptParser) lookup( Parser.ROLE, "apt" );
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return parser;
+    }
+
+    protected String parseFileToAptSink( String file )
+        throws ParseException
+    {
+        StringWriter output = null;
+        Reader reader = null;
+        try
+        {
+            output = new StringWriter();
+            reader = getTestReader( file );
+
+            Sink sink = new AptSink( output );
+            createParser().parse( reader, sink );
+        }
+        finally
+        {
+            IOUtil.close( output );
+            IOUtil.close( reader );
+        }
+
+        return output.toString();
+    }
+
+    /** @throws Exception  */
+    public void testLineBreak()
+        throws Exception
+    {
+        String linebreak = parseFileToAptSink( "test/linebreak" );
+
+        assertTrue( linebreak.indexOf( "Line\\" + EOL + "break." ) != -1 );
+    }
+
+    /** @throws Exception  */
+    public void testSnippetMacro()
+        throws Exception
+    {
+        String macro = parseFileToAptSink( "test/macro" );
+
+        assertTrue( macro.indexOf( "<modelVersion\\>4.0.0\\</modelVersion\\>" ) != -1 );
+    }
+
+    /** @throws Exception  */
+    public void testCommentsBeforeTitle()
+        throws Exception
+    {
+        String comments = parseFileToAptSink( "test/comments" );
+
+        assertEquals( 0, comments.indexOf( "~~ comments before title" + EOL + "~~ like a license header, for example"
+            + EOL + " -----" + EOL + " Test DOXIA-379" ) );
+    }
+
+    /** @throws Exception  */
+    public void testSnippet()
+        throws Exception
+    {
+        // DOXIA-259
+
+        Reader reader = null;
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        try
+        {
+            reader = getTestReader( "test/snippet" );
+
+            createParser().parse( reader, sink );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testTocMacro()
+        throws Exception
+    {
+        String toc = parseFileToAptSink( "test/toc" );
+
+        // No section, only subsection 1 and 2
+        assertTrue( toc.indexOf( "* {{{SubSection_1.1}SubSection 1.1}}" ) != -1 );
+        assertTrue( toc.indexOf( "* {{{SubSection_1.1.2.1.1}SubSection 1.1.2.1.1}}" ) == -1 );
+    }
+
+    /**
+     * Parses the test document test.apt and re-emits
+     * it into parser/test.apt.
+     *
+     * @throws java.io.IOException if the test file cannot be read.
+     * @throws org.apache.maven.doxia.parser.ParseException if the test file cannot be parsed.
+     */
+    public void testTestDocument()
+        throws IOException, ParseException
+    {
+        Writer writer = null;
+        Reader reader = null;
+        try
+        {
+            writer = getTestWriter( "test" );
+            reader = getTestReader( "test" );
+
+            Sink sink = new AptSink( writer );
+
+            createParser().parse( reader, sink );
+        }
+        finally
+        {
+            IOUtil.close( writer );
+            IOUtil.close( reader );
+        }
+    }
+
+    /** @throws Exception  */
+    public void testBoxedVerbatim()
+        throws Exception
+    {
+        String text = "+--" + EOL + "boxed verbatim" + EOL + "+--" + EOL
+                + "---" + EOL + "un-boxed verbatim" + EOL + "---" + EOL;
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+
+        SinkEventElement element = (SinkEventElement) it.next();
+        assertEquals( "verbatim", element.getName() );
+        Object[] args = element.getArgs();
+        assertEquals( SinkEventAttributeSet.BOXED, args[0] );
+
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+
+        element = (SinkEventElement) it.next();
+        assertEquals( "verbatim", element.getName() );
+        args = element.getArgs();
+        assertNull( args[0] );
+
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testMultiLinesInTableCells()
+        throws Exception
+    {
+        String text = "*----------*--------------+----------------:" + EOL +
+                " cell 1, | cell 1,2       | cell 1,3" + EOL +
+                " 1       |                | " + EOL +
+                "*----------*--------------+----------------:" + EOL +
+                " cell 2,1 | cell 2,       | cell 2,3" + EOL +
+                "          | 2             |" + EOL +
+                "*----------*--------------+----------------:" + EOL +
+                " cell 3,1 | cell 3,2      | cell 3," + EOL +
+                "          |               | 3" + EOL +
+                "*----------*--------------+----------------:" + EOL;
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "table", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        SinkEventElement element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 1, 1", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 1,2", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 1,3", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 2,1", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 2, 2", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 2,3", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 3,1", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 3,2", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 3, 3", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRows_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "table_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testLineBreakInTableCells()
+        throws Exception
+    {
+        String text = "*----------*--------------+----------------:" + EOL +
+                " cell 1,\\ | cell 1,2       | cell 1,3" + EOL +
+                " 1       |                | " + EOL +
+                "*----------*--------------+----------------:" + EOL +
+                " cell 2,1 | cell 2,\\     | cell 2,3" + EOL +
+                "          | 2             |" + EOL +
+                "*----------*--------------+----------------:" + EOL +
+                " cell 3,1 | cell 3,2      | cell 3,\\" + EOL +
+                "          |               | 3" + EOL +
+                "*----------*--------------+----------------:" + EOL;
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "table", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        SinkEventElement element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 1,\u00A0", element.getArgs()[0] );
+        assertEquals( "lineBreak", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "1", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 1,2", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 1,3", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 2,1", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 2,\u00A0", element.getArgs()[0] );
+        assertEquals( "lineBreak", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "2", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 2,3", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 3,1", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 3,2", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "cell 3,\u00A0", element.getArgs()[0] );
+        assertEquals( "lineBreak", ( (SinkEventElement) it.next() ).getName() );
+        element = (SinkEventElement) it.next();
+        assertEquals( "text", element.getName() );
+        assertNotNull( element.getArgs()[0] );
+        assertEquals( "3", element.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRows_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "table_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testDOXIA38()
+        throws Exception
+    {
+        String text =
+                "*----------*--------------*---------------*" + EOL +
+                "| Centered |   Centered   |   Centered    |" + EOL +
+                "*----------*--------------+---------------:" + EOL +
+                "| Centered | Left-aligned | Right-aligned |" + EOL +
+                "*----------*--------------+---------------:";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "table", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "tableCell", event.getName() );
+        SinkEventAttributeSet atts = (SinkEventAttributeSet) event.getArgs()[0];
+        assertEquals( "center", atts.getAttribute( SinkEventAttributeSet.ALIGN ) );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertNotNull( event.getArgs()[0] );
+        assertEquals( "Centered", event.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "tableCell", event.getName() );
+        atts = (SinkEventAttributeSet) event.getArgs()[0];
+        assertEquals( "center", atts.getAttribute( SinkEventAttributeSet.ALIGN ) );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertNotNull( event.getArgs()[0] );
+        assertEquals( "Centered", event.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "tableCell", event.getName() );
+        atts = (SinkEventAttributeSet) event.getArgs()[0];
+        assertEquals( "center", atts.getAttribute( SinkEventAttributeSet.ALIGN ) );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertNotNull( event.getArgs()[0] );
+        assertEquals( "Centered", event.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "tableCell", event.getName() );
+        atts = (SinkEventAttributeSet) event.getArgs()[0];
+        assertEquals( "center", atts.getAttribute( SinkEventAttributeSet.ALIGN ) );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertNotNull( event.getArgs()[0] );
+        assertEquals( "Centered", event.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "tableCell", event.getName() );
+        atts = (SinkEventAttributeSet) event.getArgs()[0];
+        assertEquals( "left", atts.getAttribute( SinkEventAttributeSet.ALIGN ) );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertNotNull( event.getArgs()[0] );
+        assertEquals( "Left-aligned", event.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        event = (SinkEventElement) it.next();
+        assertEquals( "tableCell", event.getName() );
+        atts = (SinkEventAttributeSet) event.getArgs()[0];
+        assertEquals( "right", atts.getAttribute( SinkEventAttributeSet.ALIGN ) );
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertNotNull( event.getArgs()[0] );
+        assertEquals( "Right-aligned", event.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "tableRows_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "table_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testSpecialCharactersInTables()
+        throws Exception
+    {
+        // DOXIA-323
+        String text =
+                "  \\~ \\= \\- \\+ \\* \\[ \\] \\< \\> \\{ \\} \\\\" + EOL
+                + EOL
+                + "*--------------------------------------------------+---------------+" + EOL
+                + "| \\~ \\= \\- \\+ \\* \\[ \\] \\< \\> \\{ \\} \\\\ | special chars |" + EOL
+                + "*--------------------------------------------------+---------------+";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "~ = - + * [ ] < > { } \\", event.getArgs()[0] );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "table", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "text", event.getName() );
+        assertEquals( "~ = - + * [ ] < > { } \\", event.getArgs()[0] );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "apt";
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptSinkTest.java b/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptSinkTest.java
new file mode 100644
index 0000000..2c6f9a2
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptSinkTest.java
@@ -0,0 +1,274 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.markup.Markup;
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Test the <code>AptSink</code> class
+ *
+ * @see AptSink
+ */
+public class AptSinkTest extends AbstractSinkTest
+{
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "apt";
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new AptSink( writer );
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        return author;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        return date;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return AptMarkup.HEADER_START_MARKUP + EOL + AptMarkup.HEADER_START_MARKUP + EOL + AptMarkup.HEADER_START_MARKUP
+             + EOL + AptMarkup.HEADER_START_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return EOL + title + EOL + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return EOL + AptMarkup.SECTION_TITLE_START_MARKUP + title + EOL + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        return EOL + StringUtils.repeat( String.valueOf( AptMarkup.SECTION_TITLE_START_MARKUP ), 2 )
+                + title + EOL + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        return EOL + StringUtils.repeat( String.valueOf( AptMarkup.SECTION_TITLE_START_MARKUP ), 3 )
+                + title + EOL + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        return EOL + StringUtils.repeat( String.valueOf( AptMarkup.SECTION_TITLE_START_MARKUP ), 4 )
+                + title + EOL + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return EOL + EOL + Markup.SPACE + "" + AptMarkup.LIST_START_MARKUP + "" + Markup.SPACE + item + EOL + EOL
+            + Markup.SPACE + "" + AptMarkup.LIST_END_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return EOL + EOL + Markup.SPACE + "" + Markup.LEFT_SQUARE_BRACKET + ""
+            + Markup.LEFT_SQUARE_BRACKET + AptMarkup.NUMBERING_LOWER_ROMAN_CHAR + ""
+            + Markup.RIGHT_SQUARE_BRACKET + "" + Markup.RIGHT_SQUARE_BRACKET
+            + Markup.SPACE + item + EOL + EOL + Markup.SPACE + "" + AptMarkup.LIST_END_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return EOL + EOL + Markup.SPACE + "" + Markup.LEFT_SQUARE_BRACKET + definum
+            + Markup.RIGHT_SQUARE_BRACKET + "" + Markup.SPACE + definition + EOL + EOL
+            + Markup.SPACE + "" + AptMarkup.LIST_END_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        return EOL + Markup.LEFT_SQUARE_BRACKET + source + Markup.RIGHT_SQUARE_BRACKET
+            + Markup.SPACE + caption + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        return EOL + AptMarkup.TABLE_ROW_START_MARKUP + AptMarkup.TABLE_COL_CENTERED_ALIGNED_MARKUP + EOL + cell
+            + AptMarkup.TABLE_ROW_SEPARATOR_MARKUP + EOL + AptMarkup.TABLE_ROW_START_MARKUP
+            + AptMarkup.TABLE_COL_CENTERED_ALIGNED_MARKUP + EOL + caption + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        return EOL + Markup.SPACE + text + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        return EOL + EOL + AptMarkup.BOXED_VERBATIM_START_MARKUP + EOL + text + EOL
+            + AptMarkup.BOXED_VERBATIM_START_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return EOL + AptMarkup.HORIZONTAL_RULE_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return EOL + AptMarkup.PAGE_BREAK_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        return AptMarkup.ANCHOR_START_MARKUP + anchor + AptMarkup.ANCHOR_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        String lnk = link.startsWith( "#" ) ? link.substring( 1 ) : link;
+        return AptMarkup.LINK_START_1_MARKUP + lnk + AptMarkup.LINK_START_2_MARKUP + text + AptMarkup.LINK_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        return AptMarkup.ITALIC_START_MARKUP + text + AptMarkup.ITALIC_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        return AptMarkup.BOLD_START_MARKUP + text + AptMarkup.BOLD_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        return AptMarkup.MONOSPACED_START_MARKUP + text + AptMarkup.MONOSPACED_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return String.valueOf( AptMarkup.BACKSLASH ) + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return AptMarkup.NON_BREAKING_SPACE_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        // "\\~, \\=, \\-, \\+, \\*, \\[, \\], \\<, \\>, \\{, \\}, \\\\"
+        StringBuffer sb = new StringBuffer();
+        sb.append( getSpecialCharacters( AptMarkup.COMMENT ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.EQUAL ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.MINUS ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.PLUS ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.STAR ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.LEFT_SQUARE_BRACKET ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.RIGHT_SQUARE_BRACKET ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.LESS_THAN ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.GREATER_THAN ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.LEFT_CURLY_BRACKET ) ).append( ",_" );
+        sb.append( getSpecialCharacters( Markup.RIGHT_CURLY_BRACKET ) ).append( ",_" );
+        sb.append( getSpecialCharacters( AptMarkup.BACKSLASH ) );
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        return text;
+    }
+
+    /**
+     * Add a backslash for a special markup character
+     *
+     * @param c
+     * @return the character with a backslash before
+     */
+    private static String getSpecialCharacters( char c )
+    {
+        return AptMarkup.BACKSLASH + "" + c;
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "~~ Simple comment with ----";
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptUtilsTest.java b/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptUtilsTest.java
new file mode 100644
index 0000000..0ab60dd
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/java/org/apache/maven/doxia/module/apt/AptUtilsTest.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.apt;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Test AptUtils.
+ *
+ * @author ltheussl
+ * @version $Id: AptUtilsTest.java 762718 2009-04-07 11:58:36Z ltheussl $
+ */
+public class AptUtilsTest
+        extends TestCase
+{
+    /**
+     * Test of isExternalLink method, of class AptUtils.
+     */
+    public void testIsExternalLink()
+    {
+        String link = "http://maven.apache.org/";
+        assertTrue( "Should be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "https://maven.apache.org/";
+        assertTrue( "Should be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "HTTPS://MAVEN.APACHE.ORG/";
+        assertTrue( "Should be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "ftp:/maven.apache.org/";
+        assertTrue( "Should be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "mailto:maven at apache.org";
+        assertTrue( "Should be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "file:/index.html";
+        assertTrue( "Should be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "resource_type://domain:port/filepathname?query_string#anchor";
+        assertTrue( "Should be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "example.pdf";
+        assertFalse( "Should NOT be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "./index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "../index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        // Windows style separators "\" are not allowed
+
+        link = "file:\\index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = ".\\index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+
+        link = "..\\index.html";
+        assertFalse( "Should NOT be an external link: " + link,
+            AptUtils.isExternalLink( link ) );
+    }
+
+    /**
+     * Test of isInternalLink method, of class AptUtils.
+     */
+    public void testIsInternalLink()
+    {
+        String link = "index.html";
+        assertTrue( "Should be an internal link: " + link, AptUtils.isInternalLink( link ) );
+        link = "file:/index.html";
+        assertFalse( "Should NOT be an internal link: " + link, AptUtils.isInternalLink( link ) );
+        link = "./index.html";
+        assertFalse( "Should NOT be an internal link: " + link, AptUtils.isInternalLink( link ) );
+    }
+
+    /**
+     * Test of isLocalLink method, of class AptUtils.
+     */
+    public void testIsLocalLink()
+    {
+        String link = "/index.html";
+        assertTrue( "Should be a local link: " + link, AptUtils.isLocalLink( link ) );
+
+        link = "./index.html";
+        assertTrue( "Should be a local link: " + link, AptUtils.isLocalLink( link ) );
+
+        link = "../index.html";
+        assertTrue( "Should be a local link: " + link, AptUtils.isLocalLink( link ) );
+
+        link = "index.html";
+        assertFalse( "Should NOT be a local link: " + link, AptUtils.isLocalLink( link ) );
+
+        link = ".\\index.html";
+        assertFalse( "Should NOT be a local link: " + link, AptUtils.isLocalLink( link ) );
+
+        link = "\\index.html";
+        assertFalse( "Should NOT be a local link: " + link, AptUtils.isLocalLink( link ) );
+
+        link = "..\\index.html";
+        assertFalse( "Should NOT be a local link: " + link, AptUtils.isLocalLink( link ) );
+    }
+
+    /**
+     * Test of encodeAnchor method, of class AptUtils.
+     */
+    public void testEncodeAnchor()
+    {
+        assertNull( AptUtils.encodeAnchor( null ) );
+        assertEquals( "a123_:_.-aBc", AptUtils.encodeAnchor( " 12!3 :_.&-a)Bc " ) );
+    }
+
+    /**
+     * Test of encodeFragment method, of class AptUtils.
+     */
+    public void testEncodeFragment()
+    {
+        assertNull( AptUtils.encodeFragment( null ) );
+        assertEquals( "abc0d", AptUtils.encodeFragment( "a B&c0)D" ) );
+    }
+
+    /**
+     * Test of linkToKey method, of class AptUtils.
+     */
+    public void testLinkToKey()
+    {
+        assertEquals( "abc56au", AptUtils.linkToKey( "aB$%C56 a&\\/'U" ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-apt/src/test/resources/test.apt b/doxia-modules/doxia-module-apt/src/test/resources/test.apt
new file mode 100644
index 0000000..0fcb00c
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/resources/test.apt
@@ -0,0 +1,110 @@
+                                    ------
+                                    Title
+                                    ------
+                                    Author
+                                    ------
+                                     Date
+
+  Paragraph 1, line 1.
+  Paragraph 1, line 2.
+
+  Paragraph 2, line 1.
+  Paragraph 2, line 2.
+
+Section title
+
+* Sub-section title
+
+** Sub-sub-section title
+
+*** Sub-sub-sub-section title
+
+**** Sub-sub-sub-sub-section title
+
+      * List item 1.
+
+      * List item 2.
+
+        Paragraph contained in list item 2.
+
+            * Sub-list item 1.
+
+            * Sub-list item 2.
+
+      * List item 3.
+        Force end of list:
+
+      []
+
++------------------------------------------+
+Verbatim text not contained in list item 3
++------------------------------------------+
+
+      [[1]] Numbered item 1.
+
+                [[A]] Numbered item A.
+
+                [[B]] Numbered item B.
+
+      [[2]] Numbered item 2.
+
+  List numbering schemes: [[1]], [[a]], [[A]], [[i]], [[I]].
+
+      [Defined term 1] of definition list.
+
+      [Defined term 2] of definition list.
+
++-------------------------------+
+Verbatim text
+                        in a box        
++-------------------------------+
+
+  --- instead of +-- suppresses the box around verbatim text.
+
+[figure] Figure caption
+
+*----------*--------------+----------------:
+| Centered | Left-aligned | Right-aligned  |
+| cell 1,1 | cell 1,2     | cell 1,3       |
+*----------*--------------+----------------:
+| cell 2,1 | cell 2,2     | cell 2,3       |
+*----------*--------------+----------------:
+Table caption
+
+  No grid, no caption:
+
+*-----*------*
+ cell | cell
+*-----*------*
+ cell | cell
+*-----*------*
+
+*---------*---------*
+|| header || header |
+*---------*---------*
+|  cell   |  cell   |
+*---------*---------*
+
+  Horizontal line:
+
+=======================================================================
+
+

+  New page.
+
+  <Italic> font. <<Bold>> font. <<<Monospaced>>> font.
+
+  {Anchor}. Link to {{Anchor}}. Link to {{http://www.pixware.fr}}. 
+  Link to {{{Anchor}showing alternate text}}.
+  Link to {{{http://www.pixware.fr}Pixware home page}}.
+
+  Force line\
+  break.
+
+  Non\ breaking\ space.
+
+  Escaped special characters: \~, \=, \-, \+, \*, \[, \], \<, \>, \{, \}, \\.
+
+  Copyright symbol: \251, \xA9, \u00a9.
+
+~~Commented out.
diff --git a/doxia-modules/doxia-module-apt/src/test/resources/test/comments.apt b/doxia-modules/doxia-module-apt/src/test/resources/test/comments.apt
new file mode 100644
index 0000000..f94b1f3
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/resources/test/comments.apt
@@ -0,0 +1,9 @@
+~~ comments before title
+~~ like a license header, for example
+ -----
+ Test DOXIA-379
+ -----
+ Herve Boutemy
+ -----
+
+Test DOXIA-379
diff --git a/doxia-modules/doxia-module-apt/src/test/resources/test/linebreak.apt b/doxia-modules/doxia-module-apt/src/test/resources/test/linebreak.apt
new file mode 100644
index 0000000..07e97c8
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/resources/test/linebreak.apt
@@ -0,0 +1,10 @@
+ -----
+ Test DOXIA-76
+ -----
+ Vincent Siveton
+ -----
+
+Test DOXIA-76
+
+ Line\
+ break.
diff --git a/doxia-modules/doxia-module-apt/src/test/resources/test/macro.apt b/doxia-modules/doxia-module-apt/src/test/resources/test/macro.apt
new file mode 100644
index 0000000..4c07b68
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/resources/test/macro.apt
@@ -0,0 +1,9 @@
+ -----
+ Test DOXIA-77
+ -----
+ Vincent Siveton
+ -----
+
+Test DOXIA-77
+
+%{snippet|id=superpom|url=http://svn.apache.org/repos/asf/maven/doxia/doxia/trunk/doxia-test-docs/src/main/resources/pom-4.0.0.xml}
diff --git a/doxia-modules/doxia-module-apt/src/test/resources/test/snippet.apt b/doxia-modules/doxia-module-apt/src/test/resources/test/snippet.apt
new file mode 100644
index 0000000..6593390
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/resources/test/snippet.apt
@@ -0,0 +1,14 @@
+
+  * list item 1
+
+%{snippet|id=test|file=pom.xml}
+
+    list paragraph text
+
+  * list item 2
+
++--
+Verbatim text
++--
+
+    list paragraph text
diff --git a/doxia-modules/doxia-module-apt/src/test/resources/test/toc.apt b/doxia-modules/doxia-module-apt/src/test/resources/test/toc.apt
new file mode 100644
index 0000000..296c9ff
--- /dev/null
+++ b/doxia-modules/doxia-module-apt/src/test/resources/test/toc.apt
@@ -0,0 +1,55 @@
+ ----
+ Test DOXIA-40
+ -----
+ Vincent Siveton
+ -----
+ January 2007
+ -----
+
+Test DOXIA-40
+
+Section 1
+
+%{toc|section=2|fromDepth=2|toDepth=4}
+
+* {SubSection 1.1}
+
+ SubSection 1.1
+
+** {Sub-SubSection 1.1.1}
+
+ SubSection 1.1.1
+
+** {Sub-SubSection 1.1.2}
+
+ SubSection 1.1.2
+
+*** {Sub-Sub-SubSection 1.1.2.1}
+
+ Sub-Sub-SubSection 1.1.2.1
+
+**** {Sub-Sub-Sub-SubSection 1.1.2.1.1}
+
+ Sub-Sub-Sub-SubSection 1.1.2.1.1
+
+*** {SubSub-SubSection 1.1.2.2}
+
+ SubSub-SubSection 1.1.2.2
+
+* {SubSection 1.2}
+
+ SubSection 1.2
+
+* {SubSection 1.3}
+
+ SubSection 1.3
+
+* {SubSection 1.4}
+
+ SubSection 1.4
+
+Section 2
+
+* {SubSection 2.1}
+
+ SubSection 2.1
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/pom.xml b/doxia-modules/doxia-module-confluence/pom.xml
new file mode 100644
index 0000000..6484e56
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/pom.xml
@@ -0,0 +1,59 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-confluence</artifactId>
+
+  <name>Doxia :: Confluence Module</name>
+  <description>A Doxia module for Confluence source documents.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+  </dependencies>
+
+  <developers>
+    <developer>
+      <name>Juan F. Codagnone</name>
+      <email>juan *at* zauber dot. com dot. ar</email>
+      <roles>
+        <role>Developer</role>
+      </roles>
+      <timezone>-3</timezone>
+    </developer>
+    <developer>
+      <name>Jason van Zyl</name>
+      <roles>
+        <role>Developer</role>
+      </roles>
+      <timezone>-5</timezone>
+    </developer>
+  </developers>
+</project>
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceMarkup.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceMarkup.java
new file mode 100644
index 0000000..bf5bde1
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceMarkup.java
@@ -0,0 +1,100 @@
+package org.apache.maven.doxia.module.confluence;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.markup.TextMarkup;
+
+/**
+ * This interface defines all markups and syntaxes used by the <b>Confluence</b> format.
+ *
+ * See <a href="http://confluence.atlassian.com/display/CONF25/Confluence+Notation+Guide+Overview">
+ * Confluence Notation Guide Overview</a>
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ConfluenceMarkup.java 706268 2008-10-20 12:49:35Z vsiveton $
+ * @since 1.0
+ */
+public interface ConfluenceMarkup
+    extends TextMarkup
+{
+    // ----------------------------------------------------------------------
+    // Confluence markups
+    // ----------------------------------------------------------------------
+
+    /** Syntax for the anchor : "{anchor:" */
+    String ANCHOR_START_MARKUP = "{anchor:";
+
+    /** Syntax for the anchor : "}" */
+    String ANCHOR_END_MARKUP = "}";
+
+    /** Syntax for the bold markup: "*" */
+    String BOLD_END_MARKUP = "*";
+
+    /** Syntax for the bold markup: "*" */
+    String BOLD_START_MARKUP = "*";
+
+    /** Syntax for the figure markup: "!" */
+    String FIGURE_END_MARKUP = "!";
+
+    /** Syntax for the figure markup: "!" */
+    String FIGURE_START_MARKUP = "!";
+
+    /** Syntax for the italic markup: "_" */
+    String ITALIC_END_MARKUP = "_";
+
+    /** Syntax for the italic markup: "_" */
+    String ITALIC_START_MARKUP = "_";
+
+    /** Syntax for the line break markup: "\\\\" */
+    String LINE_BREAK_MARKUP = "\\\\";
+
+    /** Syntax for the link end markup: "]" */
+    String LINK_END_MARKUP = "]";
+
+    /** Syntax for the link middle markup: "|" */
+    String LINK_MIDDLE_MARKUP = "|";
+
+    /** Syntax for the link start markup: "[" */
+    String LINK_START_MARKUP = "[";
+
+    /** Syntax for the list item markup: "* */
+    String LIST_ITEM_MARKUP = "* ";
+
+    /** Syntax for the mono-spaced style end: "{{" */
+    String MONOSPACED_END_MARKUP = "{{";
+
+    /** Syntax for the mono-spaced style start: "}}" */
+    String MONOSPACED_START_MARKUP = "}}";
+
+    /** Syntax for the numbering decimal markup char: "1." */
+    String NUMBERING_MARKUP = "1.";
+
+    /** Syntax for the table cell header end markup: "|" */
+    String TABLE_CELL_HEADER_END_MARKUP = "|";
+
+    /** Syntax for the table cell header start markup: "|" */
+    String TABLE_CELL_HEADER_START_MARKUP = "|";
+
+    /** Syntax for the table cell markup: "|" */
+    String TABLE_CELL_MARKUP = "|";
+
+    /** Syntax for the table row markup: "|" */
+    String TABLE_ROW_MARKUP = "|";
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceParser.java
new file mode 100644
index 0000000..3afb7c2
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceParser.java
@@ -0,0 +1,166 @@
+package org.apache.maven.doxia.module.confluence;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.doxia.module.confluence.parser.Block;
+import org.apache.maven.doxia.module.confluence.parser.BlockParser;
+import org.apache.maven.doxia.module.confluence.parser.DefinitionListBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.FigureBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.HorizontalRuleBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.ParagraphBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.SectionBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.VerbatimBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.list.ListBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.table.TableBlockParser;
+import org.apache.maven.doxia.parser.AbstractTextParser;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.util.ByLineSource;
+
+/**
+ * Parse the <a href="http://www.atlassian.com/software/confluence/">Confluence</a>.
+ * See <a href="http://confluence.atlassian.com/display/CONF25/Confluence+Notation+Guide+Overview">
+ * Confluence Notation Guide Overview</a>
+ *
+ * @version $Id: ConfluenceParser.java 946935 2010-05-21 08:46:29Z ltheussl $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="confluence"
+ */
+public class ConfluenceParser
+    extends AbstractTextParser
+{
+    private BlockParser[] parsers;
+
+    /**
+     * <p>Constructor for ConfluenceParser.</p>
+     */
+    public ConfluenceParser()
+    {
+        init();
+    }
+
+    private List parse( ByLineSource source )
+        throws ParseException
+    {
+        init();
+
+        List blocks = new ArrayList();
+
+        String line;
+
+        while ( ( line = source.getNextLine() ) != null )
+        {
+            boolean accepted = false;
+
+            for ( int i = 0; i < parsers.length; i++ )
+            {
+                BlockParser parser = parsers[i];
+
+                if ( line.trim().length() == 0 )
+                {
+                    continue;
+                }
+
+                if ( parser.accept( line, source ) )
+                {
+                    accepted = true;
+
+                    blocks.add( parser.visit( line, source ) );
+
+                    break;
+                }
+            }
+
+/*
+            if ( !accepted )
+            {
+                throw new ParseException( "don't know how to handle line: " + source.getLineNumber() + ": " + line );
+            }
+*/
+        }
+
+        return blocks;
+    }
+
+    /** {@inheritDoc} */
+    public synchronized void parse( Reader source, Sink sink )
+        throws ParseException
+    {
+        ByLineSource src = new ByLineReaderSource( source );
+
+        try
+        {
+            List blocks = parse( src );
+
+            sink.head();
+
+            sink.head_();
+
+            sink.body();
+
+            for ( Iterator i = blocks.iterator(); i.hasNext(); )
+            {
+                Block block = (Block) i.next();
+
+                block.traverse( sink );
+            }
+
+            sink.body_();
+        }
+        catch ( Exception e )
+        {
+            // TODO handle column number
+            throw new ParseException( e, src.getName(), src.getLineNumber(), -1 );
+        }
+        finally
+        {
+            setSecondParsing( false );
+            init();
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        BlockParser headingParser = new SectionBlockParser();
+        BlockParser figureParser = new FigureBlockParser();
+        BlockParser verbatimParser = new VerbatimBlockParser();
+        BlockParser definitionParser = new DefinitionListBlockParser();
+        BlockParser horizontalRuleParser = new HorizontalRuleBlockParser();
+        BlockParser listParser = new ListBlockParser();
+        BlockParser tableParser = new TableBlockParser();
+
+        BlockParser[] subparsers =
+                new BlockParser[] { headingParser, figureParser, listParser, tableParser, verbatimParser };
+        BlockParser paragraphParser = new ParagraphBlockParser( subparsers );
+
+        this.parsers =
+            new BlockParser[] { headingParser, figureParser, verbatimParser, definitionParser, horizontalRuleParser,
+                listParser, tableParser, paragraphParser };
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSink.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSink.java
new file mode 100644
index 0000000..a9313f0
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSink.java
@@ -0,0 +1,1118 @@
+package org.apache.maven.doxia.module.confluence;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Stack;
+
+import javax.swing.text.html.HTML.Attribute;
+
+import org.apache.maven.doxia.sink.AbstractTextSink;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.util.HtmlTools;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Confluence Sink implementation.
+ * <br/>
+ * <b>Note</b>: The encoding used is UTF-8.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ConfluenceSink.java 807167 2009-08-24 12:03:59Z vsiveton $
+ * @since 1.0
+ */
+public class ConfluenceSink
+    extends AbstractTextSink
+    implements ConfluenceMarkup
+{
+    /**  The writer to use. */
+    private final PrintWriter out;
+
+    /**  The writer to use. */
+    private StringWriter writer;
+
+    /** An indication on if we're in head mode. */
+    private boolean headFlag;
+
+    private int levelList = 0;
+
+    /**  listStyles. */
+    private final Stack listStyles;
+
+    /** An indication on if we're in verbatim box mode. */
+    private boolean verbatimBoxedFlag;
+
+    /** An indication on if we're in table header mode. */
+    private boolean tableHeaderFlag;
+
+    /** The link name. */
+    private String linkName;
+
+    /**
+     * Constructor, initialize the Writer and the variables.
+     *
+     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     */
+    protected ConfluenceSink( Writer writer )
+    {
+        this.out = new PrintWriter( writer );
+        this.listStyles = new Stack();
+
+        init();
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        write( ANCHOR_START_MARKUP + name + ANCHOR_END_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        anchor( name );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void anchor_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void author()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        author();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void author_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void body()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        body();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void body_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        write( BOLD_START_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        write( BOLD_END_MARKUP );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void comment( String comment )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        out.write( writer.toString() );
+        out.close();
+
+        init();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void date()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        date();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void date_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        write( " " );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        definedTerm();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definedTerm_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definition()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definition( SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definition_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionList()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionList_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionListItem()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionListItem( SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionListItem_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void figure()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figure( SinkEventAttributes attributes )
+    {
+        figure();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void figure_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void figureCaption()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        figureCaption();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void figureCaption_()
+    {
+        // nop;
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        writeEOL();
+        write( FIGURE_START_MARKUP + name + FIGURE_END_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        figureGraphics( src );
+        if ( attributes != null && attributes.getAttribute( Attribute.ALT.toString() ) != null )
+        {
+            write( attributes.getAttribute( Attribute.ALT.toString() ).toString() );
+            writeEOL( true );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        close();
+        writer.flush();
+    }
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        init();
+
+        headFlag = true;
+    }
+
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        head();
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        headFlag = false;
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void horizontalRule()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        horizontalRule();
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        write( ITALIC_START_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        write( ITALIC_END_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        write( LINE_BREAK_MARKUP );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        lineBreak();
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        linkName = name;
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        link( name );
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        linkName = null;
+        write( LINK_END_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        if ( !writer.toString().endsWith( EOL + EOL ) )
+        {
+            writeEOL( true );
+        }
+
+        levelList++;
+    }
+
+    /** {@inheritDoc} */
+    public void list( SinkEventAttributes attributes )
+    {
+        list();
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        levelList--;
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        write( StringUtils.repeat( "*", levelList ) + " " );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        listItem();
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        writeEOL( true );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        write( MONOSPACED_START_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        write( MONOSPACED_END_MARKUP );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void nonBreakingSpace()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        levelList++;
+
+        String style;
+        switch ( numbering )
+        {
+            case NUMBERING_UPPER_ALPHA:
+            case NUMBERING_LOWER_ALPHA:
+            case NUMBERING_UPPER_ROMAN:
+            case NUMBERING_LOWER_ROMAN:
+            case NUMBERING_DECIMAL:
+            default:
+                style = NUMBERING_MARKUP;
+        }
+
+        listStyles.push( style );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        numberedList( numbering );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        levelList--;
+        listStyles.pop();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        writeEOL( true );
+        String style = (String) listStyles.peek();
+        write( style + SPACE );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        numberedListItem();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        writeEOL( true );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void pageBreak()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void paragraph()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        paragraph();
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        writeEOL( true );
+        writeEOL();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void rawText( String text )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section( int level, SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section1()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section1_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section2()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section2_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section3()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section3_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section4()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section4_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section5()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section5_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section_( int level )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void sectionTitle()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        if ( level > 0 && level < 6 )
+        {
+            write( "h" + level + ". " );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        sectionTitle( 1, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        sectionTitle_( 1 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        sectionTitle( 2, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        sectionTitle_( 2 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        sectionTitle( 3, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        sectionTitle_( 3 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        sectionTitle( 4, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        sectionTitle_( 4 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        sectionTitle( 5, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        sectionTitle_( 5 );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void sectionTitle_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+        writeEOL( true );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        // nop
+        writeEOL( true );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        table();
+    }
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        writeEOL( true );
+        writeEOL();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void tableCaption()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        tableCaption();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void tableCaption_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        write( " " );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        write( " " );
+        write( TABLE_CELL_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        tableHeaderFlag = true;
+        write( TABLE_CELL_HEADER_START_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        write( TABLE_CELL_HEADER_END_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        write( TABLE_ROW_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        tableRow();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        if ( tableHeaderFlag )
+        {
+            tableHeaderFlag = false;
+            write( TABLE_ROW_MARKUP );
+        }
+        writeEOL( true );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void tableRows_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        if ( headFlag )
+        {
+            return;
+        }
+
+        if ( linkName != null )
+        {
+            write( LINK_START_MARKUP );
+        }
+
+        content( text );
+
+        if ( linkName != null )
+        {
+            write( LINK_MIDDLE_MARKUP + linkName );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        text( text );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void title()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        title();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void title_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        if ( boxed )
+        {
+            verbatimBoxedFlag = true;
+        }
+
+        if ( verbatimBoxedFlag )
+        {
+            write( "{code|borderStyle=solid}" );
+        }
+        else
+        {
+            write( "{noformat}" );
+        }
+        writeEOL( true );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        if ( verbatimBoxedFlag )
+        {
+            write( "{code}" );
+        }
+        else
+        {
+            write( "{noformat}" );
+        }
+
+        writeEOL( true );
+        writeEOL();
+    }
+
+    // ----------------------------------------------------------------------
+    // Private methods
+    // ----------------------------------------------------------------------
+
+    private void write( String text )
+    {
+        writer.write( unifyEOLs( text ) );
+    }
+
+    /**
+     * Writes a system EOL.
+     */
+    private void writeEOL()
+    {
+        write( EOL );
+    }
+
+    /**
+     * Writes a system EOL, with or without trim.
+     */
+    private void writeEOL( boolean trim )
+    {
+        if ( !trim )
+        {
+            writeEOL();
+            return;
+        }
+
+        String tmp = writer.toString().trim();
+        writer = new StringWriter();
+        writer.write( tmp );
+        write( EOL );
+    }
+
+    /**
+     * Write HTML escaped text to output.
+     *
+     * @param text The text to write.
+     */
+    protected void content( String text )
+    {
+        write( escapeHTML( text ) );
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.writer = new StringWriter();
+        this.headFlag = false;
+        this.levelList = 0;
+        this.listStyles.clear();
+        this.verbatimBoxedFlag = false;
+        this.tableHeaderFlag = false;
+        this.linkName = null;
+    }
+
+    /**
+     * Forward to HtmlTools.escapeHTML( text ).
+     *
+     * @param text the String to escape, may be null
+     * @return the text escaped, "" if null String input
+     * @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String)
+     */
+    protected static String escapeHTML( String text )
+    {
+        return HtmlTools.escapeHTML( text );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSinkFactory.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSinkFactory.java
new file mode 100644
index 0000000..a479843
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSinkFactory.java
@@ -0,0 +1,44 @@
+package org.apache.maven.doxia.module.confluence;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractTextSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Confluence implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ConfluenceSinkFactory.java 712574 2008-11-09 22:16:42Z hboutemy $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="confluence"
+ */
+public class ConfluenceSinkFactory
+    extends AbstractTextSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        // encoding can safely be ignored since it isn't written into the generated Confluence source
+        return new ConfluenceSink( writer );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSiteModule.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSiteModule.java
new file mode 100644
index 0000000..88bc39c
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/ConfluenceSiteModule.java
@@ -0,0 +1,42 @@
+package org.apache.maven.doxia.module.confluence;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.site.AbstractSiteModule;
+
+/**
+ * <p>ConfluenceSiteModule class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: ConfluenceSiteModule.java 782392 2009-06-07 14:14:16Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.module.site.SiteModule" role-hint="confluence"
+ */
+public class ConfluenceSiteModule
+    extends AbstractSiteModule
+{
+    /**
+     * Default constructor.
+     */
+    public ConfluenceSiteModule()
+    {
+        super( "confluence", "confluence", "confluence" );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/AbstractFatherBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/AbstractFatherBlock.java
new file mode 100644
index 0000000..38c1664
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/AbstractFatherBlock.java
@@ -0,0 +1,90 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * <p>Abstract AbstractFatherBlock class.</p>
+ *
+ * @version $Id: AbstractFatherBlock.java 785531 2009-06-17 09:47:59Z ltheussl $
+ */
+public abstract class AbstractFatherBlock
+    implements Block
+{
+    private  List blocks;
+
+    /**
+     * <p>before.</p>
+     *
+     * @param sink the Sink to receive events.
+     */
+    public abstract void before( Sink sink );
+
+    /**
+     * <p>after.</p>
+     *
+     * @param sink the Sink to receive events.
+     */
+    public abstract void after( Sink sink );
+
+    /**
+     * <p>Constructor for AbstractFatherBlock.</p>
+     *
+     * @param childBlocks the child blocks.
+     */
+    public AbstractFatherBlock(  List childBlocks )
+    {
+        if ( childBlocks == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+
+        this.blocks = childBlocks;
+    }
+
+    /** {@inheritDoc} */
+    public  void traverse(  Sink sink )
+    {
+        before( sink );
+
+        for ( Iterator i = blocks.iterator(); i.hasNext(); )
+        {
+            Block block = (Block) i.next();
+
+            block.traverse( sink );
+        }
+
+        after( sink );
+    }
+
+    /**
+     * <p>Getter for the field <code>blocks</code>.</p>
+     *
+     * @return a {@link java.util.List} object.
+     */
+    public  List getBlocks()
+    {
+        return blocks;
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/AnchorBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/AnchorBlock.java
new file mode 100644
index 0000000..909aa5c
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/AnchorBlock.java
@@ -0,0 +1,47 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @version $Id: AnchorBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class AnchorBlock
+    implements Block
+{
+    private  String name;
+
+    AnchorBlock(  String name )
+    {
+        if ( name == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+        this.name = name;
+    }
+
+    /** {@inheritDoc} */
+    public  void traverse(  Sink sink )
+    {
+        sink.anchor( name );
+        sink.anchor_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/Block.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/Block.java
new file mode 100644
index 0000000..4d66127
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/Block.java
@@ -0,0 +1,37 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * <p>Block interface.</p>
+ *
+ * @version $Id: Block.java 785531 2009-06-17 09:47:59Z ltheussl $
+ */
+public interface Block
+{
+    /**
+     * <p>traverse.</p>
+     *
+     * @param sink a {@link org.apache.maven.doxia.sink.Sink} object.
+     */
+    void traverse(  Sink sink );
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/BlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/BlockParser.java
new file mode 100644
index 0000000..f487360
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/BlockParser.java
@@ -0,0 +1,50 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * <p>BlockParser interface.</p>
+ *
+ * @version $Id: BlockParser.java 785531 2009-06-17 09:47:59Z ltheussl $
+ */
+public interface BlockParser
+{
+    /**
+     * accept.
+     *
+     * @param line the line.
+     * @param source the source.
+     * @return boolean true if valid.
+     */
+    boolean accept( String line, ByLineSource source );
+
+    /**
+     * visit.
+     *
+     * @param line the line.
+     * @param source the source.
+     * @return boolean true if valid.
+     * @throws org.apache.maven.doxia.parser.ParseException if any.
+     */
+    Block visit( String line, ByLineSource source ) throws ParseException;
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/BoldBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/BoldBlock.java
new file mode 100644
index 0000000..8d7ca68
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/BoldBlock.java
@@ -0,0 +1,55 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * <p>BoldBlock class.</p>
+ *
+ * @version $Id: BoldBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+public class BoldBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * <p>Constructor for BoldBlock.</p>
+     *
+     * @param childBlocks the child blocks.
+     */
+    public BoldBlock( List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        sink.bold();
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        sink.bold_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ChildBlocksBuilder.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ChildBlocksBuilder.java
new file mode 100644
index 0000000..21e1682
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ChildBlocksBuilder.java
@@ -0,0 +1,322 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Re-usable builder that can be used to generate paragraph and list item text from a string containing all the content
+ * and wiki formatting. This class is intentionally stateful, but cheap to create, so create one as needed and keep it
+ * on the stack to preserve stateless behaviour in the caller.
+ *
+ * @author Dave Syer
+ * @version $Id: ChildBlocksBuilder.java 773275 2009-05-09 21:40:21Z hboutemy $
+ * @since 1.1
+ */
+public class ChildBlocksBuilder
+{
+    private boolean insideBold = false;
+
+    private boolean insideItalic = false;
+
+    private boolean insideLink = false;
+
+    private List blocks = new ArrayList();
+
+    private StringBuffer text = new StringBuffer();
+
+    private String input;
+
+    private boolean insideMonospaced;
+
+    /**
+     * <p>Constructor for ChildBlocksBuilder.</p>
+     *
+     * @param input the input.
+     */
+    public ChildBlocksBuilder( String input )
+    {
+        this.input = input;
+    }
+
+    /**
+     * Utility method to convert marked up content into blocks for rendering.
+     *
+     * @return a list of Blocks that can be used to render it
+     */
+    public List getBlocks()
+    {
+        List specialBlocks = new ArrayList();
+
+        for ( int i = 0; i < input.length(); i++ )
+        {
+            char c = input.charAt( i );
+
+            switch ( c )
+            {
+                case '*':
+                    if ( insideBold )
+                    {
+                        insideBold = false;
+                        specialBlocks = getList( new BoldBlock( getChildren( text, specialBlocks ) ), specialBlocks );
+                        text = new StringBuffer();
+                    }
+                    else
+                    {
+                        text = addTextBlockIfNecessary( blocks, specialBlocks, text );
+                        insideBold = true;
+                    }
+
+                    break;
+                case '_':
+                    if ( insideItalic )
+                    {
+                        insideItalic = false;
+                        specialBlocks = getList( new ItalicBlock( getChildren( text, specialBlocks ) ), specialBlocks );
+                        text = new StringBuffer();
+                    }
+                    else
+                    {
+                        text = addTextBlockIfNecessary( blocks, specialBlocks, text );
+                        insideItalic = true;
+                    }
+
+                    break;
+                case '[':
+                    insideLink = true;
+                    text = addTextBlockIfNecessary( blocks, specialBlocks, text );
+                    break;
+                case ']':
+                    if ( insideLink )
+                    {
+                        boolean addHTMLSuffix = false;
+                        String link = text.toString();
+
+                        if ( !link.endsWith( ".html" ) )
+                        {
+                            if ( link.indexOf( "http" ) < 0 )
+                            {
+                                addHTMLSuffix = true;
+                            }
+                        }
+                        if ( link.indexOf( "|" ) > 0 )
+                        {
+                            String[] pieces = StringUtils.split( text.toString(), "|" );
+
+                            if ( addHTMLSuffix )
+                            {
+                                if ( pieces[1].indexOf( "#" ) < 0 )
+                                {
+                                    pieces[1] = pieces[1].concat( ".html" );
+                                }
+                                else
+                                {
+                                    if ( !pieces[1].startsWith( "#" ) )
+                                    {
+                                        String[] temp = pieces[1].split( "#" );
+                                        pieces[1] = temp[0] + ".html#" + temp[1];
+                                    }
+                                }
+                            }
+
+                            blocks.add( new LinkBlock( pieces[1], pieces[0] ) );
+                        }
+                        else
+                        {
+                            String value = link;
+
+                            if ( link.startsWith( "#" ) )
+                            {
+                                value = link.substring( 1 );
+                            }
+
+                            if ( addHTMLSuffix )
+                            {
+                                if ( link.indexOf( "#" ) < 0 )
+                                {
+                                    link = link.concat( ".html" );
+                                }
+                                else
+                                {
+                                    if ( !link.startsWith( "#" ) )
+                                    {
+                                        String[] temp = link.split( "#" );
+                                        link = temp[0] + ".html#" + temp[1];
+                                    }
+                                }
+                            }
+
+                            blocks.add( new LinkBlock( link, value ) );
+                        }
+
+                        text = new StringBuffer();
+                        insideLink = false;
+                    }
+
+                    break;
+                case '{':
+
+                    text = addTextBlockIfNecessary( blocks, specialBlocks, text );
+
+                    if ( charAt( input, i ) == '{' ) // it's monospaced
+                    {
+                        i++;
+                        insideMonospaced = true;
+                    }
+                    // else it's a confluence macro...
+
+                    break;
+                case '}':
+
+                    // System.out.println( "line = " + line );
+
+                    if ( charAt( input, i ) == '}' )
+                    {
+                        i++;
+                        insideMonospaced = false;
+                        specialBlocks = getList( new MonospaceBlock( getChildren( text, specialBlocks ) ),
+                                                 specialBlocks );
+                        text = new StringBuffer();
+                    }
+                    else
+                    {
+                        String name = text.toString();
+                        if ( name.startsWith( "anchor:" ) )
+                        {
+                            blocks.add( new AnchorBlock( name.substring( "anchor:".length() ) ) );
+                        }
+                        else
+                        {
+                            blocks.add( new TextBlock( "{" + name + "}" ) );
+                        }
+                        text = new StringBuffer();
+                    }
+
+                    break;
+                case '\\':
+
+                    // System.out.println( "line = " + line );
+
+                    if ( charAt( input, i ) == '\\' )
+                    {
+                        i++;
+                        text = addTextBlockIfNecessary( blocks, specialBlocks, text );
+                        blocks.add( new LinebreakBlock() );
+                    }
+                    else
+                    {
+                        i++;
+                        text.append( input.charAt( i ) );
+                    }
+
+                    break;
+                default:
+                    text.append( c );
+            }
+
+            if ( !specialBlocks.isEmpty() )
+            {
+                if ( !insideItalic && !insideBold && !insideMonospaced )
+                {
+                    blocks.addAll( specialBlocks );
+                    specialBlocks.clear();
+                }
+            }
+
+        }
+
+        if ( text.length() > 0 )
+        {
+            blocks.add( new TextBlock( text.toString() ) );
+        }
+
+        return blocks;
+    }
+
+    private List getList( Block block, List currentBlocks )
+    {
+        List list = new ArrayList();
+
+        if ( insideBold || insideItalic || insideMonospaced )
+        {
+            list.addAll( currentBlocks );
+        }
+
+        list.add( block );
+
+        return list;
+    }
+
+    private List getChildren( StringBuffer buffer, List currentBlocks )
+    {
+        String txt = buffer.toString().trim();
+
+        if ( currentBlocks.isEmpty() && StringUtils.isEmpty( txt ) )
+        {
+            return new ArrayList();
+        }
+
+        ArrayList list = new ArrayList();
+
+        if ( !insideBold && !insideItalic && !insideMonospaced )
+        {
+            list.addAll( currentBlocks );
+        }
+
+        if ( StringUtils.isEmpty( txt ) )
+        {
+            return list;
+        }
+
+        list.add( new TextBlock( txt ) );
+
+        return list;
+    }
+
+    private static char charAt( String input, int i )
+    {
+        return input.length() > i + 1 ? input.charAt( i + 1 ) : '\0';
+    }
+
+    private StringBuffer addTextBlockIfNecessary( List blcks, List specialBlocks, StringBuffer txt )
+    {
+        if ( txt.length() == 0 )
+        {
+            return txt;
+        }
+
+        TextBlock textBlock = new TextBlock( txt.toString() );
+
+        if ( !insideBold && !insideItalic && !insideMonospaced )
+        {
+            blcks.add( textBlock );
+        }
+        else
+        {
+            specialBlocks.add( textBlock );
+        }
+
+        return new StringBuffer();
+    }
+
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/DefinitionListBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/DefinitionListBlock.java
new file mode 100644
index 0000000..8efb377
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/DefinitionListBlock.java
@@ -0,0 +1,67 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * @version $Id: DefinitionListBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class DefinitionListBlock
+    implements Block
+{
+    private String title;
+
+    private List text;
+
+    DefinitionListBlock( String title, String text )
+    {
+        this.title = title;
+        this.text = new ChildBlocksBuilder( text ).getBlocks();
+    }
+
+    /** {@inheritDoc} */
+    public void traverse( Sink sink )
+    {
+        sink.definitionList();
+
+        if ( !StringUtils.isEmpty( title ) )
+        {
+            sink.definedTerm();
+            sink.text( title );
+            sink.definedTerm_();
+        }
+
+        sink.definition();
+
+        for ( Iterator iterator = text.iterator(); iterator.hasNext(); )
+        {
+            Block block = (Block) iterator.next();
+            block.traverse( sink );
+        }
+
+        sink.definition_();
+        sink.definitionList_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/DefinitionListBlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/DefinitionListBlockParser.java
new file mode 100644
index 0000000..0d1effa
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/DefinitionListBlockParser.java
@@ -0,0 +1,92 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.util.ByLineSource;
+
+/**
+ * <p>DefinitionListBlockParser class.</p>
+ *
+ * @author Dave Syer
+ * @version $Id: DefinitionListBlockParser.java 746983 2009-02-23 12:28:41Z vsiveton $
+ * @since 1.1
+ */
+public class DefinitionListBlockParser
+    implements BlockParser
+{
+    static final String LS = System.getProperty( "line.separator" );
+
+    /** {@inheritDoc} */
+    public boolean accept( String line, ByLineSource source )
+    {
+        return ( line.startsWith( "{note" ) || line.startsWith( "{tip" )
+            || line.startsWith( "{info" ) || line.startsWith( "{quote" ) );
+    }
+
+    /** {@inheritDoc} */
+    public Block visit( String line, ByLineSource source )
+        throws ParseException
+    {
+        StringBuffer text = new StringBuffer();
+        StringBuffer title = new StringBuffer();
+
+        int index = line.indexOf( "title=" );
+
+        if ( index >= 0 )
+        {
+            line = line.substring( index + 6 );
+
+            while ( !( line.indexOf( "}" ) >= 0 ) && line != null )
+            {
+                append( title, line );
+                line = source.getNextLine();
+            }
+
+            if ( line != null )
+            {
+                append( title, line.substring( 0, line.indexOf( "}" ) ) );
+            }
+        }
+
+        while ( ( line = source.getNextLine() ) != null )
+        {
+            if ( line.startsWith( "{note" ) || line.startsWith( "{tip" )
+                || line.startsWith( "{info" ) || line.startsWith( "{quote" ) )
+            {
+                break;
+            }
+
+            append( text, line );
+        }
+
+        return new DefinitionListBlock( title.toString(), text.toString() );
+    }
+
+    private void append( StringBuffer title, String line )
+    {
+        if ( title.length() > 0 )
+        {
+            title.append( " " );
+        }
+
+        title.append( line.trim() );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/FigureBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/FigureBlock.java
new file mode 100644
index 0000000..911ac37
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/FigureBlock.java
@@ -0,0 +1,60 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @version $Id: FigureBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class FigureBlock
+    implements Block
+{
+    private String location;
+
+    private String caption;
+
+    FigureBlock( String location )
+    {
+        this.location = location;
+    }
+
+    FigureBlock( String image, String caption )
+    {
+        this.location = image;
+        this.caption = caption;
+    }
+
+    /** {@inheritDoc} */
+    public void traverse( Sink sink )
+    {
+        sink.figure();
+        sink.figureGraphics( location );
+
+        if ( caption != null && caption.length() > 0 )
+        {
+            sink.figureCaption();
+            new TextBlock( caption ).traverse( sink );
+            sink.figureCaption_();
+        }
+
+        sink.figure_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/FigureBlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/FigureBlockParser.java
new file mode 100644
index 0000000..15185e4
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/FigureBlockParser.java
@@ -0,0 +1,105 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.util.ByLineSource;
+
+/**
+ * <p>FigureBlockParser class.</p>
+ *
+ * @version $Id: FigureBlockParser.java 763889 2009-04-10 08:57:55Z bentmann $
+ * @since 1.1
+ */
+public class FigureBlockParser
+    implements BlockParser
+{
+    /** {@inheritDoc} */
+    public boolean accept( String line, ByLineSource source )
+    {
+        return line.startsWith( "!" ) && line.lastIndexOf( "!" ) > 1;
+    }
+
+    /** {@inheritDoc} */
+    public Block visit( String line, ByLineSource source )
+        throws ParseException
+    {
+        String image = line.substring( 1, line.lastIndexOf( "!" ) );
+        if ( image.indexOf( "|" ) >= 0 )
+        {
+            // DOXIA-303: handle figure attributes
+            image = image.substring( 0, image.indexOf( "|" ) );
+        }
+
+        line = line.substring( line.lastIndexOf( "!" ) + 1 ).trim();
+
+        if ( line.startsWith( "\\\\" ) )
+        {
+            // ignore linebreak at start of caption
+            line = line.substring( 2 );
+        }
+
+        String caption = line + appendUntilEmptyLine( source );
+
+        if ( caption.trim().length() > 0 )
+        {
+            return new FigureBlock( image, caption );
+        }
+
+        return new FigureBlock( image );
+    }
+
+    /**
+     * Slurp lines from the source starting with the given line appending them together into a StringBuffer until an
+     * empty line is reached, and while the source contains more lines.
+     *
+     * @param source the source to read new lines from
+     * @return a StringBuffer appended with lines
+     * @throws ParseException
+     */
+    private String appendUntilEmptyLine( ByLineSource source )
+        throws ParseException
+    {
+        StringBuffer text = new StringBuffer();
+
+        String line;
+
+        while ( ( line = source.getNextLine() ) != null )
+        {
+
+            if ( line.trim().length() == 0 )
+            {
+                break;
+            }
+
+            if ( text.length() == 0 )
+            {
+                text.append( line.trim() );
+            }
+            else
+            {
+                text.append( " " + line.trim() );
+            }
+
+        }
+
+        return text.toString();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/HorizontalRuleBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/HorizontalRuleBlock.java
new file mode 100644
index 0000000..53503b8
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/HorizontalRuleBlock.java
@@ -0,0 +1,35 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @version $Id: HorizontalRuleBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class HorizontalRuleBlock
+    implements Block
+{
+    /** {@inheritDoc} */
+    public  void traverse(  Sink sink )
+    {
+        sink.horizontalRule();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/HorizontalRuleBlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/HorizontalRuleBlockParser.java
new file mode 100644
index 0000000..b164251
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/HorizontalRuleBlockParser.java
@@ -0,0 +1,45 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * <p>HorizontalRuleBlockParser class.</p>
+ *
+ * @version $Id: HorizontalRuleBlockParser.java 746983 2009-02-23 12:28:41Z vsiveton $
+ */
+public class HorizontalRuleBlockParser
+    implements BlockParser
+{
+    /** {@inheritDoc} */
+    public  boolean accept( String line, ByLineSource source )
+    {
+        return line.startsWith( "----" );
+    }
+
+    /** {@inheritDoc} */
+    public  Block visit(  String line,  ByLineSource source )
+        throws ParseException
+    {
+        return new HorizontalRuleBlock();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ItalicBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ItalicBlock.java
new file mode 100644
index 0000000..393a8b7
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ItalicBlock.java
@@ -0,0 +1,48 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @version $Id: ItalicBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class ItalicBlock
+    extends AbstractFatherBlock
+{
+    ItalicBlock( List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        sink.italic();
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        sink.italic_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/LinebreakBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/LinebreakBlock.java
new file mode 100644
index 0000000..f7790e2
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/LinebreakBlock.java
@@ -0,0 +1,36 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @author Dave Syer
+ * @version $Id: LinebreakBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class LinebreakBlock
+    implements Block
+{
+    /** {@inheritDoc} */
+    public void traverse( Sink sink )
+    {
+        sink.lineBreak();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/LinkBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/LinkBlock.java
new file mode 100644
index 0000000..e9ff97e
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/LinkBlock.java
@@ -0,0 +1,53 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @version $Id: LinkBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class LinkBlock
+    implements Block
+{
+    private  String reference;
+
+    private  String text;
+
+    LinkBlock(  String reference,  String text )
+    {
+        if ( reference == null || text == null )
+        {
+            throw new IllegalArgumentException( "arguments can't be null" );
+        }
+        this.reference = reference;
+        this.text = text;
+    }
+
+    /** {@inheritDoc} */
+    public  void traverse(  Sink sink )
+    {
+        sink.link( reference );
+
+        sink.text( text );
+
+        sink.link_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/MonospaceBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/MonospaceBlock.java
new file mode 100644
index 0000000..48377f5
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/MonospaceBlock.java
@@ -0,0 +1,47 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @version $Id: MonospaceBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class MonospaceBlock extends AbstractFatherBlock
+{
+    MonospaceBlock( List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        sink.monospaced();
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        sink.monospaced_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ParagraphBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ParagraphBlock.java
new file mode 100644
index 0000000..d599fce
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ParagraphBlock.java
@@ -0,0 +1,64 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @author Juan F. Codagnone
+ * @version $Id: ParagraphBlock.java 761569 2009-04-03 08:38:09Z ltheussl $
+ */
+class ParagraphBlock
+    extends AbstractFatherBlock
+{
+
+    private boolean generateParagraphTags = true;
+
+    ParagraphBlock( List blocks )
+    {
+        super( blocks );
+    }
+
+    ParagraphBlock( List blocks, boolean generateParagraphTags )
+    {
+        super( blocks );
+        this.generateParagraphTags = generateParagraphTags;
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        if ( this.generateParagraphTags )
+        {
+            sink.paragraph();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        if ( this.generateParagraphTags )
+        {
+            sink.paragraph_();
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ParagraphBlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ParagraphBlockParser.java
new file mode 100644
index 0000000..c9a7a33
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/ParagraphBlockParser.java
@@ -0,0 +1,141 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.util.ByLineSource;
+
+/**
+ * <p>ParagraphBlockParser class.</p>
+ *
+ * @version $Id: ParagraphBlockParser.java 775115 2009-05-15 12:54:18Z ltheussl $
+ */
+public class ParagraphBlockParser
+    implements BlockParser
+{
+    private BlockParser[] parsers;
+
+    /**
+     * <p>Constructor for ParagraphBlockParser.</p>
+     *
+     * @param parsers the parsers.
+     */
+    public ParagraphBlockParser( BlockParser[] parsers )
+    {
+        super();
+        this.parsers = parsers;
+    }
+
+    /** {@inheritDoc} */
+    public boolean accept( String line, ByLineSource source )
+    {
+        return true;
+    }
+
+    /**
+     * Visit the Block.
+     *
+     * @param line the line to visit.
+     * @param source the source.
+     * @param generateParagraphTags whether to generate a paragraph.
+     * @return the visited Block.
+     *
+     * @throws org.apache.maven.doxia.parser.ParseException if any
+     */
+    public Block visit( String line, ByLineSource source, boolean generateParagraphTags )
+            throws ParseException
+    {
+        if ( generateParagraphTags )
+        {
+            return this.visit( line, source );
+        }
+        else
+        {
+            ChildBlocksBuilder builder = new ChildBlocksBuilder( line );
+            return new ParagraphBlock( builder.getBlocks(), generateParagraphTags );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Block visit( String line, ByLineSource source )
+        throws ParseException
+    {
+
+        ChildBlocksBuilder builder = new ChildBlocksBuilder( appendUntilEmptyLine( line, source ) );
+        return new ParagraphBlock( builder.getBlocks() );
+    }
+
+    /**
+     * Slurp lines from the source starting with the given line appending them together into a StringBuffer until an
+     * empty line is reached, and while the source contains more lines. The result can be passed to the
+     * {@link #getBlocks(String)} method.
+     *
+     * @param line the first line
+     * @param source the source to read new lines from
+     * @return a StringBuffer appended with lines
+     * @throws ParseException
+     */
+    private String appendUntilEmptyLine( String line, ByLineSource source )
+        throws ParseException
+    {
+        StringBuffer text = new StringBuffer();
+
+        do
+        {
+
+            if ( line.trim().length() == 0 )
+            {
+                break;
+            }
+
+            boolean accepted = false;
+            for ( int i = 0; i < parsers.length; i++ )
+            {
+                BlockParser parser = parsers[i];
+                if ( parser.accept( line, source ) )
+                {
+                    accepted = true;
+                    break;
+                }
+            }
+            if ( accepted )
+            {
+                // Slightly fragile - if any of the parsers need to do this in order to decide whether to accept a line,
+                // then it will barf because of the contract of ByLineSource
+                source.ungetLine();
+                break;
+            }
+
+            if ( text.length() == 0 )
+            {
+                text.append( line.trim() );
+            }
+            else
+            {
+                text.append( " " + line.trim() );
+            }
+
+        }
+        while ( ( line = source.getNextLine() ) != null );
+
+        return text.toString();
+    }
+
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/SectionBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/SectionBlock.java
new file mode 100644
index 0000000..8607bfa
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/SectionBlock.java
@@ -0,0 +1,99 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: SectionBlock.java 772533 2009-05-07 07:35:06Z ltheussl $
+ */
+class SectionBlock
+    implements Block
+{
+    private Block text;
+
+    private int level;
+
+    SectionBlock( Block text, int level )
+    {
+        this.text = text;
+        this.level = level;
+    }
+
+    /** {@inheritDoc} */
+    public void traverse( Sink sink )
+    {
+        if ( level == Sink.SECTION_LEVEL_1 )
+        {
+            sink.section1();
+            sink.sectionTitle1();
+        }
+        else if ( level == Sink.SECTION_LEVEL_2 )
+        {
+            sink.section2();
+            sink.sectionTitle2();
+        }
+        else if ( level == Sink.SECTION_LEVEL_3 )
+        {
+            sink.section3();
+            sink.sectionTitle3();
+        }
+        else if ( level == Sink.SECTION_LEVEL_4 )
+        {
+            sink.section4();
+            sink.sectionTitle4();
+        }
+        else if ( level == Sink.SECTION_LEVEL_5 )
+        {
+            sink.section5();
+            sink.sectionTitle5();
+        }
+
+        this.text.traverse( sink );
+
+        if ( level == Sink.SECTION_LEVEL_1 )
+        {
+            sink.sectionTitle1_();
+            sink.section1_();
+        }
+        else if ( level == Sink.SECTION_LEVEL_2 )
+        {
+            sink.sectionTitle2_();
+            sink.section2_();
+        }
+        else if ( level == Sink.SECTION_LEVEL_3 )
+        {
+            sink.sectionTitle3_();
+            sink.section3_();
+
+        }
+        else if ( level == Sink.SECTION_LEVEL_4 )
+        {
+            sink.sectionTitle4_();
+            sink.section4_();
+        }
+        else if ( level == Sink.SECTION_LEVEL_5 )
+        {
+            sink.sectionTitle5_();
+            sink.section5_();
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/SectionBlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/SectionBlockParser.java
new file mode 100644
index 0000000..31e0297
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/SectionBlockParser.java
@@ -0,0 +1,75 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * <p>SectionBlockParser class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: SectionBlockParser.java 772533 2009-05-07 07:35:06Z ltheussl $
+ */
+public class SectionBlockParser
+    implements BlockParser
+{
+    /** {@inheritDoc} */
+    public boolean accept( String line, ByLineSource source )
+    {
+        if ( line.startsWith( "h1." ) )
+        {
+            return true;
+        }
+        else if ( line.startsWith( "h2." ) )
+        {
+            return true;
+        }
+        else if ( line.startsWith( "h3." ) )
+        {
+            return true;
+        }
+        else if ( line.startsWith( "h4." ) )
+        {
+            return true;
+        }
+        else if ( line.startsWith( "h5." ) )
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public Block visit( String line, ByLineSource source )
+        throws ParseException
+    {
+        int level = Integer.parseInt( Character.toString( line.charAt( 1 ) ) );
+
+        String title = line.substring( 3 ).trim();
+
+        BlockParser headingParser = new SectionBlockParser();
+        BlockParser figureParser = new FigureBlockParser();
+        BlockParser[] subparsers = new BlockParser[] { headingParser, figureParser };
+
+        return new SectionBlock( new ParagraphBlockParser( subparsers ).visit( title, source, false ), level );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/TextBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/TextBlock.java
new file mode 100644
index 0000000..36f512f
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/TextBlock.java
@@ -0,0 +1,50 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * <p>TextBlock class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: TextBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+public class TextBlock
+    implements Block
+{
+    private String text;
+
+    /**
+     * <p>Constructor for TextBlock.</p>
+     *
+     * @param text the text.
+     */
+    public TextBlock( String text )
+    {
+        this.text = text;
+    }
+
+    /** {@inheritDoc} */
+    public void traverse( Sink sink )
+    {
+        sink.text( text );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/VerbatimBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/VerbatimBlock.java
new file mode 100644
index 0000000..0d9e624
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/VerbatimBlock.java
@@ -0,0 +1,48 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: VerbatimBlock.java 747837 2009-02-25 15:50:39Z ltheussl $
+ */
+class VerbatimBlock
+    implements Block
+{
+    private String text;
+
+    VerbatimBlock( String text )
+    {
+        this.text = text;
+    }
+
+    /** {@inheritDoc} */
+    public void traverse( Sink sink )
+    {
+        sink.verbatim( SinkEventAttributeSet.BOXED );
+
+        sink.text( text );
+
+        sink.verbatim_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/VerbatimBlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/VerbatimBlockParser.java
new file mode 100644
index 0000000..2432878
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/VerbatimBlockParser.java
@@ -0,0 +1,66 @@
+package org.apache.maven.doxia.module.confluence.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * <p>VerbatimBlockParser class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: VerbatimBlockParser.java 746983 2009-02-23 12:28:41Z vsiveton $
+ */
+public class VerbatimBlockParser
+    implements BlockParser
+{
+    static final String LS = System.getProperty( "line.separator" );
+
+    /** {@inheritDoc} */
+    public boolean accept( String line, ByLineSource source )
+    {
+        if ( line.startsWith( "{code" ) || line.startsWith( "{noformat}" ) )
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public Block visit( String line, ByLineSource source )
+        throws ParseException
+    {
+        StringBuffer text = new StringBuffer();
+
+        while ( ( line = source.getNextLine() ) != null )
+        {
+            if ( line.startsWith( "{code}" ) || line.startsWith( "{noformat}" ) )
+            {
+                break;
+            }
+
+            // TODO
+            text.append( line ).append( LS );
+        }
+
+        return new VerbatimBlock( text.toString() );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/BulletedListBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/BulletedListBlock.java
new file mode 100644
index 0000000..a5ab391
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/BulletedListBlock.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.confluence.parser.list;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: BulletedListBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class BulletedListBlock
+    extends ListBlock
+{
+    BulletedListBlock(  List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public void before( Sink sink )
+    {
+        sink.list();
+    }
+
+    /** {@inheritDoc} */
+    public void after( Sink sink )
+    {
+        sink.list_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListBlock.java
new file mode 100644
index 0000000..0bb6be2
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListBlock.java
@@ -0,0 +1,37 @@
+package org.apache.maven.doxia.module.confluence.parser.list;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.confluence.parser.AbstractFatherBlock;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: ListBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+abstract class ListBlock
+    extends AbstractFatherBlock
+{
+    ListBlock(  List childBlocks )
+    {
+        super( childBlocks );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListBlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListBlockParser.java
new file mode 100644
index 0000000..40ab17c
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListBlockParser.java
@@ -0,0 +1,147 @@
+package org.apache.maven.doxia.module.confluence.parser.list;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.module.confluence.parser.Block;
+import org.apache.maven.doxia.module.confluence.parser.BlockParser;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * <p>ListBlockParser class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: ListBlockParser.java 947266 2010-05-22 07:49:50Z ltheussl $
+ */
+public class ListBlockParser
+    implements BlockParser
+{
+    /** Constant <code>BULLETED_LIST=0</code> */
+    public static final int BULLETED_LIST = 0;
+
+    /** Constant <code>NUMBERED_LIST=1</code> */
+    public static final int NUMBERED_LIST = 1;
+
+    /** {@inheritDoc} */
+    public boolean accept( String line, ByLineSource source )
+    {
+        if ( isList( line ) )
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public Block visit( String line, ByLineSource source )
+        throws ParseException
+    {
+        TreeListBuilder treeListBuilder = new TreeListBuilder();
+
+        StringBuffer text = new StringBuffer();
+
+        do
+        {
+            if ( line.trim().length() == 0 )
+            {
+                break;
+            }
+
+            if ( text.length() > 0 && isList( line ) )
+            {
+                // We reached a new line with list prefix
+                addItem( treeListBuilder, text );
+            }
+
+            if ( text.length() == 0 )
+            {
+                text.append( line.trim() );
+            }
+            else
+            {
+                text.append( " " + line.trim() );
+            }
+
+        }
+        while ( ( line = source.getNextLine() ) != null );
+
+        if ( text.length() > 0 )
+        {
+            addItem( treeListBuilder, text );
+        }
+
+        return treeListBuilder.getBlock();
+    }
+
+    private void addItem( TreeListBuilder treeListBuilder, StringBuffer text )
+    {
+        String item = text.toString();
+        int level = getLevel( item );
+
+        if ( isBulletedList( item, level - 1 ) )
+        {
+            treeListBuilder.feedEntry( BULLETED_LIST, level, item.substring( level ) );
+        }
+        else
+        {
+            treeListBuilder.feedEntry( NUMBERED_LIST, level, item.substring( level ) );
+        }
+        text.setLength( 0 );
+    }
+
+    private int getLevel( String line )
+    {
+        int level = 0;
+
+        while ( line.charAt( level ) == '*' || line.charAt( level ) == '-' || line.charAt( level ) == '#' )
+        {
+            level++;
+        }
+
+        return level;
+    }
+
+    private boolean isBulletedList( String line, int deph )
+    {
+        return ( line.charAt( deph ) == '*' || line.charAt( deph ) == '-' );
+    }
+
+    private boolean isList( String line )
+    {
+        line = line.trim();
+
+        if ( line.startsWith( "*" ) || line.startsWith( "-" ) || line.startsWith( "#" ) )
+        {
+            String temp = line.substring( 1 );
+            while ( temp.length() > 0 && ( temp.charAt( 0 ) == '*' || temp.charAt( 0 ) == '-' || temp.charAt( 0 ) == '#' ) )
+            {
+                temp = temp.substring( 1 );
+            }
+
+            if ( temp.length() > 0 && temp.charAt( 0 ) == ' ' )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListItemBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListItemBlock.java
new file mode 100644
index 0000000..7d907f7
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/ListItemBlock.java
@@ -0,0 +1,68 @@
+package org.apache.maven.doxia.module.confluence.parser.list;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.confluence.parser.AbstractFatherBlock;
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @version $Id: ListItemBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class ListItemBlock
+    extends AbstractFatherBlock
+{
+    private  ListBlock innerList;
+
+    ListItemBlock(  List blocks )
+    {
+        this( blocks, null );
+    }
+
+    ListItemBlock(  List blocks,  ListBlock innerList )
+    {
+        super( blocks );
+
+        this.innerList = innerList;
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        sink.listItem();
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        if ( innerList != null )
+        {
+            innerList.traverse( sink );
+        }
+
+        sink.listItem_();
+    }
+
+    ListBlock getInnerList()
+    {
+        return innerList;
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/NumberedListBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/NumberedListBlock.java
new file mode 100644
index 0000000..bdf49d7
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/NumberedListBlock.java
@@ -0,0 +1,53 @@
+package org.apache.maven.doxia.module.confluence.parser.list;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * <p>NumberedListBlock class.</p>
+ *
+ * @version $Id: NumberedListBlock.java 776605 2009-05-20 07:53:34Z ltheussl $
+ */
+public class NumberedListBlock
+    extends ListBlock
+{
+    /**
+     * @param childBlocks
+     */
+    NumberedListBlock(  List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public void before( Sink sink )
+    {
+        sink.numberedList( Sink.NUMBERING_DECIMAL );
+    }
+
+    /** {@inheritDoc} */
+    public void after( Sink sink )
+    {
+        sink.numberedList_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/TreeComponent.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/TreeComponent.java
new file mode 100644
index 0000000..69b9d54
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/TreeComponent.java
@@ -0,0 +1,124 @@
+package org.apache.maven.doxia.module.confluence.parser.list;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: TreeComponent.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class TreeComponent
+{
+    private static final String EOL = System.getProperty( "line.separator" );
+
+    private List children = new ArrayList();
+
+    private String text;
+
+    private TreeComponent father;
+
+    private int type;
+
+    TreeComponent( TreeComponent father, String text, int type )
+    {
+        this.text = text;
+        this.father = father;
+        this.type = type;
+    }
+
+    List getChildren()
+    {
+        return children;
+    }
+
+    TreeComponent addChildren( String t, int ttype )
+    {
+        if ( t == null )
+        {
+            throw new IllegalArgumentException( "argument is null" );
+        }
+
+        TreeComponent ret = new TreeComponent( this, t, ttype );
+
+        children.add( ret );
+
+        return ret;
+    }
+
+    TreeComponent getFather()
+    {
+        return father;
+    }
+
+    int getDepth()
+    {
+        int ret = 0;
+
+        TreeComponent c = this;
+
+        while ( ( c = c.getFather() ) != null )
+        {
+            ret++;
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    public String toString()
+    {
+        return toString( "" );
+    }
+
+    String toString( String indent )
+    {
+        StringBuffer sb = new StringBuffer();
+
+        if ( father != null )
+        {
+            sb.append( indent );
+            sb.append( "- " );
+            sb.append( text );
+            sb.append( EOL );
+        }
+
+        for ( Iterator i = children.iterator(); i.hasNext(); )
+        {
+            TreeComponent lc = (TreeComponent) i.next();
+
+            sb.append( lc.toString( indent + "   " ) );
+        }
+
+        return sb.toString();
+    }
+
+    String getText()
+    {
+        return text;
+    }
+
+    int getType()
+    {
+        return type;
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/TreeListBuilder.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/TreeListBuilder.java
new file mode 100644
index 0000000..540e155
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/list/TreeListBuilder.java
@@ -0,0 +1,138 @@
+package org.apache.maven.doxia.module.confluence.parser.list;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.doxia.module.confluence.parser.ChildBlocksBuilder;
+
+/**
+ * <p>TreeListBuilder class.</p>
+ *
+ * @version $Id: TreeListBuilder.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+public class TreeListBuilder
+{
+    private  TreeComponent root;
+
+    private TreeComponent current;
+
+    TreeListBuilder()
+    {
+        root = new TreeComponent( null, "root", 0 );
+
+        current = root;
+    }
+
+    void feedEntry( int type, int level, String text )
+    {
+        int currentDepth = current.getDepth();
+
+        int incomingLevel = level - 1;
+
+        if ( incomingLevel == currentDepth )
+        {
+            // nothing to move
+        }
+        else if ( incomingLevel > currentDepth )
+        {
+            // el actual ahora es el �ltimo que insert�
+            List components = current.getChildren();
+
+            if ( components.size() == 0 )
+            {
+                /* for example:
+                 *        * item1
+                 *     * item2
+                 */
+                for ( int i = 0, n = incomingLevel - currentDepth; i < n; i++ )
+                {
+                    current = current.addChildren( "", type );
+                }
+            }
+            else
+            {
+                current = (TreeComponent) components.get( components.size() - 1 );
+            }
+        }
+        else
+        {
+            for ( int i = 0, n = currentDepth - incomingLevel; i < n; i++ )
+            {
+                current = current.getFather();
+
+                if ( current == null )
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        current.addChildren( text.trim(), type );
+    }
+
+    ListBlock getBlock()
+    {
+        return getList( root );
+    }
+
+    private ListBlock getList( TreeComponent treeComponent )
+    {
+        List list = getListItems( treeComponent );
+
+        int type = ( (TreeComponent) treeComponent.getChildren().get( 0 ) ).getType();
+
+        if ( type == ListBlockParser.BULLETED_LIST )
+        {
+            return new BulletedListBlock( list );
+        }
+
+        return new NumberedListBlock( list );
+    }
+
+    private List getListItems( TreeComponent tc )
+    {
+        List blocks = new ArrayList();
+
+        for ( Iterator i = tc.getChildren().iterator(); i.hasNext(); )
+        {
+            TreeComponent child = (TreeComponent) i.next();
+
+            List childBlocks = new ArrayList();
+
+            if ( child.getFather() != null )
+            {
+                childBlocks.addAll( new ChildBlocksBuilder( child.getText() ).getBlocks() );
+            }
+
+            if ( child.getChildren().size() != 0 )
+            {
+                blocks.add( new ListItemBlock( childBlocks, getList( child ) ) );
+            }
+            else
+            {
+                blocks.add( new ListItemBlock( childBlocks ) );
+            }
+        }
+
+        return blocks;
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableBlock.java
new file mode 100644
index 0000000..343f918
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableBlock.java
@@ -0,0 +1,63 @@
+package org.apache.maven.doxia.module.confluence.parser.table;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.confluence.parser.AbstractFatherBlock;
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @version $Id: TableBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class TableBlock
+    extends AbstractFatherBlock
+{
+    TableBlock( List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        sink.table();
+        sink.tableRows( getJustification(), false );
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        sink.tableRows_();
+        sink.table_();
+    }
+
+    private int[] getJustification()
+    {
+        final AbstractFatherBlock b = ( (AbstractFatherBlock) getBlocks().get( 0 ) );
+        int[] justification = new int[b.getBlocks().size()];
+        for ( int i = 0; i < justification.length; i++ )
+        {
+            justification[i] = Sink.JUSTIFY_CENTER;
+        }
+
+        return justification;
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableBlockParser.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableBlockParser.java
new file mode 100644
index 0000000..5079195
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableBlockParser.java
@@ -0,0 +1,145 @@
+package org.apache.maven.doxia.module.confluence.parser.table;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.module.confluence.ConfluenceMarkup;
+import org.apache.maven.doxia.module.confluence.parser.FigureBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.ParagraphBlockParser;
+import org.apache.maven.doxia.module.confluence.parser.SectionBlockParser;
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.module.confluence.parser.BlockParser;
+import org.apache.maven.doxia.module.confluence.parser.Block;
+import org.apache.maven.doxia.module.confluence.parser.BoldBlock;
+import org.apache.maven.doxia.parser.ParseException;
+import org.codehaus.plexus.util.StringUtils;
+
+
+/**
+ * Parse tables
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TableBlockParser.java 775115 2009-05-15 12:54:18Z ltheussl $
+ */
+public class TableBlockParser
+    implements BlockParser
+{
+    private static final String EMPTY_STRING = "";
+    private static final String ANY_CHARACTER = ".*";
+    private static final String ESCAPE_CHARACTER = "\\";
+
+    /** {@inheritDoc} */
+    public  boolean accept( String line, ByLineSource source )
+    {
+        return line.startsWith( "|" );
+    }
+
+    /** {@inheritDoc} */
+    public  Block visit(  String line,  ByLineSource source )
+        throws ParseException
+    {
+        if ( !accept( line, source ) )
+        {
+            throw new IllegalAccessError( "call accept before this ;)" );
+        }
+
+        List rows = new ArrayList();
+
+        String l = line;
+
+        do
+        {
+            l = l.substring( 0, l.lastIndexOf( "|" ) );
+
+            List cells = new ArrayList();
+
+            BlockParser headingParser = new SectionBlockParser();
+            BlockParser figureParser = new FigureBlockParser();
+            BlockParser[] subparsers = new BlockParser[] { headingParser, figureParser };
+            BlockParser paragraphParser = new ParagraphBlockParser( subparsers );
+
+            if ( l.startsWith( "||" ) )
+            {
+                String[] text = StringUtils.split( l, "||" );
+
+
+                for ( int i = 0; i < text.length; i++ )
+                {
+                    List textBlocks = new ArrayList();
+
+                    textBlocks.add( ( ( ParagraphBlockParser) paragraphParser )
+                            .visit(text[i], new ByLineReaderSource( new StringReader( EMPTY_STRING ) ), false ) );
+
+                    List blocks = new ArrayList();
+
+                    blocks.add( new BoldBlock( textBlocks ) );
+
+                    cells.add( new TableCellHeaderBlock( blocks ) );
+                }
+            }
+            else
+            {
+                int it = 0;
+                String[] text = StringUtils.split( l, "|" );
+                List texts = new LinkedList();
+
+
+                while ( it < text.length )
+                {
+                    if ( text[it].matches( ANY_CHARACTER + ESCAPE_CHARACTER
+                                + ConfluenceMarkup.LINK_START_MARKUP + ANY_CHARACTER )
+                            && !text[it].matches( ANY_CHARACTER + ESCAPE_CHARACTER
+                                + ConfluenceMarkup.LINK_END_MARKUP + ANY_CHARACTER ) )
+                    {
+                        texts.add( text[it] + ConfluenceMarkup.TABLE_CELL_MARKUP + text[it + 1] );
+                        it += 2;
+                        continue;
+                     }
+                    texts.add( text[it] );
+                    it++;
+                }
+
+                Object[] pText = texts.toArray();
+                for ( int i = 0; i < pText.length; i++ )
+                {
+                    List blocks = new ArrayList();
+
+                    blocks.add( ( (ParagraphBlockParser) paragraphParser ).visit( (String) pText[i],
+                            new ByLineReaderSource( new StringReader( EMPTY_STRING ) ), false ) );
+
+                    cells.add( new TableCellBlock( blocks ) );
+                }
+            }
+
+            rows.add( new TableRowBlock( cells ) );
+        }
+
+        while ( ( l = source.getNextLine() ) != null && accept( l, source ) );
+
+        assert rows.size() >= 1;
+
+        return new TableBlock( rows );
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableCellBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableCellBlock.java
new file mode 100644
index 0000000..2c63dd2
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableCellBlock.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.confluence.parser.table;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.confluence.parser.AbstractFatherBlock;
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @version $Id: TableCellBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class TableCellBlock
+    extends AbstractFatherBlock
+{
+    TableCellBlock( List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        sink.tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        sink.tableCell_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableCellHeaderBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableCellHeaderBlock.java
new file mode 100644
index 0000000..b42c650
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableCellHeaderBlock.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.confluence.parser.table;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.confluence.parser.AbstractFatherBlock;
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @version $Id: TableCellHeaderBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class TableCellHeaderBlock
+    extends AbstractFatherBlock
+{
+    TableCellHeaderBlock( List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        sink.tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        sink.tableHeaderCell_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableRowBlock.java b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableRowBlock.java
new file mode 100644
index 0000000..19ed919
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/main/java/org/apache/maven/doxia/module/confluence/parser/table/TableRowBlock.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.confluence.parser.table;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.confluence.parser.AbstractFatherBlock;
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.List;
+
+/**
+ * @version $Id: TableRowBlock.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class TableRowBlock
+    extends AbstractFatherBlock
+{
+    TableRowBlock( List childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    public  void before(  Sink sink )
+    {
+        sink.tableRow();
+    }
+
+    /** {@inheritDoc} */
+    public  void after(  Sink sink )
+    {
+        sink.tableRow_();
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/site/site.xml b/doxia-modules/doxia-module-confluence/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/java/org/apache/maven/doxia/module/confluence/ConfluenceParserTest.java b/doxia-modules/doxia-module-confluence/src/test/java/org/apache/maven/doxia/module/confluence/ConfluenceParserTest.java
new file mode 100644
index 0000000..8733a0e
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/java/org/apache/maven/doxia/module/confluence/ConfluenceParserTest.java
@@ -0,0 +1,558 @@
+package org.apache.maven.doxia.module.confluence;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.apache.maven.doxia.parser.AbstractParserTest;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.TextSink;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Test class for ConfluenceParser.
+ */
+public class ConfluenceParserTest
+    extends AbstractParserTest
+{
+    private ConfluenceParser parser;
+
+    private StringWriter output;
+
+    private Reader reader;
+
+    private Writer writer;
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        parser = (ConfluenceParser) lookup( Parser.ROLE, "confluence" );
+
+        output = null;
+        reader = null;
+        writer = null;
+    }
+
+    /** {@inheritDoc} */
+    protected void tearDown()
+        throws Exception
+    {
+        IOUtil.close( output );
+        IOUtil.close( reader );
+        IOUtil.close( writer );
+
+        super.tearDown();
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return parser;
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "confluence";
+    }
+
+    /** @throws Exception */
+    public void testMarkupTestPage()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "test" );
+        assertContainsLines( result, "end:body" );
+    }
+
+    /** @throws Exception */
+    public void testParagraphWithSimpleFormatting()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "simple-paragraph" );
+
+        assertContainsLines( result, "begin:bold\ntext: bold\n" );
+        assertContainsLines( result, "begin:italic\ntext: italic\n" );
+        assertContainsLines( result, "begin:monospaced\ntext: monospaced\n" );
+        assertContainsLines( result, "begin:link, name: http://jira.codehaus.org\ntext: http://jira.codehaus.org" );
+        assertContainsLines( result, "begin:link, name: http://jira.codehaus.org\ntext: JIRA\n" );
+        // four paragraphs in the input...
+        assertEquals( 5, result.split( "end:paragraph" ).length );
+    }
+
+    /** @throws Exception */
+    public void testLineBreak()
+        throws Exception
+    {
+        String lineBreak = getLineBreakString();
+
+        String result = locateAndParseTestSourceFile( "linebreak" );
+
+        assertContainsLines( result, "Line\n" + lineBreak );
+        assertContainsLines( result, "with 2\n" + lineBreak );
+        assertContainsLines( result, "inline\n" + lineBreak );
+    }
+
+    /** @throws Exception */
+    public void testEscapes()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "escapes" );
+
+        assertContainsLines( result, "asterisk *" );
+        assertContainsLines( result, "underline _" );
+        assertContainsLines( result, "asterisk *not bold*" );
+        assertContainsLines( result, "underline _not italic_" );
+        assertContainsLines( result, "normal character" );
+    }
+
+    /** @throws Exception */
+    public void testSectionTitles()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "section" );
+
+        for ( int i = 1; i <= 5; i++ )
+        {
+            assertContainsLines( "Could not locate section " + i + " title", result,
+                    "sectionTitle" + i + "\ntext: Section" + i );
+        }
+
+        assertContainsLines( "Section title has leading space", result, "sectionTitle1\ntext: TitleWithLeadingSpace" );
+    }
+
+    /** @throws Exception */
+    public void testNestedBulletList()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "nested-list" );
+
+        assertContainsLines( "Nested list not found", result,
+                "begin:listItem\ntext: A top level list item\nbegin:list" );
+        // two lists in the input...
+        assertEquals( 3, result.split( "end:list\n" ).length );
+        // ...and 4 list items
+        assertEquals( 5, result.split( "end:listItem\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testNestedHeterogenousList()
+        throws Exception
+    {
+    	String result = locateAndParseTestSourceFile( "nested-list-heterogenous" );
+
+        // test heterogenous list
+        assertContainsLines( "Nested list not found", result, "begin:listItem\ntext: A top level list item\nbegin:numberedList" );
+
+        // exactly one list and one numberedList
+        assertEquals( 2, result.split( "begin:list\n" ).length );
+        assertEquals( 2, result.split( "begin:numberedList" ).length );
+
+        // ...and 4 list items
+        assertEquals( 5, result.split( "end:listItem\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testListWithSimpleFormatting()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "simple-list" );
+
+        assertContainsLines( result, "begin:bold\ntext: bold\n" );
+        assertContainsLines( result, "begin:italic\ntext: italic\n" );
+        assertContainsLines( result, "begin:monospaced\ntext: monospaced\n" );
+        assertContainsLines( result, "begin:link, name: http://jira.codehaus.org\ntext: http://jira.codehaus.org\n" );
+        assertContainsLines( result, "begin:link, name: http://jira.codehaus.org\ntext: JIRA\n" );
+        assertContainsLines( result, "begin:listItem\ntext: Item with no formatting\nend:listItem\n" );
+        assertContainsLines( result, "begin:listItem\ntext: One bullet\nend:listItem\n" );
+        assertContainsLines( result, "begin:listItem\ntext: A list item with more than one line\nend:listItem\n" );
+        // 3 lists in the input...
+        assertEquals( 4, result.split( "end:list\n" ).length );
+        // ...and 7 list items
+        assertEquals( 8, result.split( "end:listItem\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testAnchor()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "anchor" );
+
+        assertContainsLines( result, "begin:paragraph\nbegin:anchor, name: start\nend:anchor" );
+        assertContainsLines( result, "begin:anchor, name: middle\nend:anchor" );
+        assertContainsLines( result, "begin:paragraph\ntext: Simple paragraph\nbegin:anchor, name: end\nend:anchor" );
+        // 3 anchors in the input...
+        assertEquals( 4, result.split( "end:anchor\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testUnknownMacro()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "unknown-macro" );
+
+        assertContainsLines( result, "begin:paragraph\ntext: {unknown:start}" );
+    }
+
+    /** @throws Exception */
+    public void testCodeMacro()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "code" );
+
+        assertContainsLines( result, "begin:verbatim, boxed: true\ntext: public class Cat {" );
+        // 5 paragraphs in the input...
+        assertEquals( 5, result.split( "end:paragraph\n" ).length );
+        // 3 verbatim in the input...
+        assertEquals( 3, result.split( "end:verbatim\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testFigure()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "figure" );
+
+        assertContainsLines( result, "begin:figure\nfigureGraphics, name: images/photo.jpg\nend:figure\n" );
+        assertContainsLines( result, "attempted inline !image.jpg! (should fail)" );
+        // this isn't ideal... Doxia captions are not the same as what people would use to add text to a confluence
+        assertContainsLines( result, "figureGraphics, name: images/photo.jpg\n"
+            + "begin:figureCaption\ntext: With caption on same line\n" + "end:figureCaption" );
+        assertContainsLines( result, "figureGraphics, name: images/nolinebreak.jpg\n"
+            + "begin:figureCaption\ntext: With caption underneath and no linebreak\nend:figureCaption" );
+        // ignore linebreak after figure insert...
+        assertContainsLines( result, "figureGraphics, name: images/linebreak.jpg\n"
+            + "begin:figureCaption\ntext: With caption underneath and linebreak\nend:figureCaption" );
+        // ignore formtting in caption...
+        assertContainsLines( result, "figureGraphics, name: images/bold.jpg\n"
+            + "begin:figureCaption\ntext: With *bold* caption underneath\nend:figureCaption" );
+        // DOXIA-303: image attributes are ignored
+        assertContainsLines( result, "begin:figure\nfigureGraphics, name: image.gif\nend:figure\n" );
+
+        // 2 paragraphs in the input... (the figures do not go in a paragraph by analogy with AptParser)
+        assertEquals( 3, result.split( "end:paragraph\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testLink()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "link" );
+
+        assertContainsLines( result, "begin:link, name: middle.html\ntext: middle\nend:link" );
+        assertContainsLines( result, "begin:link, name: end.html\ntext: end\nend:link" );
+        assertContainsLines( result, "begin:link, name: link.html\ntext: alias\nend:link" );
+        assertContainsLines( result, "begin:link, name: link.html#anchor\ntext: link#anchor\nend:link" );
+        assertContainsLines( result, "begin:link, name: #simple\ntext: simple\nend:link" );
+        // 3 paragraphs in the input...
+        assertEquals( 4, result.split( "end:paragraph\n" ).length );
+        // 5 links in the input...
+        assertEquals( 6, result.split( "end:link\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testTableWithLinks()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "table-link" );
+
+        assertContainsLines( result, "begin:tableCell\nbegin:link, name: http://example.com/release.0.1.3/ex-win32-win32.x86.zip\ntext: Download\nend:link\n\n\nend:tableCell\n" );
+        assertContainsLines( result, "begin:tableCell\nbegin:link, name: http://example.com/release.0.1.2/ex-win32-win32.x86.zip\ntext: http://example.com/release.0.1.2/ex-win32-win32.x86.zip\nend:link\n\n\nend:tableCell\n" );
+
+        // 3 links in the input
+        assertEquals( 4, result.split( "end:link\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testParagraphWithList()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "paragraph-list" );
+
+        assertContainsLines( result, "begin:paragraph\ntext: A paragraph\nend:paragraph\n" );
+        assertContainsLines( result, "begin:listItem\ntext: A nested list item\nend:listItem\n" );
+        assertContainsLines( result, "begin:listItem\ntext: Another nested list item with two lines\nend:listItem\n" );
+        // 2 paragraphs in the input...
+        assertEquals( 3, result.split( "end:paragraph\n" ).length );
+        // 1 list in the input...
+        assertEquals( 2, result.split( "end:list\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testParagraphWithFigure()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "paragraph-figure" );
+
+        assertContainsLines( result, "begin:paragraph\ntext: A paragraph\nend:paragraph\n" );
+        assertContainsLines( result, "begin:figure\nfigureGraphics, name: images/logo.png\nbegin:figureCaption\ntext: with a figure\nend:figureCaption" );
+        // 2 paragraphs in the input...
+        assertEquals( 3, result.split( "end:paragraph\n" ).length );
+        // 1 figure in the input...
+        assertEquals( 2, result.split( "end:figure\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testParagraphWithHeader()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "paragraph-header" );
+
+        assertContainsLines( result, "begin:paragraph\ntext: A paragraph\nend:paragraph\n" );
+        assertContainsLines( result, "begin:section2\nbegin:sectionTitle2\ntext: A header\nend:sectionTitle2" );
+        // 3 paragraphs in the input...
+        assertEquals( 4, result.split( "end:paragraph\n" ).length );
+        // 1 header in the input...
+        assertEquals( 2, result.split( "end:sectionTitle2\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testNestedFormats()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "nested-format" );
+
+        assertContainsLines( result, "begin:bold\nbegin:italic\ntext: bold italic\nend:italic" );
+        assertContainsLines( result, "begin:italic\nbegin:bold\ntext: italic bold\nend:bold" );
+        assertContainsLines( result, "begin:bold\nbegin:monospaced\ntext: bold monospaced\nend:monospaced" );
+        assertContainsLines( result, "text: A paragraph with \nbegin:bold\ntext: bold \nbegin:italic\ntext: italic\nend:italic" );
+        assertContainsLines( result, "begin:italic\ntext: italic \nbegin:bold\ntext: bold\nend:bold" );
+        assertContainsLines( result, "begin:bold\ntext: bold \nbegin:monospaced\ntext: monospaced\nend:monospaced" );
+        // 2 paragraphs in the input...
+        assertEquals( 3, result.split( "end:paragraph\n" ).length );
+        // 6 bolds in the input...
+        assertEquals( 7, result.split( "end:bold\n" ).length );
+        // 4 italics in the input...
+        assertEquals( 5, result.split( "end:italic\n" ).length );
+        // 2 monospaced in the input...
+        assertEquals( 3, result.split( "end:monospaced\n" ).length );
+    }
+
+    /** @throws Exception */
+    public void testNoteInfoTipQuote()
+        throws Exception
+    {
+        String result = locateAndParseTestSourceFile( "note-tip-info" );
+
+        assertContainsLines( result, "begin:definedTerm\ntext: Be Careful\nend:definedTerm\n" );
+        assertContainsLines( result, "begin:definition\ntext: The body of the note here..\nend:definition" );
+        assertContainsLines( result, "begin:definedTerm\ntext: Guess What?\nend:definedTerm\n" );
+        assertContainsLines( result, "begin:definition\ntext: The body of the tip here..\nend:definition" );
+        assertContainsLines( result, "begin:definedTerm\ntext: Some Info\nend:definedTerm\n" );
+        assertContainsLines( result, "begin:definition\ntext: The body of the info here..\nend:definition" );
+        assertContainsLines( result, "begin:definedTerm\ntext: Simon Says\nend:definedTerm\n" );
+        assertContainsLines( result, "begin:definition\ntext: The body of the \nbegin:bold\ntext: quote\nend:bold" );
+
+        // 5 paragraphs in the input...
+        assertEquals( 6, result.split( "end:paragraph\n" ).length );
+        // 4 dinitionList in the input...
+        assertEquals( 5, result.split( "end:definitionList\n" ).length );
+    }
+
+    /**
+     * DOXIA-247
+     *
+     * @throws ParseException if something goes wrong.
+     */
+    public void testEndBracket()
+        throws ParseException
+    {
+        String document = "Test"
+            + "\n\n* list1"
+            + "\n\n* list2"
+            + "\n\n* list2"
+            + "\n{pre}123{/pre}";
+
+        output = new StringWriter();
+        Sink sink = new TextSink( output );
+
+        /* parsing with additional space at end works */
+        createParser().parse( new StringReader( document + " " ), sink );
+        assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
+
+        /* parsing with document ending in } should not fail */
+        try
+        {
+            createParser().parse( new StringReader( document ), sink );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+            fail( "parsing with document ending in } should not fail" );
+        }
+
+        assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
+    }
+
+    /**
+     * DOXIA-247
+     *
+     * @throws ParseException
+     */
+    public void testEndBracketInList()
+        throws ParseException
+    {
+        String document1 = "Test"
+            + "\n\n* list1"
+            + "\n\n* list2"
+            + "\n\n* list2{pre}123{/pre} "
+            + "\n123";
+
+        String document2 = "Test"
+            + "\n\n* list1"
+            + "\n\n* list2"
+            + "\n\n* list2{pre}123{/pre}"
+            + "\n123";
+
+        output = new StringWriter();
+        Sink sink = new TextSink( output );
+
+        /* parsing with additional space at end of list item works */
+        createParser().parse( new StringReader( document1 ), sink );
+        assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
+
+        /* parsing with end of list item ending in } should not fail */
+        try
+        {
+            createParser().parse( new StringReader( document2 ), sink );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+            fail( "parsing with end of list item ending in } should not fail" );
+        }
+
+        assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
+    }
+
+    public void testDoxia382SinkCannotBeReused()
+            throws ParseException
+    {
+        String document1 = "Test A"
+            + "\n\n* list1"
+            + "\n\n* list2"
+            + "\n\n* list2{pre}123{/pre} "
+            + "\n123";
+
+        String document2 = "Test B"
+            + "\n\n* list1"
+            + "\n\n* list2"
+            + "\n\n* list2{pre}123{/pre}"
+            + "\n123";
+
+        output = new StringWriter();
+        Sink sink = new TextSink( new FilterWriter( output )
+        {
+            public void close() throws IOException
+            {
+                super.close();
+                this.out = null;
+            }
+
+            public void write( String str, int off, int len )
+                    throws IOException
+            {
+                if ( out == null )
+                {
+                    throw new IOException( "Writing to an already closed Writer" );
+                }
+            }
+        });
+
+        createParser().parse( new StringReader( document1 ), sink );
+        createParser().parse( new StringReader( document2 ), sink );
+    }
+    
+
+    /**
+     * DOXIA-370
+     *
+     * @throws ParseException
+     */
+    public void testSeparatorInParagraph()
+        throws ParseException
+    {
+        String document = "Up\n---\nDown\n";
+
+        output = new StringWriter();
+        Sink sink = new TextSink( output );
+
+        /* parsing with separator in middle of paragraph */
+        createParser().parse( new StringReader( document ), sink );
+        assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
+
+    }
+
+    private void assertContainsLines( String message, String result, String lines )
+    {
+        lines = StringUtils.replace( lines, "\n", EOL );
+        if ( message != null )
+        {
+            assertTrue( message, result.indexOf( lines ) != -1 );
+        }
+        else
+        {
+            assertTrue( result.indexOf( lines ) != -1 );
+        }
+    }
+
+    private void assertContainsLines( String result, String lines )
+    {
+        this.assertContainsLines( null, result, lines );
+    }
+
+    private String getLineBreakString()
+    {
+        StringWriter sw = new StringWriter();
+        Sink sink = new TextSink( sw );
+        sink.lineBreak();
+
+        return sw.toString();
+    }
+
+    private String locateAndParseTestSourceFile( String stem )
+        throws IOException, ParseException
+    {
+        output = new StringWriter();
+        reader = getTestReader( stem, outputExtension() );
+        writer = getTestWriter( stem, "txt" );
+
+        Sink sink = new TextSink( output );
+        createParser().parse( reader, sink );
+
+        // write to file
+        String expected = output.toString();
+        writer.write( expected );
+        writer.flush();
+        return expected;
+    }
+
+}
diff --git a/doxia-modules/doxia-module-confluence/src/test/java/org/apache/maven/doxia/module/confluence/ConfluenceSinkTest.java b/doxia-modules/doxia-module-confluence/src/test/java/org/apache/maven/doxia/module/confluence/ConfluenceSinkTest.java
new file mode 100644
index 0000000..9adb89e
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/java/org/apache/maven/doxia/module/confluence/ConfluenceSinkTest.java
@@ -0,0 +1,288 @@
+package org.apache.maven.doxia.module.confluence;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.util.HtmlTools;
+
+/**
+ * Test the Confluence Sink
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ConfluenceSinkTest.java 779565 2009-05-28 11:24:35Z vsiveton $
+ * @see ConfluenceSink
+ */
+public class ConfluenceSinkTest
+    extends AbstractSinkTest
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new ConfluenceSink( writer );
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        return ConfluenceMarkup.ANCHOR_START_MARKUP + anchor + ConfluenceMarkup.ANCHOR_END_MARKUP + anchor;
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return false;
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        return null;
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        return ConfluenceMarkup.BOLD_START_MARKUP + text + ConfluenceMarkup.BOLD_END_MARKUP;
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        return null;
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        return EOL + ConfluenceMarkup.FIGURE_START_MARKUP + source + ConfluenceMarkup.FIGURE_END_MARKUP + caption;
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        return ConfluenceMarkup.ITALIC_START_MARKUP + text + ConfluenceMarkup.ITALIC_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return ConfluenceMarkup.LINE_BREAK_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        return ConfluenceMarkup.LINK_START_MARKUP + text + ConfluenceMarkup.LINK_MIDDLE_MARKUP + link
+            + ConfluenceMarkup.LINK_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return ConfluenceMarkup.LIST_ITEM_MARKUP + item + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        return ConfluenceMarkup.MONOSPACED_START_MARKUP + text + ConfluenceMarkup.MONOSPACED_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return ConfluenceMarkup.NUMBERING_MARKUP + " " + item + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        return text + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return "h1. " + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return "h2. " + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        return "h3. " + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        return "h4. " + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        return "h5. " + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        return "| " + cell + " |" + EOL + "Table_caption" + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        return HtmlTools.escapeHTML( text );
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        return "{code|borderStyle=solid}" + EOL + text + "{code}" + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "twiki";
+    }
+
+    // ----------------------------------------------------------------------
+    // Override unused tests
+    // ----------------------------------------------------------------------
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testAuthor()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testDate()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testHead()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testBody()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testTitle()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testDefinitionList()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "";
+    }
+}
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/anchor.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/anchor.confluence
new file mode 100644
index 0000000..425dcba
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/anchor.confluence
@@ -0,0 +1,7 @@
+h1. Section Title
+
+{anchor:start}Simple paragraph.
+
+Simple paragraph{anchor:end}
+
+Simple {anchor:middle} paragraph.
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/code.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/code.confluence
new file mode 100644
index 0000000..d4f73d5
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/code.confluence
@@ -0,0 +1,21 @@
+Simple paragraph.
+
+{code:title=Cat.java}
+public class Cat {
+	public void sitOn(Mat mat) {
+		// ... code here <angle brackets>
+	}
+}
+{code}
+
+Another paragraph (the title of the code block is ignored).
+
+Simple paragraph with embedded code
+{code}
+public class Cat {
+	public void sitOn(Mat mat) {
+		// ... code here <angle brackets>
+	}
+}
+{code}
+in the same paragraph (this didn't work until DOXIA-181). See also DOXIA-302.
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/escapes.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/escapes.confluence
new file mode 100644
index 0000000..82e4533
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/escapes.confluence
@@ -0,0 +1,6 @@
+Simple paragraph with asterisk \* and underline \_.
+
+Simple paragraph with asterisk \*not bold\* and underline \_not italic\_.
+
+Simple paragraph with normal \character escaped.
+
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/figure.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/figure.confluence
new file mode 100644
index 0000000..cb44247
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/figure.confluence
@@ -0,0 +1,18 @@
+Simple paragraph.
+
+!images/photo.jpg!
+
+Simple paragraph with attempted inline !image.jpg! (should fail).
+
+!images/photo.jpg! With caption on same line
+
+!images/linebreak.jpg!\\
+With caption underneath and linebreak
+
+!images/nolinebreak.jpg!
+With caption underneath and no linebreak
+
+!images/bold.jpg!
+With *bold* caption underneath
+
+!image.gif|align=right, vspace=4!
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/linebreak.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/linebreak.confluence
new file mode 100644
index 0000000..d076a6d
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/linebreak.confluence
@@ -0,0 +1,5 @@
+Line\\
+break.
+
+Line\\with 2\\inline\\
+Breaks.
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/link.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/link.confluence
new file mode 100644
index 0000000..7d54368
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/link.confluence
@@ -0,0 +1,5 @@
+Line with [middle] link
+
+Line with link at the [end]
+
+Line with [link#anchor] and [#simple] anchor and [alias|link]
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/nested-format.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/nested-format.confluence
new file mode 100644
index 0000000..9dbbefb
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/nested-format.confluence
@@ -0,0 +1,6 @@
+
+A paragraph with *_bold italic_* _*italic bold*_ *{{bold monospaced}}*
+
+A paragraph with *bold _italic_* _italic *bold*_ *bold {{monospaced}}*
+
+
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/nested-list-heterogenous.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/nested-list-heterogenous.confluence
new file mode 100644
index 0000000..3788418
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/nested-list-heterogenous.confluence
@@ -0,0 +1,4 @@
+* A top level list item 
+*# A nested list item 
+*# Another nested list item 
+* Back at the top level
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/nested-list.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/nested-list.confluence
new file mode 100644
index 0000000..2a4e719
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/nested-list.confluence
@@ -0,0 +1,8 @@
+
+A paragraph
+
+* A top level list item
+** A nested list item
+** Another nested list item
+* Back at the top level
+
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/note-tip-info.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/note-tip-info.confluence
new file mode 100644
index 0000000..75e83a8
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/note-tip-info.confluence
@@ -0,0 +1,27 @@
+Simple paragraph.
+
+{note:title=Be Careful}
+The body of the note here..
+{note}
+
+Tip
+
+{tip:title=Guess What?}
+The body of the tip here..
+{tip}
+
+Info
+
+{info:title=Some 
+Info}
+The body of the info here..
+{info}
+
+Quote
+
+{quote:title=Simon Says}
+The body of the *quote* here..
+{quote}
+
+(In all cases there is no way to reverse the Doxia output 
+back to confluence because they are all rendered as a defitionList.)
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-figure.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-figure.confluence
new file mode 100644
index 0000000..6d3b027
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-figure.confluence
@@ -0,0 +1,6 @@
+
+A paragraph
+!images/logo.png!
+with a figure
+
+Another paragraph
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-header.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-header.confluence
new file mode 100644
index 0000000..5467b0b
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-header.confluence
@@ -0,0 +1,6 @@
+
+A paragraph
+h2. A header
+with a header
+
+Another paragraph
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-list.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-list.confluence
new file mode 100644
index 0000000..df0d9f8
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/paragraph-list.confluence
@@ -0,0 +1,8 @@
+
+A paragraph
+* A nested list item
+* Another nested list item
+with two lines
+
+Back at the top level
+
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/section.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/section.confluence
new file mode 100644
index 0000000..d28fe4c
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/section.confluence
@@ -0,0 +1,9 @@
+h1. Section1
+
+h2. Section2
+h3. Section3
+
+h4. Section4
+h5. Section5
+
+h1.  TitleWithLeadingSpace
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/simple-list.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/simple-list.confluence
new file mode 100644
index 0000000..7b213a4
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/simple-list.confluence
@@ -0,0 +1,14 @@
+* Simple paragraph with *bold* and _italic_ text.
+* Here is a link to [JIRA|http://jira.codehaus.org]
+* Here is a link with no text [http://jira.codehaus.org]
+* This is some {{monospaced}} text.
+* Item with no formatting
+
+Paragraph
+
+* One bullet
+
+* A list item with
+more than one line 
+
+*bold text, not a list!*
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/simple-paragraph.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/simple-paragraph.confluence
new file mode 100644
index 0000000..00d15f2
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/simple-paragraph.confluence
@@ -0,0 +1,8 @@
+Simple paragraph with *bold* and _italic_ text.
+
+Here is a link to [JIRA|http://jira.codehaus.org]
+
+Here is a link with no text [http://jira.codehaus.org]
+
+This is some {{monospaced}} text.
+
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/table-link.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/table-link.confluence
new file mode 100644
index 0000000..cdab3a2
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/table-link.confluence
@@ -0,0 +1,4 @@
+|| Version || Download || Format || 
+|0.1.1 |[Download|http://example.com/release.0.1.1/ex-win32-win32.x86.zip]| 12-12-2008 | zip |
+|0.1.2 |[http://example.com/release.0.1.2/ex-win32-win32.x86.zip]| 04-12-2008 | zip |
+|0.1.3 |[Download|http://example.com/release.0.1.3/ex-win32-win32.x86.zip]| 03-11-2008 | zip |
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/table.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/table.confluence
new file mode 100644
index 0000000..227a8e0
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/table.confluence
@@ -0,0 +1,13 @@
+
+Simple table:
+
+||heading 1||heading 2||heading 3||
+|col A1|col A2|col A3|
+|col B1|col B2|col B3|
+
+Table with links (DOXIA-301):
+
+|| Version || Download || Date || Format || 
+|0.1.1 | [Download|http://example.com/release.0.1.1/ex-win32-win32.x86.zip] | 12-12-2008 | zip |
+|0.1.2 | [Download|http://example.com/release.0.1.2/ex-win32-win32.x86.zip] | 04-12-2008 | zip |
+|0.1.3 | [Download|http://example.com/release.0.1.3/ex-win32-win32.x86.zip] | 03-11-2008 | zip |
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/test.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/test.confluence
new file mode 100644
index 0000000..3d5daea
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/test.confluence
@@ -0,0 +1,98 @@
+This the way that we would like to *translate* sites that are primarily
+authored in confluence while at the same time having the site be rendered in
+a _standard way on a static website_.
+
+----
+
+Here is a link to [JIRA|http://jira.codehaus.org]
+
+Here is a link with no text [http://jira.codehaus.org]
+
+This is some {{monospaced}} text.
+
+* item one
+** foo
+** bar
+* item two
+* item three
+
+Some more text
+
+# number one
+# number two
+# number three
+
+||one||two||three||
+|foo|bar|baz|
+
+h1. I am h1
+
+this is how you would code a mojo!
+
+{code}
+public class MyMojo
+    extends AbstractMojo
+{
+    /**
+     * @parameter expression="${plugin.artifacts}"
+     * @required
+     */
+    private List pluginArtifacts;
+
+    public void execute()
+        throws MojoExecutionException
+    {
+        ...
+        for ( Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
+        {
+            Artifact pluginArtifact = (Artifact) i.next();
+        }
+        ...
+    }
+}
+{code}
+
+h2. I am h2
+
+this is the way of the world
+
+{noformat}
+public class MyMojo
+    extends AbstractMojo
+{
+    /**
+     * @parameter expression="${plugin.artifacts}"
+     * @required
+     */
+    private List pluginArtifacts;
+
+    public void execute()
+        throws MojoExecutionException
+    {
+        ...
+        for ( Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
+        {
+            Artifact pluginArtifact = (Artifact) i.next();
+        }
+        ...
+    }
+}
+{noformat}
+
+h3. I am h3
+
+this is the way of the world
+
+h4. I am h4
+
+this is the way of the world
+
+h5. I am h5
+
+this is the way of the world
+
+h1. Answered Questions
+
+h3. What can I do to get the Maven love?
+
+Well, you just have to be calm and the maven love will come your way.
diff --git a/doxia-modules/doxia-module-confluence/src/test/resources/unknown-macro.confluence b/doxia-modules/doxia-module-confluence/src/test/resources/unknown-macro.confluence
new file mode 100644
index 0000000..3b84a0c
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/resources/unknown-macro.confluence
@@ -0,0 +1,5 @@
+{unknown:start}Simple paragraph.
+
+Simple paragraph{unknown:end}.
+
+Simple {unknown:middle} paragraph.
diff --git a/doxia-modules/doxia-module-confluence/src/test/site/confluence/faq.confluence b/doxia-modules/doxia-module-confluence/src/test/site/confluence/faq.confluence
new file mode 100644
index 0000000..4f426ef
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/site/confluence/faq.confluence
@@ -0,0 +1,228 @@
+*NOTE:* _This page contains drafts of user contributed FAQ entries. The content you see here might not be fully fool-proof or might not comply with the best practices promoted by Maven. What is only guaranteed is that they have worked once for some members. It is best to treat these items as "works in progress" until they have been reviewed and promoted to the main Maven documentation site._
+
+Users may also list questions here that they would like answered. So this page can serve as a collection of questions that users would like answered. Please put unanswered questions at the bottom. Please follow the format that is being used because it will help in our automated exaction of material which can then be incorporated into the main site.
+
+h1. Answered Questions
+
+h3. Why am I getting a "<plugin name> does not exist or no valid version" error? 
+
+This means that Maven is unable to access the required plugin from your local repository, and unable to access the official or 'central' Maven2 plugin repository. 
+
+You may troubleshoot the problem by performing the following actions: 
+# If you are behind a http proxy, please check the Maven2 [proxy settings guide|http://maven.apache.org/guides/mini/guide-proxies.html]. 
+# If the plugin you seek cannot be redistributed freely then you may [add it manually to your repository|http://maven.apache.org/guides/mini/guide-coping-with-sun-jars.html]. 
+
+If the problem still persists you may seek help from the Maven user list, browse [archived discussions|http://www.mail-archive.com/users@maven.apache.org/], or log a [ticket|http://jira.codehaus.org/browse/MNG] describing your problem if you think you have found a bug. Tickets could also be issued for feature enhancement requests, and other tasks.
+
+h3. How do I install a file in my local repository along with a generic Pom?
+
+This solution requires at least 2.0.1-SNAPSHOT or above version of Maven 2. You may do this by typing this command (please take note that this is a single line only).
+
+{code}
+mvn install:install-file 
+      -DgroupId=<group-id> 
+      -DartifactId=<artifact-id>
+      -Dversion=<version>
+      -Dfile=<path-to-file>
+      -Dpackaging=<packaging> (i.e. jar)
+      -DgeneratePom=true
+{code}
+
+This command installs the jar in your local repository with the generated generic pom.
+
+h3. How do I install a file in my local repository along with my customed Pom?
+
+The solution requires at least 2.0.1-SNAPSHOT or above version of Maven 2 and add the -DpomFile=<path-to-pom> parameter just like the sample below.
+
+{code}
+mvn install:install-file 
+      -DgroupId=<group-id> 
+      -DartifactId=<artifact-id>
+      -Dversion=<version>
+      -Dfile=<path-to-file>
+      -Dpackaging=<packaging> (i.e. jar)
+      -DpomFile=<path-to-pom>
+{code}
+
+This command will install the file in your local repository along with your customed pom.
+
+h3. Are there any ways of including/excluding the other modules in the navigation menu in the parent site?
+
+[http://jira.codehaus.org/browse/MNG-661], provides a simple patch which provides parent and module links using the project URLs which as you
+correctly point out only work when the site is deployed.
+
+h3. Where is the ______ plugin?
+
+If you cannot find a certain plugin, you may want to take a look on the following sites. 
+
+# http://mojo.codehaus.org  
+# https://svn.codehaus.org/mojo/trunk/mojo
+# https://svn.codehaus.org/mojo/trunk/mojo/mojo-sandbox
+
+h3. Why are there no pre/post goals in Maven 2.x?
+
+In Maven 1.x, pre/postGoals were used to inject custom behavior to the build process. This caused a problem, since declaring your code to be a preGoal of some other goal meant that you depended on that specific goal, rather than the work it did. It also caused confusion when trying to inject other behavior into a build that already had pre/postGoals attached: Where would the new behavior be injected?.
+
+Pre- and post-goals in Maven 1.x were usually used to develop the concept of a workflow, or lifecycle, for the build, where *x* happened before *y*, which had to happen before the sources could be compiled. In Maven 2, we've incorporated this concept of a build lifecycle natively. The steps - or phases - in this lifecycle correspond to the types of actions that might occur in a build. Your plugin can declare which type of action it performs - or which phase it should bind to - and that w [...]
+
+For more information, see:
+
+* [Introduction to the Build Lifecycle|http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html]
+
+h3. How does Maven compare with Ivy?
+
+For a comparison of Maven's features vs Ivy's you can refer to our [feature comparison|http://docs.codehaus.org/display/MAVEN/Feature+Comparisons]
+
+h3. How do I get the dependencies of a plug-in from within a Mojo?
+
+{code}
+public class MyMojo
+    extends AbstractMojo
+{
+    /**
+     * @parameter expression="${plugin.artifacts}"
+     * @required
+     */
+    private List pluginArtifacts;
+
+    public void execute()
+        throws MojoExecutionException
+    {
+        ...
+        for ( Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
+        {
+            Artifact pluginArtifact = (Artifact) i.next();
+        }
+        ...
+    }
+}
+{code}
+
+h3. How do I get the sources for a project within a Mojo?
+
+{code}
+public class MyMojo
+    extends AbstractMojo
+{
+    /**
+     * @parameter expression="${project.compileSourceRoots}"
+     * @required
+     */
+    private List sourceRoots;
+
+    public void execute()
+        throws MojoExecutionException
+    {
+        ...
+        for ( Iterator i = sourceRoots.iterator(); i.hasNext(); )
+        {
+            String sourceRoot = (String) i.next();
+
+           // Do what you want with these directories
+        }
+    }
+}
+{code}
+
+h3. How do I use SNAPSHOT versions of plug-ins?
+
+The POM is meant to be current state, not history. We actually only record the versions in the repository, and if the SCM tag is populated in the <scm> section of the POM for each released version you can reconstruct the information.
+
+There were other issues with putting history in the POM: forgetting to add it, getting the right level of information when you work on multiple branches, and similar things.
+
+h3. How do I create a report that does not require using Doxia's Sink interface?
+
+Make it a report and override the isExternalReport() method to return true. Then you don't need to use the Sink at all.
+
+h3. Is it possible to prevent warnings from a custom repository?
+
+Warnings from custom repositories (usually located within the organization's network, or even on the same workstation) are triggered when Maven tries to verify the integrity of the files in the repository. This verification is done via the SHA1 or MD5 sum of the file. If these sum files do not exist then a warning is shown.
+
+Support for downloading the the security sum files is not yet included in the Maven2 distribution. There are free command-line utilities on the Internet that generate these sums. Below is an example of a bash script (use [Cygwin|http://cygwin.com] if you are using a windows machine) that generates sha1sum for all jar, xml and pom files contained in the directory where it is executed:
+
+{code}
+#!/usr/bin/bash
+
+gensum(){
+   shaname=$1.sha1
+
+   sum=`sha1sum $1 | cut -f1 -d" "`
+   echo $sum > $shaname
+}
+
+processFile(){
+   while read oneline 
+   do
+      gensum $oneline
+
+   done < "$1"
+}
+
+tmpFile=$TMP/shagen.list
+
+echo "Generating sha1 sums for XML files"
+find . -name "*.xml" > "$tmpFile"
+
+processFile "$tmpFile"
+
+echo "Generating sha1 sums for POM files"
+find . -name "*.pom" > "$tmpFile"
+
+processFile "$tmpFile"
+
+echo "Generating sha1 sums for JAR files"
+find . -name "*.jar" > "$tmpFile"
+
+processFile "$tmpFile"
+
+rm "$tmpFile"
+{code}
+
+The script above has been tested on [Cygwin|http://cygwin.com] and is provided as is and with no guarantee.
+
+h3. Why am I getting the following error when I run the <plugin_name>: "ERROR: Cannot override read-only parameter: <parameter_name>"?
+
+This means that the parameter you are overriding in your pom.xml is readonly. Hence, it is not possible to override this parameter.
+
+h3. The sites of the plugins of Maven have a page with an overview of the goals and the parameters of the plugin. How is this generated?
+
+Include maven-plugin-plugin as a report.
+
+h3. how can I define the antrun plugin to be executed on demand like "mvn antrun:run"?
+
+This isn't actually possible with the plugin, unless perhaps you do this:
+- put everything in a top level configuration of the plugin
+- pass in a variable from the command line, eg -Dtarget=foo antrun:run
+- execute the appropriate target in the script based on the variable
+
+Ideally, you would write a plugin for these goals (Ant support for
+plugins will be available soon, currently you must write them in java
+or beanshell).
+
+h3. When I extend a mojo from another plugin, it's variables are not properly populated. Why?
+
+The field metadata when creating plugins is read from source files, so is not available when the original source is not. The metadata is available in the plugin in {{META-INF/maven/plugin.xml}}, but at this point there is no way to incorporate it when building a new plugin.
+
+We currently recommend building your plugins using composition instead of inheritence.
+
+h1. Unanswered Questions
+
+h3. Where can I find a listing of the available variable references that could be use during site generation?
+
+h3. How do I generate sources with the antrun plug-in?
+
+h3. Can I disable transitive dependencies?
+
+h3. How do I run a shell script from inside a Mojo?
+
+h3. Are resources inherited in the POM?
+
+h3. Why is there no <versions/> element in the v4.0.0 POMs?
+
+h3. How do I find the stale resources in a Mojo to avoid reprocessing them?
+
+h3. Will the release plug-in resolve all my SNAPSHOT dependencies?
+
+h3. How do I integrate static (x)html into my Maven site?
+
+h3. Where did the properties files go for plug-in configuration in Maven 1.x?
diff --git a/doxia-modules/doxia-module-confluence/src/test/site/confluence/page.confluence b/doxia-modules/doxia-module-confluence/src/test/site/confluence/page.confluence
new file mode 100644
index 0000000..3d5daea
--- /dev/null
+++ b/doxia-modules/doxia-module-confluence/src/test/site/confluence/page.confluence
@@ -0,0 +1,98 @@
+This the way that we would like to *translate* sites that are primarily
+authored in confluence while at the same time having the site be rendered in
+a _standard way on a static website_.
+
+----
+
+Here is a link to [JIRA|http://jira.codehaus.org]
+
+Here is a link with no text [http://jira.codehaus.org]
+
+This is some {{monospaced}} text.
+
+* item one
+** foo
+** bar
+* item two
+* item three
+
+Some more text
+
+# number one
+# number two
+# number three
+
+||one||two||three||
+|foo|bar|baz|
+
+h1. I am h1
+
+this is how you would code a mojo!
+
+{code}
+public class MyMojo
+    extends AbstractMojo
+{
+    /**
+     * @parameter expression="${plugin.artifacts}"
+     * @required
+     */
+    private List pluginArtifacts;
+
+    public void execute()
+        throws MojoExecutionException
+    {
+        ...
+        for ( Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
+        {
+            Artifact pluginArtifact = (Artifact) i.next();
+        }
+        ...
+    }
+}
+{code}
+
+h2. I am h2
+
+this is the way of the world
+
+{noformat}
+public class MyMojo
+    extends AbstractMojo
+{
+    /**
+     * @parameter expression="${plugin.artifacts}"
+     * @required
+     */
+    private List pluginArtifacts;
+
+    public void execute()
+        throws MojoExecutionException
+    {
+        ...
+        for ( Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
+        {
+            Artifact pluginArtifact = (Artifact) i.next();
+        }
+        ...
+    }
+}
+{noformat}
+
+h3. I am h3
+
+this is the way of the world
+
+h4. I am h4
+
+this is the way of the world
+
+h5. I am h5
+
+this is the way of the world
+
+h1. Answered Questions
+
+h3. What can I do to get the Maven love?
+
+Well, you just have to be calm and the maven love will come your way.
diff --git a/doxia-modules/doxia-module-docbook-simple/pom.xml b/doxia-modules/doxia-module-docbook-simple/pom.xml
new file mode 100644
index 0000000..64f671a
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-docbook-simple</artifactId>
+
+  <name>Doxia :: Simplified DocBook Module</name>
+  <description>A Doxia module for Simplified DocBook source documents.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-maven-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/main/components/components.xml</descriptor>
+            <descriptor>target/generated-resources/plexus/META-INF/plexus/components.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>merge-descriptors</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/doxia-modules/doxia-module-docbook-simple/src/main/components/components.xml b/doxia-modules/doxia-module-docbook-simple/src/main/components/components.xml
new file mode 100644
index 0000000..73b2eee
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/main/components/components.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<component-set>
+  <!-- For backward compatibility -->
+  <components>
+    <component>
+      <role>org.apache.maven.doxia.parser.Parser</role>
+      <role-hint>doc-book</role-hint>
+      <implementation>org.apache.maven.doxia.module.docbook.DocBookParser</implementation>
+      <description>Parse a <code>Docbook</code> document and emit events into the specified doxia
+        Sink.</description>
+    </component>
+    <component>
+      <role>org.apache.maven.doxia.sink.SinkFactory</role>
+      <role-hint>doc-book</role-hint>
+      <implementation>org.apache.maven.doxia.module.docbook.DocbookSinkFactory</implementation>
+      <description>Docbook implementation of the Sink factory.</description>
+    </component>
+    <component>
+      <role>org.apache.maven.doxia.module.site.SiteModule</role>
+      <role-hint>doc-book</role-hint>
+      <implementation>org.apache.maven.doxia.module.docbook.DocBookSiteModule</implementation>
+      <description></description>
+    </component>
+  </components>
+</component-set>
diff --git a/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookParser.java b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookParser.java
new file mode 100644
index 0000000..6938546
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookParser.java
@@ -0,0 +1,1096 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Stack;
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.markup.HtmlMarkup;
+import org.apache.maven.doxia.parser.AbstractXmlParser;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Parse a <a href="http://www.docbook.org/schemas/simplified"><code>Simplified DocBook</code></a> document
+ * and emit events into the specified doxia Sink.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: DocBookParser.java 807168 2009-08-24 12:04:46Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="docbook"
+ */
+public class DocBookParser
+    extends AbstractXmlParser
+    implements DocbookMarkup, SimplifiedDocbookMarkup
+{
+    /**
+     * Level counter for calculating the section level.
+     */
+    private int level;
+
+    /**
+     * Used to distinguish italic from bold.
+     */
+    private boolean isBold;
+
+    private boolean inHead;
+
+    private boolean ignore;
+
+    private boolean simpleTag;
+
+    private char trademark;
+
+    /**
+     * A selective stack of parent elements
+     */
+    private final Stack parent = new Stack();
+
+    /**
+     * The list of DocBook elements that introduce a new level of hierarchy.
+     */
+    private static final Collection HIER_ELEMENTS = new HashSet();
+
+    /**
+     * Simplified DocBook elements that are direct children of <article>
+     * and that should be emitted into the Sink's head.
+     */
+    private static final Collection META_ELEMENTS = new HashSet();
+
+    /**
+     * Simplified DocBook elements that occur within <articleinfo>
+     * and that are currently recognized by the parser.
+     */
+    private static final Collection ARTICLEINFO_ELEMENTS = new HashSet();
+
+    /**
+     * The list of DocBook elements that will be rendered verbatim
+     */
+    private static final Collection VERBATIM_ELEMENTS = new HashSet();
+
+    /**
+     * The list of DocBook elements that will be rendered inline and bold
+     */
+    private static final Collection BOLD_ELEMENTS = new HashSet();
+
+    /**
+     * The list of DocBook elements that will be rendered inline and italic
+     */
+    private static final Collection ITALIC_ELEMENTS = new HashSet();
+
+    /**
+     * The list of DocBook elements that will be rendered inline and monospace
+     */
+    private static final Collection MONOSPACE_ELEMENTS = new HashSet();
+
+    /**
+     * The list of DocBook elements that may be ignored, either because they don't
+     * require any special processing or because they are not yet implemented.
+     */
+    private static final Collection IGNORABLE_ELEMENTS = new HashSet();
+    static
+    {
+        META_ELEMENTS.add( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() );
+        META_ELEMENTS.add( SimplifiedDocbookMarkup.AUTHORBLURB_TAG.toString() );
+        META_ELEMENTS.add( SimplifiedDocbookMarkup.SUBTITLE_TAG.toString() );
+        META_ELEMENTS.add( SimplifiedDocbookMarkup.TITLE_TAG.toString() );
+        META_ELEMENTS.add( SimplifiedDocbookMarkup.TITLEABBREV_TAG.toString() );
+
+        ARTICLEINFO_ELEMENTS.add( SimplifiedDocbookMarkup.TITLE_TAG.toString() );
+        ARTICLEINFO_ELEMENTS.add( SimplifiedDocbookMarkup.CORPAUTHOR_TAG.toString() );
+        ARTICLEINFO_ELEMENTS.add( SimplifiedDocbookMarkup.DATE_TAG.toString() );
+
+        HIER_ELEMENTS.add( SimplifiedDocbookMarkup.SECTION_TAG.toString() );
+        HIER_ELEMENTS.add( SimplifiedDocbookMarkup.APPENDIX_TAG.toString() );
+        HIER_ELEMENTS.add( SimplifiedDocbookMarkup.BIBLIOGRAPHY_TAG.toString() );
+        HIER_ELEMENTS.add( SimplifiedDocbookMarkup.BIBLIODIV_TAG.toString() );
+
+        VERBATIM_ELEMENTS.add( SimplifiedDocbookMarkup.PROGRAMLISTING_TAG.toString() );
+        VERBATIM_ELEMENTS.add( SimplifiedDocbookMarkup.LITERALLAYOUT_TAG.toString() );
+
+        BOLD_ELEMENTS.add( SimplifiedDocbookMarkup.COMMAND_TAG.toString() );
+        BOLD_ELEMENTS.add( SimplifiedDocbookMarkup.USERINPUT_TAG.toString() );
+
+        ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.REPLACEABLE_TAG.toString() );
+        ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.SYSTEMITEM_TAG.toString() );
+        ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.CITETITLE_TAG.toString() );
+        ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.EMPHASIS_TAG.toString() );
+        ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.ATTRIBUTION_TAG.toString() );
+        ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.LINEANNOTATION_TAG.toString() );
+
+        MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.COMPUTEROUTPUT_TAG.toString() );
+        MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.REPLACEABLE_TAG.toString() );
+        MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.LITERAL_TAG.toString() );
+        MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.OPTION_TAG.toString() );
+        MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.SYSTEMITEM_TAG.toString() );
+        MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.USERINPUT_TAG.toString() );
+        MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.FILENAME_TAG.toString() );
+
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.ABBREV_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.ABSTRACT_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.BIBLIOMIXED_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.BIBLIOMSET_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.COLSPEC_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.EPIGRAPH_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.EXAMPLE_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.FOOTNOTEREF_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.INLINEMEDIAOBJECT_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.ISSUENUM_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.PHRASE_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.PUBDATE_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.PUBLISHERNAME_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.SPANSPEC_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.TEXTOBJECT_TAG.toString() );
+        IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.VOLUMENUM_TAG.toString() );
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.parent.clear();
+        this.trademark = 0;
+        this.level = 0;
+        this.isBold = false;
+        this.inHead = false;
+        this.ignore = false;
+        this.simpleTag = false;
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    protected void handleStartTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( inHead && !META_ELEMENTS.contains( parser.getName() )
+                && isParent( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
+        {
+            sink.head_();
+            inHead = false;
+
+            // assume any element that is not meta starts the body
+            sink.body();
+        }
+
+        final SinkEventAttributeSet attribs = getAttributesFromParser( parser );
+        simpleTag = parser.isEmptyElementTag();
+
+        if ( parser.getName().equals( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
+        {
+            handleArticleStart( sink, attribs );
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() ) )
+        {
+            handleArticleInfoStartTags( parser.getName(), sink, attribs );
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() ) )
+        {
+            parent.push( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() );
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.FOOTNOTE_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.SECTIONINFO_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.VIDEOOBJECT_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.AUDIOOBJECT_TAG.toString() ) )
+        {
+            parent.push( parser.getName() );
+            ignore = true;
+        }
+        else if ( isParent( ( SimplifiedDocbookMarkup.FOOTNOTE_TAG.toString() ) )
+                || isParent( SimplifiedDocbookMarkup.AUDIOOBJECT_TAG.toString() )
+                || isParent( SimplifiedDocbookMarkup.VIDEOOBJECT_TAG.toString() )
+                || isParent( SimplifiedDocbookMarkup.SECTIONINFO_TAG.toString() )
+                || isParent( SimplifiedDocbookMarkup.ENTRYTBL_TAG.toString() ) )
+        {
+            return; // TODO: implement footnotes, entrytbl
+        }
+        else if ( HIER_ELEMENTS.contains( parser.getName() ) )
+        {
+            handleSectionElements( sink, parser.getName(), attribs );
+        }
+        else if ( listStartTags ( parser.getName(), sink, attribs ) )
+        {
+            return;
+        }
+        else if ( mediaStartTag( parser.getName(), sink, attribs ) )
+        {
+            return;
+        }
+        else if ( tableStartTags( parser.getName(), sink, attribs ) )
+        {
+            return;
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.PARA_TAG.toString() ) )
+        {
+            handleParaStart( sink, attribs );
+        }
+        else if ( styleStartTags( parser.getName(), sink, attribs ) )
+        {
+            return;
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.TITLE_TAG.toString() ) )
+        {
+            handleTitleStart( sink, attribs );
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.EMAIL_TAG.toString() ) )
+        {
+            handleEmailStart( parser, sink, attribs );
+        }
+        else if ( linkStartTag( parser.getName(), sink, attribs ) )
+        {
+            return;
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.QUOTE_TAG.toString() ) )
+        {
+            sink.text( "\"", null );
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.TRADEMARK_TAG.toString() ) )
+        {
+            trademark = '\u2122';
+            final Object trade = attribs.getAttribute( "class" );
+
+            if ( trade != null )
+            {
+                trademark = DocbookUtils.trademarkFromClass( trade.toString() );
+            }
+        }
+        else
+        {
+            if ( !ignorable( parser.getName() ) )
+            {
+                if ( simpleTag )
+                {
+                    handleUnknown( parser, sink, HtmlMarkup.TAG_TYPE_SIMPLE );
+                }
+                else
+                {
+                    handleUnknown( parser, sink, HtmlMarkup.TAG_TYPE_START );
+                }
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleEndTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( parser.getName().equals( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
+        {
+            sink.body_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() ) )
+        {
+            parent.pop();
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() ) )
+        {
+             handleArticleInfoEndTags( parser.getName(), sink );
+        }
+        else if ( HIER_ELEMENTS.contains( parser.getName() ) )
+        {
+            sink.section_( level );
+
+            //decrease the nesting level
+            level--;
+            parent.pop();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.FOOTNOTE_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.AUDIOOBJECT_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.VIDEOOBJECT_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.SECTIONINFO_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.ENTRYTBL_TAG.toString() ) )
+        {
+            parent.pop();
+            ignore = false;
+        }
+        else if ( isParent( ( SimplifiedDocbookMarkup.FOOTNOTE_TAG.toString() ) )
+                || isParent( SimplifiedDocbookMarkup.AUDIOOBJECT_TAG.toString() )
+                || isParent( SimplifiedDocbookMarkup.VIDEOOBJECT_TAG.toString() )
+                || isParent( SimplifiedDocbookMarkup.SECTIONINFO_TAG.toString() )
+                || isParent( SimplifiedDocbookMarkup.ENTRYTBL_TAG.toString() ) )
+        {
+            return;
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG.toString() ) )
+        {
+            sink.list_();
+            parent.pop();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() ) )
+        {
+            sink.numberedList_();
+            parent.pop();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() ) )
+        {
+            parent.pop();
+
+            if ( isParent( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() ) )
+            {
+                sink.definition_();
+            }
+            else if ( isParent( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() ) )
+            {
+                sink.numberedListItem_();
+            }
+            else
+            {
+                sink.listItem_();
+            }
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() ) )
+        {
+            sink.definitionList_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.VARLISTENTRY_TAG.toString() ) )
+        {
+            sink.definitionListItem_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.TERM_TAG.toString() ) )
+        {
+            sink.definedTerm_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() ) )
+        {
+            sink.figure_();
+            parent.pop();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.FIGURE_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.THEAD_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.TFOOT_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.TBODY_TAG.toString() ) )
+        {
+            parent.pop();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.CAPTION_TAG.toString() ) )
+        {
+            handleCaptionEnd(sink);
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.TABLE_TAG.toString() )
+            || parser.getName().equals( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() ) )
+        {
+            sink.table_();
+
+            parent.pop();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.TR_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.ROW_TAG.toString() ) )
+        {
+            sink.tableRow_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.TGROUP_TAG.toString() ) )
+        {
+            sink.tableRows_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.ENTRY_TAG.toString() )
+                && isParent( SimplifiedDocbookMarkup.THEAD_TAG.toString() )
+            || parser.getName().equals( TH_TAG.toString() ) )
+        {
+            sink.tableHeaderCell_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.ENTRY_TAG.toString() ) )
+        {
+            sink.tableCell_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.PARA_TAG.toString() ) )
+        {
+            handleParaEnd( sink );
+        }
+        else if ( VERBATIM_ELEMENTS.contains( parser.getName() ) )
+        {
+            sink.verbatim_();
+        }
+        else if ( BOLD_ELEMENTS.contains( parser.getName() )
+            && MONOSPACE_ELEMENTS.contains( parser.getName() ) )
+        {
+            sink.monospaced_();
+            sink.bold_();
+        }
+        else if ( ITALIC_ELEMENTS.contains( parser.getName() )
+            && MONOSPACE_ELEMENTS.contains( parser.getName() ) )
+        {
+            sink.monospaced_();
+            sink.italic_();
+        }
+        else if ( BOLD_ELEMENTS.contains( parser.getName() ) )
+        {
+            sink.bold_();
+        }
+        else if ( ITALIC_ELEMENTS.contains( parser.getName() ) )
+        {
+            if ( isBold )
+            {
+                sink.bold_();
+
+                isBold = false;
+            }
+            else
+            {
+                sink.italic_();
+            }
+        }
+        else if ( MONOSPACE_ELEMENTS.contains( parser.getName() ) )
+        {
+            sink.monospaced_();
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.TITLE_TAG.toString() ) )
+        {
+            handleTitleEnd( sink );
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.ULINK_TAG.toString() )
+                || parser.getName().equals( SimplifiedDocbookMarkup.LINK_TAG.toString() ) )
+        {
+            if ( isParent( parser.getName() ) )
+            {
+                parent.pop();
+                sink.link_();
+            }
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.QUOTE_TAG.toString() ) )
+        {
+            sink.text( "\"", null );
+        }
+        else if ( parser.getName().equals( SimplifiedDocbookMarkup.TRADEMARK_TAG.toString() ) )
+        {
+            sink.text( Character.toString( trademark ), null );
+        }
+        else if ( !simpleTag && !ignorable( parser.getName() ) )
+        {
+            handleUnknown( parser, sink, HtmlMarkup.TAG_TYPE_END );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleComment( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        final String text = parser.getText();
+
+        if ( "PB".equals( text.trim() ) )
+        {
+            sink.pageBreak();
+        }
+        else if ( "HR".equals( text.trim() ) )
+        {
+            sink.horizontalRule();
+        }
+        else if ( "LB".equals( text.trim() ) )
+        {
+            sink.lineBreak();
+        }
+        else if ( "anchor_end".equals( text.trim() ) )
+        {
+            sink.anchor_();
+        }
+        else
+        {
+            sink.comment( text.trim() );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleCdsect( XmlPullParser parser, Sink sink )
+            throws XmlPullParserException
+    {
+        if ( !ignore )
+        {
+            super.handleCdsect( parser, sink );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleEntity( XmlPullParser parser, Sink sink )
+            throws XmlPullParserException
+    {
+        if ( !ignore )
+        {
+            super.handleEntity( parser, sink );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleText( XmlPullParser parser, Sink sink )
+            throws XmlPullParserException
+    {
+        if ( !ignore )
+        {
+            super.handleText( parser, sink );
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    private void handleArticleInfoStartTags( String name, Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( !ARTICLEINFO_ELEMENTS.contains( name ) )
+        {
+            ignore = true;
+            return; // TODO: other meta data are ignored, implement!
+        }
+
+        if ( name.equals( SimplifiedDocbookMarkup.TITLE_TAG.toString() ) )
+        {
+            sink.title( attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.CORPAUTHOR_TAG.toString() ) )
+        {
+            sink.author( attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.DATE_TAG.toString() ) )
+        {
+            sink.date( attribs );
+        }
+    }
+
+    private void handleArticleInfoEndTags( String name, Sink sink )
+    {
+        if ( !ARTICLEINFO_ELEMENTS.contains( name ) )
+        {
+            ignore = false;
+            return; // TODO: other meta data are ignored, implement!
+        }
+
+        if ( name.equals( SimplifiedDocbookMarkup.TITLE_TAG.toString() ) )
+        {
+            sink.title_();
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.CORPAUTHOR_TAG.toString() ) )
+        {
+            sink.author_();
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.DATE_TAG.toString() ) )
+        {
+            sink.date_();
+        }
+    }
+
+    private void handleCaptionStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( isParent( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() ) )
+        {
+            sink.figureCaption( attribs );
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() )
+            || isParent( SimplifiedDocbookMarkup.TABLE_TAG.toString() ) )
+        {
+            sink.tableCaption( attribs );
+        }
+
+        parent.push( SimplifiedDocbookMarkup.CAPTION_TAG.toString() );
+    }
+
+    private void handleCaptionEnd( Sink sink )
+    {
+        parent.pop();
+
+        if ( isParent( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() ) )
+        {
+            sink.figureCaption_();
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() )
+            || isParent( SimplifiedDocbookMarkup.TABLE_TAG.toString() ) )
+        {
+            sink.tableCaption_();
+        }
+    }
+
+    private void handleEmailStart( XmlPullParser parser, Sink sink, SinkEventAttributeSet attribs )
+            throws XmlPullParserException
+    {
+        try
+        {
+            final String mailto = parser.nextText();
+            sink.link( "mailto:" + mailto, attribs );
+            sink.monospaced();
+            sink.text( mailto, null );
+            sink.monospaced_();
+            sink.link_();
+        }
+        catch ( IOException e )
+        {
+            throw new XmlPullParserException( "IOException: " + e.getMessage(), parser, e );
+        }
+    }
+
+    private void handleFigureStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        sink.figure( attribs );
+        parent.push( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() );
+    }
+
+    private void handleArticleStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        sink.head( attribs );
+        inHead = true;
+
+        parent.push( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() );
+    }
+
+    //If the element introduces a new level of hierarchy, raise the stack
+    private void handleSectionElements( Sink sink, String name, SinkEventAttributeSet attribs )
+    {
+        //increase the nesting level
+        level++;
+
+        sink.section( level, attribs );
+
+        parent.push( name );
+    }
+
+    private void handleAnchorStart( Sink sink, SinkEventAttributeSet attribs  )
+    {
+        final Object id = attribs.getAttribute( SimplifiedDocbookMarkup.ID_ATTRIBUTE );
+
+        if ( id != null )
+        {
+            sink.anchor( id.toString(), attribs );
+        }
+    }
+
+    private void handleImageDataStart( Sink sink, SinkEventAttributeSet attribs )
+            throws XmlPullParserException
+    {
+        final Object fileref = attribs.getAttribute( SimplifiedDocbookMarkup.FILEREF_ATTRIBUTE );
+
+        if ( fileref == null )
+        {
+            throw new XmlPullParserException( "Missing fileref attribute in imagedata!" );
+        }
+
+        sink.figureGraphics( fileref.toString(), attribs );
+    }
+
+    private void handleItemizedListStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        sink.list( attribs );
+        //for itemizedlists in variablelists
+        parent.push( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG.toString() );
+    }
+
+    private void handleLinkStart( Sink sink, SinkEventAttributeSet attribs )
+            throws XmlPullParserException
+    {
+        final Object linkend = attribs.getAttribute( SimplifiedDocbookMarkup.LINKEND_ATTRIBUTE );
+
+        if ( linkend == null )
+        {
+            throw new XmlPullParserException( "Missing linkend attribute in link!" );
+        }
+
+        parent.push( SimplifiedDocbookMarkup.LINK_TAG.toString() );
+        sink.link( "#" + linkend.toString(), attribs );
+    }
+
+    private void handleListItemStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( isParent( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() ) )
+        {
+            sink.definition( attribs );
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() ) )
+        {
+            sink.numberedListItem( attribs );
+        }
+        else
+        {
+            sink.listItem( attribs );
+        }
+
+        parent.push( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() );
+    }
+
+    private void handleOrderedListStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        //default enumeration style is decimal
+        int numeration = Sink.NUMBERING_DECIMAL;
+
+        final Object num = attribs.getAttribute( SimplifiedDocbookMarkup.NUMERATION_ATTRIBUTE );
+
+        if ( num != null )
+        {
+            numeration = DocbookUtils.doxiaListNumbering( num.toString() );
+        }
+
+        sink.numberedList( numeration, attribs );
+        parent.push( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() );
+    }
+
+    private void handleParaEnd( Sink sink )
+    {
+        if ( !isParent( SimplifiedDocbookMarkup.CAPTION_TAG.toString() )
+                && ! isParent( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() ) )
+        {
+            sink.paragraph_();
+        }
+    }
+
+    private void handleParaStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( !isParent( SimplifiedDocbookMarkup.CAPTION_TAG.toString() )
+                && ! isParent( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() ) )
+        {
+            sink.paragraph( attribs );
+        }
+    }
+
+    private void handleTableStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        final Object frame = attribs.getAttribute( SimplifiedDocbookMarkup.FRAME_ATTRIBUTE );
+        if ( frame != null )
+        {
+            attribs.addAttribute( SimplifiedDocbookMarkup.FRAME_ATTRIBUTE,
+                    DocbookUtils.doxiaTableFrameAttribute( frame.toString() ) );
+        }
+
+        sink.table( attribs );
+
+        parent.push( SimplifiedDocbookMarkup.TABLE_TAG.toString() );
+    }
+
+    private void handleTitleStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( isParent( SimplifiedDocbookMarkup.TABLE_TAG.toString() )
+                || isParent( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() ) )
+        {
+            sink.tableCaption( attribs );
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
+        {
+            sink.title( attribs );
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.SECTION_TAG.toString() ) )
+        {
+            sink.sectionTitle( level, attribs );
+        }
+        else
+        {
+            sink.bold();
+        }
+    }
+
+    private void handleTitleEnd( Sink sink )
+    {
+        if ( isParent( SimplifiedDocbookMarkup.TABLE_TAG.toString() )
+                || isParent( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() ) )
+        {
+            sink.tableCaption_();
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.SECTION_TAG.toString() ) )
+        {
+            sink.sectionTitle_( level );
+        }
+        else if ( isParent( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
+        {
+            sink.title_();
+        }
+        else
+        {
+            sink.bold_();
+        }
+    }
+
+    private void handleUlinkStart( Sink sink, SinkEventAttributeSet attribs )
+            throws XmlPullParserException
+    {
+        final Object url = attribs.getAttribute( SimplifiedDocbookMarkup.URL_ATTRIBUTE );
+
+        if ( url == null )
+        {
+            throw new XmlPullParserException( "Missing url attribute in ulink!" );
+        }
+
+        parent.push( SimplifiedDocbookMarkup.ULINK_TAG.toString() );
+        sink.link( url.toString(), attribs );
+    }
+
+    private void handleVariableListStart( Sink sink, SinkEventAttributeSet attribs )
+    {
+        sink.definitionList( attribs );
+        parent.push( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() );
+    }
+
+    private void handleXrefStart( Sink sink, SinkEventAttributeSet attribs )
+            throws XmlPullParserException
+    {
+        final Object linkend = attribs.getAttribute( SimplifiedDocbookMarkup.LINKEND_ATTRIBUTE );
+
+        if ( linkend == null )
+        {
+            throw new XmlPullParserException( "Missing linkend attribute in xref!" );
+        }
+
+        sink.link( "#" + linkend.toString(), attribs );
+        sink.text( "Link" ); //TODO: determine text of link target
+        sink.link_();
+    }
+
+    private boolean ignorable( String name )
+    {
+        return IGNORABLE_ELEMENTS.contains( name );
+    }
+
+    /**
+     * Determines if the given element is a parent element.
+     *
+     * @param element the element to determine.
+     * @return true if the given element is a parent element.
+     */
+    private boolean isParent( String element )
+    {
+        if ( parent.size() > 0 )
+        {
+            return parent.peek().equals( element );
+        }
+
+        return false;
+    }
+
+    private boolean linkStartTag( String name, Sink sink, SinkEventAttributeSet attribs )
+            throws XmlPullParserException
+    {
+        if ( name.equals( SimplifiedDocbookMarkup.ULINK_TAG.toString() ) )
+        {
+            handleUlinkStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.LINK_TAG.toString() ) )
+        {
+            handleLinkStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.XREF_TAG.toString() ) )
+        {
+            handleXrefStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.ANCHOR_TAG.toString() ) )
+        {
+            handleAnchorStart( sink, attribs );
+        }
+        else
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean listStartTags( String name, Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( name.equals( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG.toString() ) )
+        {
+            handleItemizedListStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() ) )
+        {
+            handleOrderedListStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() ) )
+        {
+            handleListItemStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() ) )
+        {
+            handleVariableListStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.VARLISTENTRY_TAG.toString() ) )
+        {
+            sink.definitionListItem( attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.TERM_TAG.toString() ) )
+        {
+            sink.definedTerm( attribs );
+        }
+        else
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean mediaStartTag( String name, Sink sink, SinkEventAttributeSet attribs )
+            throws XmlPullParserException
+    {
+        if ( name.equals( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() ) )
+        {
+            handleFigureStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG.toString() )
+                || name.equals( SimplifiedDocbookMarkup.FIGURE_TAG.toString() ) )
+        {
+            parent.push( name );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.IMAGEDATA_TAG.toString() ) )
+        {
+            handleImageDataStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.CAPTION_TAG.toString() ) )
+        {
+            handleCaptionStart( sink, attribs );
+        }
+        else
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean styleStartTags( String name, Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( VERBATIM_ELEMENTS.contains( name ) )
+        {
+            sink.verbatim( SinkEventAttributeSet.BOXED );
+        }
+        else if ( BOLD_ELEMENTS.contains( name ) && MONOSPACE_ELEMENTS.contains( name ) )
+        {
+            sink.bold();
+            sink.monospaced();
+        }
+        else if ( ITALIC_ELEMENTS.contains( name ) && MONOSPACE_ELEMENTS.contains( name ) )
+        {
+            sink.italic();
+            sink.monospaced();
+        }
+        else if ( BOLD_ELEMENTS.contains( name ) )
+        {
+            sink.bold();
+        }
+        else if ( ITALIC_ELEMENTS.contains( name ) && "bold".equals( attribs.getAttribute( "role" ) ) )
+        {
+            sink.bold();
+            isBold = true;
+        }
+        else if ( ITALIC_ELEMENTS.contains( name ) )
+        {
+            sink.italic();
+        }
+        else if ( MONOSPACE_ELEMENTS.contains( name ) )
+        {
+            sink.monospaced();
+        }
+        else
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean tableStartTags( String name, Sink sink, SinkEventAttributeSet attribs )
+    {
+        if ( name.equals( SimplifiedDocbookMarkup.ENTRYTBL_TAG.toString() ) )
+        {
+            parent.push( name );
+            ignore = true;
+            // insert empty table cell instead
+            sink.tableCell( (SinkEventAttributeSet) null );
+            sink.tableCell_();
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.TABLE_TAG.toString() )
+            || name.equals( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() ) )
+        {
+            handleTableStart( sink, attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.THEAD_TAG.toString() )
+                || name.equals( SimplifiedDocbookMarkup.TFOOT_TAG.toString() )
+                || name.equals( SimplifiedDocbookMarkup.TBODY_TAG.toString() ) )
+        {
+            parent.push( name );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.TGROUP_TAG.toString() ) )
+        {
+            // this is required by the DTD
+            final int cols = Integer.parseInt( (String) attribs.getAttribute( "cols" ) );
+            int[] justification = new int[cols];
+            int justif = Sink.JUSTIFY_LEFT;
+
+            final Object align = attribs.getAttribute( SinkEventAttributeSet.ALIGN );
+
+            if ( align != null )
+            {
+                final String al = align.toString();
+
+                if ( "right".equals( al ) )
+                {
+                    justif = Sink.JUSTIFY_RIGHT;
+                }
+                else if ( "center".equals( al ) )
+                {
+                    justif = Sink.JUSTIFY_CENTER;
+                }
+            }
+
+            for ( int i = 0; i < justification.length; i++ )
+            {
+                justification[i] = justif;
+            }
+
+            boolean grid = false;
+            final Object rowsep = attribs.getAttribute( "rowsep" );
+
+            if ( rowsep != null && Integer.parseInt( (String) rowsep ) == 1 )
+            {
+                grid = true;
+            }
+
+            final Object colsep = attribs.getAttribute( "colsep" );
+
+            if ( colsep != null && Integer.parseInt( (String) colsep ) == 1 )
+            {
+                grid = true;
+            }
+
+            sink.tableRows( justification, grid );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.TR_TAG.toString() )
+                || name.equals( SimplifiedDocbookMarkup.ROW_TAG.toString() ) )
+        {
+            sink.tableRow( attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.ENTRY_TAG.toString() )
+                && isParent( SimplifiedDocbookMarkup.THEAD_TAG.toString() )
+                || name.equals( SimplifiedDocbookMarkup.TH_TAG.toString() ) )
+        {
+            sink.tableHeaderCell( attribs );
+        }
+        else if ( name.equals( SimplifiedDocbookMarkup.ENTRY_TAG.toString() ) )
+        {
+            sink.tableCell( attribs );
+        }
+        else
+        {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookSink.java b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookSink.java
new file mode 100644
index 0000000..585ca90
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookSink.java
@@ -0,0 +1,1725 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+import org.apache.maven.doxia.sink.AbstractXmlSink;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * <a href="http://www.oasis-open.org/docbook">Docbook</a> Sink implementation.
+ * <br/>
+ * It uses the Docbook v4.4 DTD <a href="http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd">
+ * http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd</a>.
+ *
+ * @version $Id: DocBookSink.java 807168 2009-08-24 12:04:46Z vsiveton $
+ * @since 1.0
+ */
+public class DocBookSink
+    extends AbstractXmlSink
+    implements DocbookMarkup, SimplifiedDocbookMarkup
+{
+    /** DocBook V4.4 SGML public id: "-//OASIS//DTD DocBook V4.4//EN"
+     * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_SGML_PUBLIC_ID} instead of. */
+    public static final String DEFAULT_SGML_PUBLIC_ID = DocbookMarkup.DEFAULT_SGML_PUBLIC_ID;
+
+    /** DocBook XML V4.4 XML public id: "-//OASIS//DTD DocBook XML V4.4//EN"
+     * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_XML_PUBLIC_ID} instead of. */
+    public static final String DEFAULT_XML_PUBLIC_ID = DocbookMarkup.DEFAULT_XML_PUBLIC_ID;
+
+    /** DocBook XML V4.4 XML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
+     * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_XML_SYSTEM_ID} instead of. */
+    public static final String DEFAULT_XML_SYSTEM_ID = DocbookMarkup.DEFAULT_XML_SYSTEM_ID;
+
+    /** DocBook XML V4.4 SGML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
+     * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_SGML_SYSTEM_ID} instead of. */
+    public static final String DEFAULT_SGML_SYSTEM_ID = DocbookMarkup.DEFAULT_SGML_SYSTEM_ID;
+
+    /** The output writer. */
+    private PrintWriter out;
+
+    /** xmlMode. */
+    private boolean xmlMode = false;
+
+    /** styleSheet. */
+    private String styleSheet = null;
+
+    /** language. */
+    private String lang = null;
+
+    /** publicId. */
+    private String publicId = null;
+
+    /** systemId. */
+    private String systemId = null;
+
+    /** italicBegin. */
+    private String italicBeginTag;
+
+    /** italicEnd. */
+    private String italicEndTag;
+
+    /** boldBegin. */
+    private String boldBeginTag;
+
+    /** boldEnd. */
+    private String boldEndTag;
+
+    /** monospacedBegin. */
+    private String monospacedBeginTag;
+
+    /** monospacedEnd. */
+    private String monospacedEndTag;
+
+    /** horizontalRule. */
+    private String horizontalRuleElement;
+
+    /** pageBreak. */
+    private String pageBreakElement;
+
+    /** lineBreak. */
+    private String lineBreakElement;
+
+    /** An image source file. */
+    private String graphicsFileName;
+
+    /** hasTitle. */
+    private boolean hasTitle;
+
+    /** authorDate. */
+    private boolean authorDateFlag;
+
+    /** verbatim. */
+    private boolean verbatimFlag;
+
+    /** externalLink. */
+    private boolean externalLinkFlag;
+
+    /** tableHasCaption. */
+    private boolean tableHasCaption;
+
+    /** Used for table rows. */
+    private PrintWriter savedOut;
+
+    /** tableRows. */
+    private String tableRows;
+
+    /** tableRows writer. */
+    private StringWriter tableRowsWriter;
+
+    /** tableHasGrid. */
+    private boolean tableHasGrid;
+
+    private boolean skip;
+
+    private boolean paragraph;
+
+    private String encoding;
+
+    /** Map of warn messages with a String as key to describe the error type and a Set as value.
+     * Using to reduce warn messages. */
+    private Map warnMessages;
+
+    /**
+     * Constructor, initialize the Writer.
+     *
+     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newXmlWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     */
+    protected DocBookSink( Writer writer )
+    {
+        this( writer, null );
+    }
+
+    /**
+     * Constructor, initialize the Writer and tells which encoding is used.
+     *
+     * @param writer not null writer to write the result.
+     * @param encoding the encoding used, that should be written to the generated HTML content
+     * if not <code>null</code>.
+     */
+    protected DocBookSink( Writer writer, String encoding )
+    {
+        this.out = new PrintWriter( writer );
+        this.encoding = encoding;
+
+        setItalicElement( "<emphasis>" );
+        setBoldElement( "<emphasis role=\"bold\">" );
+        setMonospacedElement( "<literal>" );
+        setHorizontalRuleElement( "<!-- HR -->" );
+        setPageBreakElement( "<!-- PB -->" );
+        setLineBreakElement( "<!-- LB -->" );
+    }
+
+    /**
+     * Constructor, initialize the Writer and tells which encoding and languageId are used.
+     *
+     * @param writer not null writer to write the result.
+     * @param encoding the encoding used, that should be written to the generated HTML content
+     * if not <code>null</code>.
+     * @param languageId language identifier for the root element as defined by
+     * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
+     * in addition, the empty string may be specified.
+     * @since 1.1
+     */
+    protected DocBookSink( Writer writer, String encoding, String languageId )
+    {
+        this( writer, encoding );
+
+        this.lang = languageId;
+    }
+
+    /**
+     * <p>escapeSGML</p>
+     *
+     * @param text The text to escape.
+     * @param xmlMode xmlMode.
+     * @return The escaped text.
+     * @deprecated Use HtmlTools#escapeHTML(String,boolean).
+     */
+    public static final String escapeSGML( String text, boolean xmlMode )
+    {
+        return HtmlTools.escapeHTML( text, xmlMode );
+    }
+
+    /**
+     * Sets the xml mode.
+     *
+     * @param mode the mode to set.
+     * @deprecated xml mode is not used.
+     */
+    public void setXMLMode( boolean mode )
+    {
+        this.xmlMode = mode;
+    }
+
+    /**
+     * Returns the current xmlMode.
+     *
+     * @return the current xmlMode.
+     * @deprecated xml mode is not used.
+     */
+    public boolean isXMLMode()
+    {
+        return xmlMode;
+    }
+
+    /**
+     * Sets the encoding. The encoding specified here must be consistent with then encoding
+     * used in the Writer used by this DocBookSink instance.
+     *
+     * @param enc the encoding to set.
+     */
+    public void setEncoding( String enc )
+    {
+        encoding = enc;
+    }
+
+    /**
+     * Returns the encoding.
+     *
+     * @return the encoding set (can be <code>null</code>).
+     */
+    public String getEncoding()
+    {
+        return encoding;
+    }
+
+    /**
+     * Sets the styleSheet.
+     *
+     * @param sheet the styleSheet to set.
+     */
+    public void setStyleSheet( String sheet )
+    {
+        this.styleSheet = sheet;
+    }
+
+    /**
+     * Returns the current styleSheet.
+     *
+     * @return the current styleSheet.
+     */
+    public String getStyleSheet()
+    {
+        return styleSheet;
+    }
+
+    /**
+     * Sets the publicId.
+     *
+     * @param id the publicId to set.
+     */
+    public void setPublicId( String id )
+    {
+        this.publicId = id;
+    }
+
+    /**
+     * Returns the current publicId.
+     *
+     * @return the current publicId.
+     */
+    public String getPublicId()
+    {
+        return publicId;
+    }
+
+    /**
+     * Sets the systemId.
+     *
+     * @param id the systemId to set.
+     */
+    public void setSystemId( String id )
+    {
+        this.systemId = id;
+    }
+
+    /**
+     * Returns the current systemId.
+     *
+     * @return the current systemId.
+     */
+    public String getSystemId()
+    {
+        return systemId;
+    }
+
+    /**
+     * Sets the language.
+     *
+     * @param language the language to set.
+     */
+    public void setLanguage( String language )
+    {
+        this.lang = language;
+    }
+
+    /**
+     * Returns the current language.
+     *
+     * @return the current language.
+     */
+    public String getLanguage()
+    {
+        return lang;
+    }
+
+    /**
+     * Sets the current italicBeginTag and constructs the corresponding end tag from it.
+     *
+     * @param tag the tag to set. If tag is null, the empty string is used.
+     */
+    public void setItalicElement( String tag )
+    {
+        if ( tag == null )
+        {
+            tag = "";
+        }
+        this.italicBeginTag = tag;
+        italicEndTag = makeEndTag( italicBeginTag );
+    }
+
+    /**
+     * Constructs the corresponding end tag from the given begin tag.
+     *
+     * @param beginTag the begin tag to set. If null, the empty string is returned.
+     * @return the corresponding end tag.
+     */
+    private String makeEndTag( String beginTag )
+    {
+        int length = beginTag.length();
+        if ( length == 0 )
+        {
+            return "";
+        }
+
+        if ( beginTag.charAt( 0 ) != '<' || beginTag.charAt( length - 1 ) != '>' )
+        {
+            throw new IllegalArgumentException( "'" + beginTag + "', not a tag" );
+        }
+
+        StringTokenizer tokens = new StringTokenizer( beginTag, "<> \t\n\r\f" );
+        if ( !tokens.hasMoreTokens() )
+        {
+            throw new IllegalArgumentException( "'" + beginTag + "', invalid tag" );
+        }
+
+        return "</" + tokens.nextToken() + ">";
+    }
+
+    /**
+     * Returns the current italicBeginTag.
+     *
+     * @return the current italicBeginTag. Defaults to "<emphasis>".
+     */
+    public String getItalicElement()
+    {
+        return italicBeginTag;
+    }
+
+    /**
+     * Sets the current boldBeginTag and constructs the corresponding end tag from it.
+     *
+     * @param tag the tag to set. If tag is null, the empty string is used.
+     */
+    public void setBoldElement( String tag )
+    {
+        if ( tag == null )
+        {
+            tag = "";
+        }
+        this.boldBeginTag = tag;
+        boldEndTag = makeEndTag( boldBeginTag );
+    }
+
+    /**
+     * Returns the current boldBeginTag.
+     *
+     * @return the current boldBeginTag. Defaults to "<emphasis role=\"bold\">".
+     */
+    public String getBoldElement()
+    {
+        return boldBeginTag;
+    }
+
+    /**
+     * Sets the current monospacedBeginTag and constructs the corresponding end tag from it.
+     *
+     * @param tag the tag to set. If tag is null, the empty string is used.
+     */
+    public void setMonospacedElement( String tag )
+    {
+        if ( tag == null )
+        {
+            tag = "";
+        }
+        this.monospacedBeginTag = tag;
+        monospacedEndTag = makeEndTag( monospacedBeginTag );
+    }
+
+    /**
+     * Returns the current monospacedBeginTag.
+     *
+     * @return the current monospacedBeginTag. Defaults to "<literal>>".
+     */
+    public String getMonospacedElement()
+    {
+        return monospacedBeginTag;
+    }
+
+    /**
+     * Sets the current horizontalRuleElement.
+     *
+     * @param element the element to set.
+     */
+    public void setHorizontalRuleElement( String element )
+    {
+        this.horizontalRuleElement = element;
+    }
+
+    /**
+     * Returns the current horizontalRuleElement.
+     *
+     * @return the current horizontalRuleElement. Defaults to "<!-- HR -->".
+     */
+    public String getHorizontalRuleElement()
+    {
+        return horizontalRuleElement;
+    }
+
+    /**
+     * Sets the current pageBreakElement.
+     *
+     * @param element the element to set.
+     */
+    public void setPageBreakElement( String element )
+    {
+        this.pageBreakElement = element;
+    }
+
+    /**
+     * Returns the current pageBreakElement.
+     *
+     * @return the current pageBreakElement. Defaults to "<!-- PB -->".
+     */
+    public String getPageBreakElement()
+    {
+        return pageBreakElement;
+    }
+
+    /**
+     * Sets the current lineBreakElement.
+     *
+     * @param element the element to set.
+     */
+    public void setLineBreakElement( String element )
+    {
+        this.lineBreakElement = element;
+    }
+
+    /**
+     * Returns the current lineBreakElement.
+     *
+     * @return the current lineBreakElement. Defaults to "<!-- LB -->".
+     */
+    public String getLineBreakElement()
+    {
+        return lineBreakElement;
+    }
+
+    /**
+     * Reset all variables.
+     *
+     * @deprecated since 1.1.2, use {@link #init()} instead of.
+     */
+    protected void resetState()
+    {
+       init();
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        hasTitle = false;
+        authorDateFlag = false;
+        verbatimFlag = false;
+        externalLinkFlag = false;
+        graphicsFileName = null;
+        tableHasCaption = false;
+        savedOut = null;
+        tableRows = null;
+        tableHasGrid = false;
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#DEFAULT_XML_PUBLIC_ID
+     * @see SimplifiedDocbookMarkup#DEFAULT_XML_SYSTEM_ID
+     * @see SimplifiedDocbookMarkup#ARTICLE_TAG
+     */
+    public void head()
+    {
+        init();
+
+        MutableAttributeSet att = writeXmlHeader( "article" );
+
+        writeStartTag( SimplifiedDocbookMarkup.ARTICLE_TAG, att );
+    }
+
+    /**
+     * writeXmlHeader.
+     *
+     * @param root not null.
+     * @return an attribute set.
+     * @see SimplifiedDocbookMarkup#DEFAULT_XML_PUBLIC_ID
+     * @see SimplifiedDocbookMarkup#DEFAULT_XML_SYSTEM_ID
+     * @see SimplifiedDocbookMarkup#ARTICLE_TAG
+     * @since 1.1
+     */
+    protected MutableAttributeSet writeXmlHeader( String root )
+    {
+        markup( "<?xml version=\"1.0\"" );
+        if ( encoding != null )
+        {
+            markup( " encoding=\"" + encoding + "\"" );
+        }
+        markup( "?>" );
+
+        if ( styleSheet != null )
+        {
+            markup( "<?xml-stylesheet type=\"text/css\" href=\"" + styleSheet + "\" ?>" );
+        }
+
+        String pubId;
+        markup( "<!DOCTYPE " + root + " PUBLIC" );
+
+        if ( publicId == null )
+        {
+            pubId = SimplifiedDocbookMarkup.DEFAULT_XML_PUBLIC_ID;
+        }
+        else
+        {
+            pubId = publicId;
+        }
+        markup( " \"" + pubId + "\"" );
+        String sysId = systemId;
+        if ( sysId == null )
+        {
+                sysId = SimplifiedDocbookMarkup.DEFAULT_XML_SYSTEM_ID;
+        }
+        markup( " \"" + sysId + "\">" );
+
+        MutableAttributeSet att = new SimpleAttributeSet();
+        if ( lang != null )
+        {
+            att.addAttribute( LANG_ATTRIBUTE, lang );
+        }
+        return att;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ARTICLEINFO_TAG
+     */
+    public void head_()
+    {
+        if ( hasTitle )
+        {
+            writeEndTag( SimplifiedDocbookMarkup.ARTICLEINFO_TAG );
+            hasTitle = false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ARTICLEINFO_TAG
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void title()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.ARTICLEINFO_TAG );
+        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
+        hasTitle = true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void title_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#CORPAUTHOR_TAG
+     */
+    public void author()
+    {
+        authorDateFlag = true;
+        writeStartTag( SimplifiedDocbookMarkup.CORPAUTHOR_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#CORPAUTHOR_TAG
+     */
+    public void author_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.CORPAUTHOR_TAG );
+        authorDateFlag = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#DATE_TAG
+     */
+    public void date()
+    {
+        authorDateFlag = true;
+        writeStartTag( SimplifiedDocbookMarkup.DATE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#DATE_TAG
+     */
+    public void date_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.DATE_TAG );
+        authorDateFlag = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ARTICLE_TAG
+     */
+    public void body_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.ARTICLE_TAG );
+        out.flush();
+        init();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section1()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section1_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section2()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section2_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section3()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section3_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section4()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section4_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section5()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#SECTION_TAG
+     */
+    public void section5_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle1()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle1_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle2()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle2_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle3()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle3_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle4()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle4_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle5()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void sectionTitle5_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ITEMIZEDLIST_TAG
+     */
+    public void list()
+    {
+        paragraph_();
+        writeStartTag( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ITEMIZEDLIST_TAG
+     */
+    public void list_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
+     */
+    public void listItem()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
+        paragraph();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
+     */
+    public void listItem_()
+    {
+        paragraph_();
+        writeEndTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ORDEREDLIST_TAG
+     * @see SimplifiedDocbookMarkup#NUMERATION_ATTRIBUTE
+     */
+    public void numberedList( int numbering )
+    {
+        String numeration = DocbookUtils.docbookListNumbering( numbering );
+
+        paragraph_();
+
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( SimplifiedDocbookMarkup.NUMERATION_ATTRIBUTE, numeration );
+
+        writeStartTag( SimplifiedDocbookMarkup.ORDEREDLIST_TAG, att );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ORDEREDLIST_TAG
+     */
+    public void numberedList_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.ORDEREDLIST_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
+     */
+    public void numberedListItem()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
+        paragraph();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
+     */
+    public void numberedListItem_()
+    {
+        paragraph_();
+        writeEndTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#VARIABLELIST_TAG
+     */
+    public void definitionList()
+    {
+        paragraph_();
+        writeStartTag( SimplifiedDocbookMarkup.VARIABLELIST_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#VARIABLELIST_TAG
+     */
+    public void definitionList_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.VARIABLELIST_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#VARLISTENTRY_TAG
+     */
+    public void definitionListItem()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.VARLISTENTRY_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#VARLISTENTRY_TAG
+     */
+    public void definitionListItem_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.VARLISTENTRY_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TERM_TAG
+     */
+    public void definedTerm()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.TERM_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TERM_TAG
+     */
+    public void definedTerm_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TERM_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
+     */
+    public void definition()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
+        paragraph();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
+     */
+    public void definition_()
+    {
+        paragraph_();
+        writeEndTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#PARA_TAG
+     */
+    public void paragraph()
+    {
+        if ( !paragraph )
+        {
+            writeStartTag( SimplifiedDocbookMarkup.PARA_TAG );
+            paragraph = true;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#PARA_TAG
+     */
+    public void paragraph_()
+    {
+        if ( paragraph )
+        {
+            writeEndTag( SimplifiedDocbookMarkup.PARA_TAG );
+            paragraph = false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#PROGRAMLISTING_TAG
+     */
+    public void verbatim( boolean boxed )
+    {
+        verbatimFlag = true;
+        paragraph_();
+        writeStartTag( SimplifiedDocbookMarkup.PROGRAMLISTING_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#PROGRAMLISTING_TAG
+     */
+    public void verbatim_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.PROGRAMLISTING_TAG );
+        verbatimFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        markup( horizontalRuleElement );
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        markup( pageBreakElement );
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG );
+    }
+
+    /**
+     * <p>graphicElement</p>
+     *
+     * @see SimplifiedDocbookMarkup#MEDIAOBJECT_TAG
+     * @see SimplifiedDocbookMarkup#IMAGEOBJECT_TAG
+     * @see SimplifiedDocbookMarkup#IMAGEDATA_TAG
+     * @see SimplifiedDocbookMarkup#FORMAT_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#FILEREF_ATTRIBUTE
+     * @deprecated do not use!
+     */
+    protected void graphicElement()
+    {
+        if ( graphicsFileName != null )
+        {
+            String format = FileUtils.extension( graphicsFileName ).toUpperCase( Locale.ENGLISH );
+            if ( format.length() == 0 )
+            {
+                format = "JPEG";
+            }
+
+            writeStartTag( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG );
+            writeStartTag( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG );
+
+            MutableAttributeSet att = new SimpleAttributeSet();
+            att.addAttribute( SimplifiedDocbookMarkup.FORMAT_ATTRIBUTE, format );
+            att.addAttribute( SimplifiedDocbookMarkup.FILEREF_ATTRIBUTE,
+                    HtmlTools.escapeHTML( graphicsFileName, true ) );
+
+            writeSimpleTag( SimplifiedDocbookMarkup.IMAGEDATA_TAG, att );
+
+            writeEndTag( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG );
+            writeEndTag( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG );
+            graphicsFileName = null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        String format = FileUtils.extension( name ).toUpperCase( Locale.ENGLISH );
+
+        writeStartTag( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG );
+
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( SimplifiedDocbookMarkup.FORMAT_ATTRIBUTE, format );
+        att.addAttribute( SimplifiedDocbookMarkup.FILEREF_ATTRIBUTE, HtmlTools.escapeHTML( name, true ) );
+
+        writeSimpleTag( SimplifiedDocbookMarkup.IMAGEDATA_TAG, att );
+
+        writeEndTag( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#FIGURE_TAG
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void figureCaption()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.CAPTION_TAG );
+        writeStartTag( SimplifiedDocbookMarkup.PARA_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#FIGURE_TAG
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void figureCaption_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.PARA_TAG );
+        writeEndTag( SimplifiedDocbookMarkup.CAPTION_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        tableHasCaption = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#INFORMALTABLE_TAG
+     * @see SimplifiedDocbookMarkup#FRAME_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#ROWSEP_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#COLSEP_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#TABLE_TAG
+     */
+    public void table_()
+    {
+        if ( tableHasCaption )
+        {
+            tableHasCaption = false;
+            // Formal table+title already written to original destination ---
+
+            out.write( tableRows  );
+            writeEndTag( TABLE_TAG );
+        }
+        else
+        {
+            String frame = "none";
+            String sep = "0";
+            if ( tableHasGrid )
+            {
+                frame = "all";
+                sep = "1";
+            }
+
+            MutableAttributeSet att = new SimpleAttributeSet();
+            att.addAttribute( SimplifiedDocbookMarkup.FRAME_ATTRIBUTE, frame );
+            att.addAttribute( SimplifiedDocbookMarkup.ROWSEP_ATTRIBUTE, sep );
+            att.addAttribute( SimplifiedDocbookMarkup.COLSEP_ATTRIBUTE, sep );
+
+            writeStartTag( SimplifiedDocbookMarkup.INFORMALTABLE_TAG, att );
+
+            out.write( tableRows  );
+
+            writeEndTag( SimplifiedDocbookMarkup.INFORMALTABLE_TAG );
+        }
+
+        tableRows = null;
+        tableHasGrid = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TGROUP_TAG
+     * @see SimplifiedDocbookMarkup#COLS_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#COLSPEC_TAG
+     */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        tableHasGrid = grid;
+
+        // Divert output to a string ---
+        out.flush();
+        savedOut = out;
+        tableRowsWriter = new StringWriter();
+        out = new PrintWriter( tableRowsWriter );
+
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( SimplifiedDocbookMarkup.COLS_ATTRIBUTE, String.valueOf( justification.length ) );
+
+        writeStartTag( SimplifiedDocbookMarkup.TGROUP_TAG, att );
+
+        for ( int i = 0; i < justification.length; ++i )
+        {
+            String justif;
+            switch ( justification[i] )
+            {
+                case Sink.JUSTIFY_LEFT:
+                    justif = "left";
+                    break;
+                case Sink.JUSTIFY_RIGHT:
+                    justif = "right";
+                    break;
+                case Sink.JUSTIFY_CENTER:
+                default:
+                    justif = "center";
+                    break;
+            }
+
+            att = new SimpleAttributeSet();
+            att.addAttribute( "align", justif );
+
+            writeSimpleTag( SimplifiedDocbookMarkup.COLSPEC_TAG, att );
+        }
+
+        writeStartTag( SimplifiedDocbookMarkup.TBODY_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TGROUP_TAG
+     * @see SimplifiedDocbookMarkup#TBODY_TAG
+     */
+    public void tableRows_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TBODY_TAG );
+        writeEndTag( SimplifiedDocbookMarkup.TGROUP_TAG );
+
+        // Remember diverted output and restore original destination ---
+        out.flush();
+        if ( tableRowsWriter == null )
+        {
+            throw new IllegalArgumentException( "tableRows( int[] justification, boolean grid )"
+                                                + " was not called before." );
+        }
+
+        tableRows = tableRowsWriter.toString();
+        tableRowsWriter = null;
+        out = savedOut;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ROW_TAG
+     */
+    public void tableRow()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.ROW_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ROW_TAG
+     */
+    public void tableRow_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.ROW_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ENTRY_TAG
+     */
+    public void tableCell()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.ENTRY_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ENTRY_TAG
+     */
+    public void tableCell_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.ENTRY_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ENTRY_TAG
+     */
+    public void tableHeaderCell()
+    {
+        writeStartTag( SimplifiedDocbookMarkup.ENTRY_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ENTRY_TAG
+     */
+    public void tableHeaderCell_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.ENTRY_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TABLE_TAG
+     * @see SimplifiedDocbookMarkup#FRAME_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#ROWSEP_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#COLSEP_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void tableCaption()
+    {
+        tableHasCaption = true;
+
+        String frame;
+        int sep;
+        if ( tableHasGrid )
+        {
+            frame = "all";
+            sep = 1;
+        }
+        else
+        {
+            frame = "none";
+            sep = 0;
+        }
+
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( SimplifiedDocbookMarkup.FRAME_ATTRIBUTE, frame );
+        att.addAttribute( SimplifiedDocbookMarkup.ROWSEP_ATTRIBUTE, String.valueOf( sep ) );
+        att.addAttribute( SimplifiedDocbookMarkup.COLSEP_ATTRIBUTE, String.valueOf( sep ) );
+
+        writeStartTag( TABLE_TAG, att );
+
+        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#TITLE_TAG
+     */
+    public void tableCaption_()
+    {
+        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ANCHOR_TAG
+     */
+    public void anchor( String name )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Anchor name cannot be null!" );
+        }
+
+        if ( authorDateFlag )
+        {
+            return;
+        }
+
+        String id = name;
+
+        if ( !DoxiaUtils.isValidId( id ) )
+        {
+            id = DoxiaUtils.encodeId( name, true );
+
+            String msg = "Modified invalid anchor name: '" + name + "' to '" + id + "'";
+            logMessage( "modifiedLink", msg );
+        }
+
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( ID_ATTRIBUTE, id );
+
+        writeSimpleTag( SimplifiedDocbookMarkup.ANCHOR_TAG, att );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ANCHOR_TAG
+     */
+    public void anchor_()
+    {
+        comment( "anchor_end" );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ULINK_TAG
+     * @see SimplifiedDocbookMarkup#URL_ATTRIBUTE
+     * @see SimplifiedDocbookMarkup#LINK_TAG
+     * @see SimplifiedDocbookMarkup#LINKEND_ATTRIBUTE
+     */
+    public void link( String name )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Link name cannot be null!" );
+        }
+
+        if ( DoxiaUtils.isInternalLink( name ) )
+        {
+            String linkend = name.substring( 1 );
+            MutableAttributeSet att = new SimpleAttributeSet();
+            att.addAttribute( SimplifiedDocbookMarkup.LINKEND_ATTRIBUTE, HtmlTools.escapeHTML( linkend ) );
+
+            writeStartTag( SimplifiedDocbookMarkup.LINK_TAG, att );
+        }
+        else
+        {
+            externalLinkFlag = true;
+            MutableAttributeSet att = new SimpleAttributeSet();
+            att.addAttribute( SimplifiedDocbookMarkup.URL_ATTRIBUTE, HtmlTools.escapeHTML( name, true ) );
+
+            writeStartTag( SimplifiedDocbookMarkup.ULINK_TAG, att );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see SimplifiedDocbookMarkup#ULINK_TAG
+     * @see SimplifiedDocbookMarkup#LINK_TAG
+     */
+    public void link_()
+    {
+        if ( externalLinkFlag )
+        {
+            writeEndTag( SimplifiedDocbookMarkup.ULINK_TAG );
+            externalLinkFlag = false;
+        }
+        else
+        {
+            writeEndTag( SimplifiedDocbookMarkup.LINK_TAG );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        markup( italicBeginTag );
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        markup( italicEndTag );
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        markup( boldBeginTag );
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        markup( boldEndTag );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        if ( !authorDateFlag )
+        {
+            markup( monospacedBeginTag );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        if ( !authorDateFlag )
+        {
+            markup( monospacedEndTag );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        markup( lineBreakElement );
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        markup( "&#x00A0;" );
+        //markup( " " );
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        if ( verbatimFlag )
+        {
+            verbatimContent( text );
+        }
+        else
+        {
+            content( text );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+        if ( StringUtils.isNotEmpty( comment ) && comment.indexOf( "--" ) != -1 )
+        {
+            String originalComment = comment;
+            // http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments
+            while ( comment.indexOf( "--" ) != -1 )
+            {
+                comment = StringUtils.replace( comment, "--", "- -" );
+            }
+
+            String msg = "Modified invalid comment: '" + originalComment + "' to '" + comment + "'";
+            logMessage( "modifiedComment", msg );
+        }
+
+        StringBuffer buffer = new StringBuffer( comment.length() + 9 );
+
+        buffer.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS ).append( SPACE );
+        buffer.append( comment );
+        buffer.append( SPACE ).append( MINUS ).append( MINUS ).append( GREATER_THAN );
+
+        markup( buffer.toString() );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Unkown events just log a warning message but are ignored otherwise.
+     * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        String msg = "Unknown Sink event: '" + name + "', ignoring!";
+        logMessage( "unknownEvent", msg );
+    }
+
+    // -----------------------------------------------------------------------
+
+    /**
+     * Write text to output, preserving white space.
+     *
+     * @param text The text to write.
+     */
+    protected void markup( String text )
+    {
+        if ( !skip )
+        {
+            out.write( text );
+        }
+    }
+
+    /**
+     * Write SGML escaped text to output, not preserving white space.
+     *
+     * @param text The text to write.
+     */
+    protected void content( String text )
+    {
+        if ( !skip )
+        {
+            out.write( HtmlTools.escapeHTML( text, true ) );
+        }
+    }
+
+    /**
+     * Write SGML escaped text to output, preserving white space.
+     *
+     * @param text The text to write.
+     */
+    protected void verbatimContent( String text )
+    {
+        if ( !skip )
+        {
+            out.write( HtmlTools.escapeHTML( text, true ) );
+        }
+    }
+
+    // -----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        out.flush();
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        out.close();
+
+        if ( getLog().isWarnEnabled() && this.warnMessages != null )
+        {
+            for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                Set set = (Set) entry.getValue();
+
+                for ( Iterator it2 = set.iterator(); it2.hasNext(); )
+                {
+                    String msg = (String) it2.next();
+
+                    getLog().warn( msg );
+                }
+            }
+
+            this.warnMessages = null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void write( String text )
+    {
+        markup( unifyEOLs( text ) );
+    }
+
+    /**
+     * <p>Setter for the field <code>skip</code>.</p>
+     *
+     * @param skip the skip to set.
+     * @since 1.1
+     */
+    public void setSkip( boolean skip )
+    {
+        this.skip = skip;
+    }
+
+    /**
+     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
+     *
+     * @param key not null
+     * @param msg not null
+     * @see #close()
+     * @since 1.1.1
+     */
+    private void logMessage( String key, String msg )
+    {
+        msg = "[Docbook Sink] " + msg;
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( msg );
+
+            return;
+        }
+
+        if ( warnMessages == null )
+        {
+            warnMessages = new HashMap();
+        }
+
+        Set set = (Set) warnMessages.get( key );
+        if ( set == null )
+        {
+            set = new TreeSet();
+        }
+        set.add( msg );
+        warnMessages.put( key, set );
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookSiteModule.java b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookSiteModule.java
new file mode 100644
index 0000000..6e6f0da
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocBookSiteModule.java
@@ -0,0 +1,42 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.site.AbstractSiteModule;
+
+/**
+ * <p>DocBookSiteModule class.</p>
+ *
+ * @author <a href="mailto:evenisse at codehaus.org">Emmanuel Venisse</a>
+ * @version $Id: DocBookSiteModule.java 782392 2009-06-07 14:14:16Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.module.site.SiteModule" role-hint="docbook"
+ */
+public class DocBookSiteModule
+    extends AbstractSiteModule
+{
+    /**
+     * Default constructor.
+     */
+    public DocBookSiteModule()
+    {
+        super( "docbook", "xml", "docbook" );
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookMarkup.java b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookMarkup.java
new file mode 100644
index 0000000..4370f91
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookMarkup.java
@@ -0,0 +1,474 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.markup.XmlMarkup;
+
+/**
+ * List of <code>DocBook</code> markups.
+ * TODO: only use <a href="http://www.docbook.org/schemas/sdocbook/elements.html">Simplified DocBook elements</a>,
+ * remove full DocBook-only ones.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: DocbookMarkup.java 778398 2009-05-25 11:55:37Z ltheussl $
+ * @since 1.0
+ * @deprecated This interface is incomplete and will be removed. Use {@link SimplifiedDocbookMarkup} instead.
+ */
+public interface DocbookMarkup
+    extends XmlMarkup
+{
+    /** DocBook V4.4 SGML public id: "-//OASIS//DTD DocBook V4.4//EN" */
+    String DEFAULT_SGML_PUBLIC_ID = "-//OASIS//DTD DocBook V4.4//EN";
+
+    /** DocBook XML V4.4 XML public id: "-//OASIS//DTD DocBook XML V4.4//EN" */
+    String DEFAULT_XML_PUBLIC_ID = "-//OASIS//DTD DocBook V4.4//EN";
+
+    /** DocBook XML V4.4 XML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" */
+    String DEFAULT_XML_SYSTEM_ID = "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd";
+
+    /** DocBook XML V4.4 SGML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" */
+    String DEFAULT_SGML_SYSTEM_ID = "http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd";
+
+    // ----------------------------------------------------------------------
+    // Specific DocBook tags
+    // ----------------------------------------------------------------------
+
+    /** DocBook tag for <code>anchor</code> */
+    Tag ANCHOR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "anchor";
+        }
+    };
+
+    /** DocBook tag for <code>article</code> */
+    Tag ARTICLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "article";
+        }
+    };
+
+    /** DocBook tag for <code>articleinfo</code> */
+    Tag ARTICLEINFO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "articleinfo";
+        }
+    };
+
+    /** DocBook tag for <code>book</code> */
+    Tag BOOK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "book";
+        }
+    };
+
+    /** DocBook tag for <code>bookinfo</code> */
+    Tag BOOKINFO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bookinfo";
+        }
+    };
+
+    /** DocBook tag for <code>chapter</code> */
+    Tag CHAPTER_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "chapter";
+        }
+    };
+
+    /** DocBook tag for <code>colspec</code> */
+    Tag COLSPEC_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "colspec";
+        }
+    };
+
+    /** DocBook tag for <code>corpauthor</code> */
+    Tag CORPAUTHOR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "corpauthor";
+        }
+    };
+
+    /** DocBook tag for <code>date</code> */
+    Tag DATE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "date";
+        }
+    };
+
+    /** DocBook tag for <code>email</code> */
+    Tag EMAIL_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "email";
+        }
+    };
+
+    /** DocBook tag for <code>entry</code> */
+    Tag ENTRY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "entry";
+        }
+    };
+
+    /** DocBook tag for <code>figure</code> */
+    Tag FIGURE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "figure";
+        }
+    };
+
+    /** DocBook tag for <code>formalpara</code> */
+    Tag FORMALPARA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "formalpara";
+        }
+    };
+
+    /** DocBook tag for <code>imagedata</code> */
+    Tag IMAGEDATA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "imagedata";
+        }
+    };
+
+    /** DocBook tag for <code>imageobject</code> */
+    Tag IMAGEOBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "imageobject";
+        }
+    };
+
+    /** DocBook tag for <code>info</code> */
+    Tag INFO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "info";
+        }
+    };
+
+    /** DocBook tag for <code>informalfigure</code> */
+    Tag INFORMALFIGURE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "informalfigure";
+        }
+    };
+
+    /** DocBook tag for <code>informaltable</code> */
+    Tag INFORMALTABLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "informaltable";
+        }
+    };
+
+    /** DocBook tag for <code>itemizedlist</code> */
+    Tag ITEMIZEDLIST_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "itemizedlist";
+        }
+    };
+
+    /** DocBook tag for <code>link</code> */
+    Tag LINK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "link";
+        }
+    };
+
+    /** DocBook tag for <code>listitem</code> */
+    Tag LISTITEM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "listitem";
+        }
+    };
+
+    /** DocBook tag for <code>mediaobject</code> */
+    Tag MEDIAOBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "mediaobject";
+        }
+    };
+
+    /** DocBook tag for <code>orderedlist</code> */
+    Tag ORDEREDLIST_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "orderedlist";
+        }
+    };
+
+    /** DocBook tag for <code>para</code> */
+    Tag PARA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "para";
+        }
+    };
+
+    /** DocBook tag for <code>programlisting</code> */
+    Tag PROGRAMLISTING_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "programlisting";
+        }
+    };
+
+    /** DocBook tag for <code>row</code> */
+    Tag ROW_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "row";
+        }
+    };
+
+    /** DocBook tag for <code>section</code> */
+    Tag SECTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "section";
+        }
+    };
+
+    /** DocBook tag for <code>simpara</code> */
+    Tag SIMPARA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "simpara";
+        }
+    };
+
+    /** DocBook tag for <code>tbody</code> */
+    Tag TBODY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tbody";
+        }
+    };
+
+    /** DocBook tag for <code>term</code> */
+    Tag TERM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "term";
+        }
+    };
+
+    /** DocBook tag for <code>tgroup</code> */
+    Tag TGROUP_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tgroup";
+        }
+    };
+
+    /** DocBook tag for <code>thead</code> */
+    Tag THEAD_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "thead";
+        }
+    };
+
+    /** DocBook tag for <code>ulink</code> */
+    Tag ULINK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "ulink";
+        }
+    };
+
+    /** DocBook tag for <code>url</code> */
+    Tag URL_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "url";
+        }
+    };
+
+    /** DocBook tag for <code>variablelist</code> */
+    Tag VARIABLELIST_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "variablelist";
+        }
+    };
+
+    /** DocBook tag for <code>varlistentry</code> */
+    Tag VARLISTENTRY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "varlistentry";
+        }
+    };
+
+    /** DocBook tag for <code>xref</code> */
+    Tag XREF_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "xref";
+        }
+    };
+
+    // ----------------------------------------------------------------------
+    // Specific Docbook attributes
+    // ----------------------------------------------------------------------
+
+    /** DocBook attribute <code>cols</code> used in <code>tgroup</code> tags */
+    String COLS_ATTRIBUTE = "cols";
+
+    /** DocBook attribute <code>colsep</code> used in <code>informaltable</code> tags */
+    String COLSEP_ATTRIBUTE = "colsep";
+
+    /** DocBook attribute <code>fileref</code> used in <code>orderedlist, imageobject</code> tags */
+    String FILEREF_ATTRIBUTE = "fileref";
+
+    /** DocBook attribute <code>format</code> used in <code>imagedata</code> tags */
+    String FORMAT_ATTRIBUTE = "format";
+
+    /** DocBook attribute <code>frame</code> used in <code>informaltable</code> tags */
+    String FRAME_ATTRIBUTE = "frame";
+
+    /** DocBook attribute <code>linkend</code> used in <code>link</code> tag */
+    String LINKEND_ATTRIBUTE = "linkend";
+
+    /** DocBook attribute <code>numeration</code> used in <code>orderedlist</code> tag */
+    String NUMERATION_ATTRIBUTE = "numeration";
+
+    /** DocBook attribute <code>rowsep</code> used in <code>informaltable</code> tags */
+    String ROWSEP_ATTRIBUTE = "rowsep";
+
+    /** DocBook attribute <code>url</code> used in <code>ulink</code> tags */
+    String URL_ATTRIBUTE = "url";
+
+    // ----------------------------------------------------------------------
+    // Specific Docbook styles
+    // ----------------------------------------------------------------------
+
+    /** Docbook style <code>arabic</code> used in <code>numeration</code> attribute */
+    String ARABIC_STYLE = "arabic";
+
+    /** DocBook style <code>loweralpha</code> used in <code>numeration</code> attribute */
+    String LOWERALPHA_STYLE = "loweralpha";
+
+    /** DocBook style <code>lowerroman</code> used in <code>numeration</code> attribute */
+    String LOWERROMAN_STYLE = "lowerroman";
+
+    /** DocBook style <code>upperalpha</code> used in <code>numeration</code> attribute */
+    String UPPERALPHA_STYLE = "upperalpha";
+
+    /** DocBook style <code>upperroman</code> used in <code>numeration</code> attribute */
+    String UPPERROMAN_STYLE = "upperroman";
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookSinkFactory.java b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookSinkFactory.java
new file mode 100644
index 0000000..0e0f69e
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookSinkFactory.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractXmlSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Docbook implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: DocbookSinkFactory.java 739565 2009-01-31 14:39:03Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="docbook"
+ */
+public class DocbookSinkFactory
+    extends AbstractXmlSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        return new DocBookSink( writer, encoding );
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding, String languageId )
+    {
+        return new DocBookSink( writer, encoding, languageId );
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookUtils.java b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookUtils.java
new file mode 100644
index 0000000..db69220
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/DocbookUtils.java
@@ -0,0 +1,191 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Utility methods for Doxia Docbook Parser and Sink.
+ *
+ * @author ltheussl
+ * @version $Id: DocbookUtils.java 784698 2009-06-15 09:19:18Z ltheussl $
+ * @since 1.1.1
+ */
+public final class DocbookUtils
+{
+    /**
+     * Translate a given Docbook table frame attribute value to a valid
+     * Doxia table frame attribute value.
+     *
+     * <p>The input has to be one of <code>"all"</code>, <code>"bottom"</code>,
+     * <code>"none"</code>, <code>"sides"</code>, <code>"top"</code> or <code>"topbot"</code>,
+     * otherwise an IllegalArgumentException is thrown.</p>
+     *
+     * <p>The corresponding output values are <code>"box"</code>, <code>"below"</code>,
+     * <code>"void"</code>, <code>"vsides"</code>, <code>"above"</code> and <code>"hsides"</code>.</p>
+     *
+     * @param frame a valid docbook table frame attribute as specified above,
+     * otherwise an IllegalArgumentException is thrown.
+     * @return a valid Doxia table frame attribute as specified above.
+     */
+    public static String doxiaTableFrameAttribute( final String frame )
+    {
+        String fr = frame;
+
+        if ( "all".equals( fr ) )
+        {
+            fr = "box";
+        }
+        else if ( "bottom".equals( fr ) )
+        {
+            fr = "below";
+        }
+        else if ( "none".equals( fr ) )
+        {
+            fr = "void";
+        }
+        else if ( "sides".equals( fr ) )
+        {
+            fr = "vsides";
+        }
+        else if ( "top".equals( fr ) )
+        {
+            fr = "above";
+        }
+        else if ( "topbot".equals( fr ) )
+        {
+            fr = "hsides";
+        }
+        else
+        {
+            throw new IllegalArgumentException( "Not a valid frame attribute: " + fr );
+        }
+
+        return fr;
+    }
+
+    /**
+     * Convert a docbook ordered-list numbering style to a doxia numbering style.
+     *
+     * <p>The input has to be one of the style constants defined in {@link SimplifiedDocbookMarkup},
+     * otherwise an IllegalArgumentException is thrown.</p>
+     *
+     * <p>The output is one of the numbering constants defined in {@link Sink}.</p>
+     * @param style a docbook ordered-list numbering style.
+     * @return a doxia numbering style.
+     */
+    public static int doxiaListNumbering( final String style )
+    {
+        if ( SimplifiedDocbookMarkup.LOWERALPHA_STYLE.equals( style ) )
+        {
+            return Sink.NUMBERING_LOWER_ALPHA;
+        }
+        else if ( SimplifiedDocbookMarkup.LOWERROMAN_STYLE.equals( style ) )
+        {
+            return Sink.NUMBERING_LOWER_ROMAN;
+        }
+        else if ( SimplifiedDocbookMarkup.UPPERALPHA_STYLE.equals( style ) )
+        {
+            return Sink.NUMBERING_UPPER_ALPHA;
+        }
+        else if ( SimplifiedDocbookMarkup.UPPERROMAN_STYLE.equals( style ) )
+        {
+            return Sink.NUMBERING_UPPER_ROMAN;
+        }
+        else if ( SimplifiedDocbookMarkup.ARABIC_STYLE.equals( style ) )
+        {
+            return Sink.NUMBERING_DECIMAL;
+        }
+        else
+        {
+            throw new IllegalArgumentException( "Not a valid numbering style: " + style );
+        }
+    }
+
+    /**
+     * Convert a doxia numbering style to a docbook ordered-list numbering style.
+     *
+     * <p>The input has to be one of the numbering constants defined in {@link Sink},
+     * otherwise an IllegalArgumentException is thrown.</p>
+     *
+     * <p>The output is one of the style constants defined in {@link SimplifiedDocbookMarkup}.</p>
+     * @param numbering a doxia numbering style.
+     * @return a docbook ordered-list numbering style.
+     */
+    public static String docbookListNumbering( final int numbering )
+    {
+        switch ( numbering )
+        {
+            case Sink.NUMBERING_UPPER_ALPHA:
+                return SimplifiedDocbookMarkup.UPPERALPHA_STYLE;
+            case Sink.NUMBERING_LOWER_ALPHA:
+                return SimplifiedDocbookMarkup.LOWERALPHA_STYLE;
+            case Sink.NUMBERING_UPPER_ROMAN:
+                return SimplifiedDocbookMarkup.UPPERROMAN_STYLE;
+            case Sink.NUMBERING_LOWER_ROMAN:
+                return SimplifiedDocbookMarkup.LOWERROMAN_STYLE;
+            case Sink.NUMBERING_DECIMAL:
+                return SimplifiedDocbookMarkup.ARABIC_STYLE;
+            default:
+                throw new IllegalArgumentException( "Not a valid numbering: " + numbering );
+        }
+    }
+
+    /**
+     * Get a trademark character from a class attribute.
+     *
+     * <p>The input String has to be one of <code>"registered"</code>, <code>"copyright"</code>,
+     * <code>"service"</code> or <code>"trade"</code> otherwise an IllegalArgumentException is thrown.</p>
+     *
+     * <p>The corresponding output is <code>'\u00AE'</code>, <code>'\u00A9'</code>,
+     * <code>'\u2120'</code> or <code>'\u2122'</code>.</p>
+     *
+     * @param trade a valid class atribute for the docbook <code><trademark></code> tag.
+     * @return the corresponding unicode character.
+     */
+    public static char trademarkFromClass( final String trade )
+    {
+        if ( "registered".equals( trade ) )
+        {
+            return '\u00AE';
+        }
+        else if ( "copyright".equals( trade ) )
+        {
+            return '\u00A9';
+        }
+        else if ( "service".equals( trade ) )
+        {
+            return '\u2120';
+        }
+        else if ( "trade".equals( trade ) )
+        {
+            return '\u2122';
+        }
+        else
+        {
+            throw new IllegalArgumentException( "Not a trademark class: " + trade );
+        }
+    }
+
+    private DocbookUtils()
+    {
+        // utility class
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/SimplifiedDocbookMarkup.java b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/SimplifiedDocbookMarkup.java
new file mode 100644
index 0000000..dce3014
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/main/java/org/apache/maven/doxia/module/docbook/SimplifiedDocbookMarkup.java
@@ -0,0 +1,1303 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.markup.XmlMarkup;
+
+/**
+ * List of <code>Simplified DocBook</code> markups.
+ * Contains all 119 elements of the
+ * <a href="http://www.docbook.org/schemas/sdocbook/elements.html">Simplified DocBook DTD v. 1.1</a>.
+ *
+ * @author ltheussl
+ * @version $Id: SimplifiedDocbookMarkup.java 778398 2009-05-25 11:55:37Z ltheussl $
+ * @since 1.1.1
+ */
+public interface SimplifiedDocbookMarkup
+    extends XmlMarkup
+{
+    /** DocBook XML V1.1 XML public identifier: "-//OASIS//DTD Simplified DocBook XML V1.1//EN". */
+    String DEFAULT_XML_PUBLIC_ID = "-//OASIS//DTD Simplified DocBook XML V1.1//EN";
+
+    /** DocBook XML V1.1 XML system identifier: "http://www.oasis-open.org/docbook/xml/simple/1.1/sdocbook.dtd". */
+    String DEFAULT_XML_SYSTEM_ID = "http://www.oasis-open.org/docbook/xml/simple/1.1/sdocbook.dtd";
+
+    // ----------------------------------------------------------------------
+    // 119 Simplified DocBook elements
+    // ----------------------------------------------------------------------
+
+    /** DocBook tag for <code>abbrev</code>. */
+    Tag ABBREV_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "abbrev";
+        }
+    };
+
+    /** DocBook tag for <code>abstract</code>. */
+    Tag ABSTRACT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "abstract";
+        }
+    };
+
+    /** DocBook tag for <code>acronym</code>. */
+    Tag ACRONYM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "acronym";
+        }
+    };
+
+    /** DocBook tag for <code>affiliation</code>. */
+    Tag AFFILIATION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "affiliation";
+        }
+    };
+
+    /** DocBook tag for <code>anchor</code>. */
+    Tag ANCHOR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "anchor";
+        }
+    };
+
+    /** DocBook tag for <code>appendix</code>. */
+    Tag APPENDIX_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "appendix";
+        }
+    };
+
+    /** DocBook tag for <code>article</code>. */
+    Tag ARTICLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "article";
+        }
+    };
+
+    /** DocBook tag for <code>articleinfo</code>. */
+    Tag ARTICLEINFO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "articleinfo";
+        }
+    };
+
+    /** DocBook tag for <code>attribution</code>. */
+    Tag ATTRIBUTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "attribution";
+        }
+    };
+
+    /** DocBook tag for <code>audiodata</code>. */
+    Tag AUDIODATA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "audiodata";
+        }
+    };
+
+    /** DocBook tag for <code>audioobject</code>. */
+    Tag AUDIOOBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "audioobject";
+        }
+    };
+
+    /** DocBook tag for <code>author</code>. */
+    Tag AUTHOR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "author";
+        }
+    };
+
+    /** DocBook tag for <code>authorblurb</code>. */
+    Tag AUTHORBLURB_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "authorblurb";
+        }
+    };
+
+    /** DocBook tag for <code>authorgroup</code>. */
+    Tag AUTHORGROUP_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "authorgroup";
+        }
+    };
+
+    /** DocBook tag for <code>authorinitials</code>. */
+    Tag AUTHORINITIALS_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "authorinitials";
+        }
+    };
+
+    /** DocBook tag for <code>bibliodiv</code>. */
+    Tag BIBLIODIV_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bibliodiv";
+        }
+    };
+
+    /** DocBook tag for <code>bibliography</code>. */
+    Tag BIBLIOGRAPHY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bibliography";
+        }
+    };
+
+    /** DocBook tag for <code>bibliomisc</code>. */
+    Tag BIBLIOMISC_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bibliomisc";
+        }
+    };
+
+    /** DocBook tag for <code>bibliomixed</code>. */
+    Tag BIBLIOMIXED_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bibliomixed";
+        }
+    };
+
+    /** DocBook tag for <code>bibliomset</code>. */
+    Tag BIBLIOMSET_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bibliomset";
+        }
+    };
+
+    /** DocBook tag for <code>blockquote</code>. */
+    Tag BLOCKQUOTE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "blockquote";
+        }
+    };
+
+    /** DocBook tag for <code>caption</code>. */
+    Tag CAPTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "caption";
+        }
+    };
+
+    /** DocBook tag for <code>citetitle</code>. */
+    Tag CITETITLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "citetitle";
+        }
+    };
+
+    /** DocBook tag for <code>col</code>. */
+    Tag COL_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "col";
+        }
+    };
+
+    /** DocBook tag for <code>colgroup</code>. */
+    Tag COLGROUP_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "colgroup";
+        }
+    };
+
+    /** DocBook tag for <code>colspec</code>. */
+    Tag COLSPEC_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "colspec";
+        }
+    };
+
+    /** DocBook tag for <code>command</code>. */
+    Tag COMMAND_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "command";
+        }
+    };
+
+    /** DocBook tag for <code>computeroutput</code>. */
+    Tag COMPUTEROUTPUT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "computeroutput";
+        }
+    };
+
+    /** DocBook tag for <code>copyright</code>. */
+    Tag COPYRIGHT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "copyright";
+        }
+    };
+
+    /** DocBook tag for <code>corpauthor</code>. */
+    Tag CORPAUTHOR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "corpauthor";
+        }
+    };
+
+    /** DocBook tag for <code>date</code>. */
+    Tag DATE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "date";
+        }
+    };
+
+    /** DocBook tag for <code>edition</code>. */
+    Tag EDITION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "edition";
+        }
+    };
+
+    /** DocBook tag for <code>editor</code>. */
+    Tag EDITOR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "editor";
+        }
+    };
+
+    /** DocBook tag for <code>email</code>. */
+    Tag EMAIL_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "email";
+        }
+    };
+
+    /** DocBook tag for <code>emphasis</code>. */
+    Tag EMPHASIS_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "emphasis";
+        }
+    };
+
+    /** DocBook tag for <code>entry</code>. */
+    Tag ENTRY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "entry";
+        }
+    };
+
+    /** DocBook tag for <code>entrytbl</code>. */
+    Tag ENTRYTBL_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "entrytbl";
+        }
+    };
+
+    /** DocBook tag for <code>epigraph</code>. */
+    Tag EPIGRAPH_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "epigraph";
+        }
+    };
+
+    /** DocBook tag for <code>example</code>. */
+    Tag EXAMPLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "example";
+        }
+    };
+
+    /** DocBook tag for <code>figure</code>. */
+    Tag FIGURE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "figure";
+        }
+    };
+
+    /** DocBook tag for <code>filename</code>. */
+    Tag FILENAME_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "filename";
+        }
+    };
+
+    /** DocBook tag for <code>firstname</code>. */
+    Tag FIRSTNAME_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "firstname";
+        }
+    };
+
+    /** DocBook tag for <code>footnote</code>. */
+    Tag FOOTNOTE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "footnote";
+        }
+    };
+
+    /** DocBook tag for <code>footnoteref</code>. */
+    Tag FOOTNOTEREF_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "footnoteref";
+        }
+    };
+
+    /** DocBook tag for <code>holder</code>. */
+    Tag HOLDER_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "holder";
+        }
+    };
+
+    /** DocBook tag for <code>honorific</code>. */
+    Tag HONORIFIC_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "honorific";
+        }
+    };
+
+    /** DocBook tag for <code>imagedata</code>. */
+    Tag IMAGEDATA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "imagedata";
+        }
+    };
+
+    /** DocBook tag for <code>imageobject</code>. */
+    Tag IMAGEOBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "imageobject";
+        }
+    };
+
+    /** DocBook tag for <code>informaltable</code>. */
+    Tag INFORMALTABLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "informaltable";
+        }
+    };
+
+    /** DocBook tag for <code>inlinemediaobject</code>. */
+    Tag INLINEMEDIAOBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "inlinemediaobject";
+        }
+    };
+
+    /** DocBook tag for <code>issuenum</code>. */
+    Tag ISSUENUM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "issuenum";
+        }
+    };
+
+    /** DocBook tag for <code>itemizedlist</code>. */
+    Tag ITEMIZEDLIST_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "itemizedlist";
+        }
+    };
+
+    /** DocBook tag for <code>jobtitle</code>. */
+    Tag JOBTITLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "jobtitle";
+        }
+    };
+
+    /** DocBook tag for <code>keyword</code>. */
+    Tag KEYWORD_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "keyword";
+        }
+    };
+
+    /** DocBook tag for <code>keywordset</code>. */
+    Tag KEYWORDSET_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "keywordset";
+        }
+    };
+
+    /** DocBook tag for <code>legalnotice</code>. */
+    Tag LEGALNOTICE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "legalnotice";
+        }
+    };
+
+    /** DocBook tag for <code>lineage</code>. */
+    Tag LINEAGE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "lineage";
+        }
+    };
+
+    /** DocBook tag for <code>lineannotation</code>. */
+    Tag LINEANNOTATION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "lineannotation";
+        }
+    };
+
+    /** DocBook tag for <code>link</code>. */
+    Tag LINK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "link";
+        }
+    };
+
+    /** DocBook tag for <code>listitem</code>. */
+    Tag LISTITEM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "listitem";
+        }
+    };
+
+    /** DocBook tag for <code>literal</code>. */
+    Tag LITERAL_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "literal";
+        }
+    };
+
+    /** DocBook tag for <code>literallayout</code>. */
+    Tag LITERALLAYOUT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "literallayout";
+        }
+    };
+
+    /** DocBook tag for <code>mediaobject</code>. */
+    Tag MEDIAOBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "mediaobject";
+        }
+    };
+
+    /** DocBook tag for <code>note</code>. */
+    Tag NOTE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "note";
+        }
+    };
+
+    /** DocBook tag for <code>objectinfo</code>. */
+    Tag OBJECTINFO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "objectinfo";
+        }
+    };
+
+    /** DocBook tag for <code>option</code>. */
+    Tag OPTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "option";
+        }
+    };
+
+    /** DocBook tag for <code>orderedlist</code>. */
+    Tag ORDEREDLIST_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "orderedlist";
+        }
+    };
+
+    /** DocBook tag for <code>orgname</code>. */
+    Tag ORGNAME_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "orgname";
+        }
+    };
+
+    /** DocBook tag for <code>othercredit</code>. */
+    Tag OTHERCREDIT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "othercredit";
+        }
+    };
+
+    /** DocBook tag for <code>othername</code>. */
+    Tag OTHERNAME_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "othername";
+        }
+    };
+
+    /** DocBook tag for <code>para</code>. */
+    Tag PARA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "para";
+        }
+    };
+
+    /** DocBook tag for <code>phrase</code>. */
+    Tag PHRASE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "phrase";
+        }
+    };
+
+    /** DocBook tag for <code>programlisting</code>. */
+    Tag PROGRAMLISTING_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "programlisting";
+        }
+    };
+
+    /** DocBook tag for <code>pubdate</code>. */
+    Tag PUBDATE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "pubdate";
+        }
+    };
+
+    /** DocBook tag for <code>publishername</code>. */
+    Tag PUBLISHERNAME_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "publishername";
+        }
+    };
+
+    /** DocBook tag for <code>quote</code>. */
+    Tag QUOTE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "quote";
+        }
+    };
+
+    /** DocBook tag for <code>releaseinfo</code>. */
+    Tag RELEASEINFO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "releaseinfo";
+        }
+    };
+
+    /** DocBook tag for <code>replaceable</code>. */
+    Tag REPLACEABLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "replaceable";
+        }
+    };
+
+    /** DocBook tag for <code>revdescription</code>. */
+    Tag REVDESCRIPTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "revdescription";
+        }
+    };
+
+    /** DocBook tag for <code>revhistory</code>. */
+    Tag REVHISTORY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "revhistory";
+        }
+    };
+
+    /** DocBook tag for <code>revision</code>. */
+    Tag REVISION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "revision";
+        }
+    };
+
+    /** DocBook tag for <code>revnumber</code>. */
+    Tag REVNUMBER_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "revnumber";
+        }
+    };
+
+    /** DocBook tag for <code>revremark</code>. */
+    Tag REVREMARK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "revremark";
+        }
+    };
+
+    /** DocBook tag for <code>row</code>. */
+    Tag ROW_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "row";
+        }
+    };
+
+    /** DocBook tag for <code>section</code>. */
+    Tag SECTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "section";
+        }
+    };
+
+    /** DocBook tag for <code>sectioninfo</code>. */
+    Tag SECTIONINFO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "sectioninfo";
+        }
+    };
+
+    /** DocBook tag for <code>sidebar</code>. */
+    Tag SIDEBAR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "sidebar";
+        }
+    };
+
+    /** DocBook tag for <code>spanspec</code>. */
+    Tag SPANSPEC_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "spanspec";
+        }
+    };
+
+    /** DocBook tag for <code>subject</code>. */
+    Tag SUBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "subject";
+        }
+    };
+
+    /** DocBook tag for <code>subjectset</code>. */
+    Tag SUBJECTSET_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "subjectset";
+        }
+    };
+
+    /** DocBook tag for <code>subjectterm</code>. */
+    Tag SUBJECTTERM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "subjectterm";
+        }
+    };
+
+    /** DocBook tag for <code>subscript</code>. */
+    Tag SUBSCRIPT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "subscript";
+        }
+    };
+
+    /** DocBook tag for <code>subtitle</code>. */
+    Tag SUBTITLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "subtitle";
+        }
+    };
+
+    /** DocBook tag for <code>superscript</code>. */
+    Tag SUPERSCRIPT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "superscript";
+        }
+    };
+
+    /** DocBook tag for <code>surname</code>. */
+    Tag SURNAME_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "surname";
+        }
+    };
+
+    /** DocBook tag for <code>systemitem</code>. */
+    Tag SYSTEMITEM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "systemitem";
+        }
+    };
+
+    /** DocBook tag for <code>table</code>. */
+    Tag TABLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "table";
+        }
+    };
+
+    /** DocBook tag for <code>tbody</code>. */
+    Tag TBODY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tbody";
+        }
+    };
+
+    /** DocBook tag for <code>td</code>. */
+    Tag TD_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "td";
+        }
+    };
+
+    /** DocBook tag for <code>term</code>. */
+    Tag TERM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "term";
+        }
+    };
+
+    /** DocBook tag for <code>textdata</code>. */
+    Tag TEXTDATA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "textdata";
+        }
+    };
+
+    /** DocBook tag for <code>textobject</code>. */
+    Tag TEXTOBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "textobject";
+        }
+    };
+
+    /** DocBook tag for <code>tfoot</code>. */
+    Tag TFOOT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tfoot";
+        }
+    };
+
+    /** DocBook tag for <code>tgroup</code>. */
+    Tag TGROUP_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tgroup";
+        }
+    };
+
+    /** DocBook tag for <code>th</code>. */
+    Tag TH_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "th";
+        }
+    };
+
+    /** DocBook tag for <code>thead</code>. */
+    Tag THEAD_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "thead";
+        }
+    };
+
+    /** DocBook tag for <code>title</code>. */
+    Tag TITLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "title";
+        }
+    };
+
+    /** DocBook tag for <code>titleabbrev</code>. */
+    Tag TITLEABBREV_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "titleabbrev";
+        }
+    };
+
+    /** DocBook tag for <code>tr</code>. */
+    Tag TR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tr";
+        }
+    };
+
+    /** DocBook tag for <code>trademark</code>. */
+    Tag TRADEMARK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "trademark";
+        }
+    };
+
+    /** DocBook tag for <code>ulink</code>. */
+    Tag ULINK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "ulink";
+        }
+    };
+
+    /** DocBook tag for <code>userinput</code>. */
+    Tag USERINPUT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "userinput";
+        }
+    };
+
+    /** DocBook tag for <code>variablelist</code>. */
+    Tag VARIABLELIST_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "variablelist";
+        }
+    };
+
+    /** DocBook tag for <code>varlistentry</code>. */
+    Tag VARLISTENTRY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "varlistentry";
+        }
+    };
+
+    /** DocBook tag for <code>videodata</code>. */
+    Tag VIDEODATA_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "videodata";
+        }
+    };
+
+    /** DocBook tag for <code>videoobject</code>. */
+    Tag VIDEOOBJECT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "videoobject";
+        }
+    };
+
+    /** DocBook tag for <code>volumenum</code>. */
+    Tag VOLUMENUM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "volumenum";
+        }
+    };
+
+    /** DocBook tag for <code>xref</code>. */
+    Tag XREF_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "xref";
+        }
+    };
+
+    /** DocBook tag for <code>year</code>. */
+    Tag YEAR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "year";
+        }
+    };
+
+    // ----------------------------------------------------------------------
+    // Common Docbook attributes
+    // ----------------------------------------------------------------------
+
+    /** Common DocBook attribute <code>id</code>. */
+    String ID_ATTRIBUTE = "id";
+
+    /** Common DocBook attribute <code>lang</code>. */
+    String LANG_ATTRIBUTE = "lang";
+
+    /** Common DocBook attribute <code>remap</code>. */
+    String REMAP_ATTRIBUTE = "remap";
+
+    /** Common DocBook attribute <code>revisionflag</code>. */
+    String REVISIONFLAG_ATTRIBUTE = "revisionflag";
+
+    // ----------------------------------------------------------------------
+    // Specific Docbook attributes
+    // ----------------------------------------------------------------------
+
+    /** DocBook attribute <code>cols</code> used in <code>tgroup</code> tag. */
+    String COLS_ATTRIBUTE = "cols";
+
+    /** DocBook attribute <code>colsep</code> used in <code>informaltable</code> tag. */
+    String COLSEP_ATTRIBUTE = "colsep";
+
+    /** DocBook attribute <code>fileref</code> used in <code>orderedlist, imageobject</code> tag. */
+    String FILEREF_ATTRIBUTE = "fileref";
+
+    /** DocBook attribute <code>format</code> used in <code>imagedata</code> tag. */
+    String FORMAT_ATTRIBUTE = "format";
+
+    /** DocBook attribute <code>frame</code> used in <code>informaltable</code> tag. */
+    String FRAME_ATTRIBUTE = "frame";
+
+    /** DocBook attribute <code>linkend</code> used in <code>link</code> tag. */
+    String LINKEND_ATTRIBUTE = "linkend";
+
+    /** DocBook attribute <code>numeration</code> used in <code>orderedlist</code> tag. */
+    String NUMERATION_ATTRIBUTE = "numeration";
+
+    /** DocBook attribute <code>rowsep</code> used in <code>informaltable</code> tag. */
+    String ROWSEP_ATTRIBUTE = "rowsep";
+
+    /** DocBook attribute <code>url</code> used in <code>ulink</code> tag. */
+    String URL_ATTRIBUTE = "url";
+
+    // ----------------------------------------------------------------------
+    // Specific Docbook styles
+    // ----------------------------------------------------------------------
+
+    /** Docbook style <code>arabic</code> used in <code>numeration</code> attribute. */
+    String ARABIC_STYLE = "arabic";
+
+    /** DocBook style <code>loweralpha</code> used in <code>numeration</code> attribute. */
+    String LOWERALPHA_STYLE = "loweralpha";
+
+    /** DocBook style <code>lowerroman</code> used in <code>numeration</code> attribute. */
+    String LOWERROMAN_STYLE = "lowerroman";
+
+    /** DocBook style <code>upperalpha</code> used in <code>numeration</code> attribute. */
+    String UPPERALPHA_STYLE = "upperalpha";
+
+    /** DocBook style <code>upperroman</code> used in <code>numeration</code> attribute. */
+    String UPPERROMAN_STYLE = "upperroman";
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/site/site.xml b/doxia-modules/doxia-module-docbook-simple/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookIdentityTest.java b/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookIdentityTest.java
new file mode 100644
index 0000000..55c07ce
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookIdentityTest.java
@@ -0,0 +1,46 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.module.AbstractIdentityTest;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.Sink;
+
+
+/**
+ * Check that piping a full model through a DocBookParser and a DocBookSink
+ * leaves the model unchanged. The test is done in AbstractIdentityTest.
+ */
+public class DocBookIdentityTest extends AbstractIdentityTest
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new DocBookSink( writer, "UTF-8" );
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return new DocBookParser();
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookParserTest.java b/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookParserTest.java
new file mode 100644
index 0000000..a755b90
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookParserTest.java
@@ -0,0 +1,407 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+import java.util.Iterator;
+
+import org.apache.maven.doxia.parser.AbstractParserTest;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * @author <a href="mailto:lars at trieloff.net">Lars Trieloff</a>
+ * @version $Id: DocBookParserTest.java 784739 2009-06-15 11:49:18Z ltheussl $
+ */
+public class DocBookParserTest extends AbstractParserTest
+{
+    /** The parser to test. */
+    private DocBookParser parser;
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        parser = (DocBookParser) lookup( Parser.ROLE, "doc-book" );
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return parser;
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "xml";
+    }
+
+    /**
+     * Parses the test document test.xml and re-emits it into test.docbook.
+     *
+     * @throws IOException if something goes wrong
+     * @throws ParseException if something goes wrong
+     */
+    public void testTestDocument()
+        throws IOException, ParseException
+    {
+        Writer writer = null;
+        Reader reader = null;
+
+        try
+        {
+            writer = getTestWriter( "test", "docbook" );
+            reader = getTestReader( "test" );
+
+            Sink sink = new DocBookSink( writer );
+
+            createParser().parse( reader, sink );
+
+            writer = getTestWriter( "sdocbook_full", "docbook" );
+            reader = getTestReader( "sdocbook_full" );
+
+            sink = new DocBookSink( writer );
+
+            createParser().parse( reader, sink );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+            IOUtil.close( writer );
+        }
+    }
+
+    /** @throws Exception  */
+    public void testSignificantWhiteSpace()
+        throws Exception
+    {
+        // NOTE significant white space
+        String text = "<para><command>word</command> <emphasis>word</emphasis></para>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+
+        SinkEventElement el = (SinkEventElement) it.next();
+        assertEquals( "text", el.getName() );
+        assertEquals( " ",  (String) el.getArgs()[0] );
+
+        assertEquals( "italic", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+
+        // same test with EOL
+        text = "<para><command>word</command>" + EOL + "<emphasis>word</emphasis></para>";
+
+        sink.reset();
+        parser.parse( text, sink );
+        it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+
+        el = (SinkEventElement) it.next();
+        assertEquals( "text", el.getName() );
+        assertEquals( EOL,  (String) el.getArgs()[0] );
+
+        assertEquals( "italic", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testIds()
+        throws Exception
+    {
+        final String text = "<article id=\"article\"><section id=\"section\">"
+                + "<title id=\"title\">Title</title><para id=\"para\">Paragraph</para></section></article>";
+
+        final SinkEventTestingSink sink = new SinkEventTestingSink();
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "head", event.getName() );
+        assertEquals( " id=article", event.getArgs()[0].toString() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "section1", event.getName() );
+        assertEquals( " id=section", event.getArgs()[0].toString() );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "sectionTitle1", event.getName() );
+        assertEquals( " id=title", event.getArgs()[0].toString() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "paragraph", event.getName() );
+        assertEquals( " id=para", event.getArgs()[0].toString() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testFigure()
+        throws Exception
+    {
+        String text = "<mediaobject><imageobject>"
+                + "<imagedata fileref=\"fileref\" format=\"PNG\" /></imageobject>"
+                + "<caption><para>Figure caption</para></caption></mediaobject>";
+
+        final SinkEventTestingSink sink = new SinkEventTestingSink();
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "figure", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figureGraphics", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figureCaption", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figureCaption_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figure_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        sink.reset();
+        text = "<figure><title>Title</title><mediaobject><imageobject>"
+                + "<imagedata fileref=\"fileref\" format=\"PNG\"/></imageobject>"
+                + "<textobject><phrase>text</phrase></textobject></mediaobject></figure>";
+        parser.parse( text, sink );
+        it = sink.getEventList().iterator();
+
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figure", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figureGraphics", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "figure_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testLinks()
+        throws Exception
+    {
+        final String text = "<para><anchor id=\"Anchor\" />Anchor<!-- anchor_end -->."
+                + "Link to <link linkend=\"Anchor\">Anchor</link>."
+                + "Link to <ulink url=\"url.com\">url.com</ulink>.</para>";
+
+        final SinkEventTestingSink sink = new SinkEventTestingSink();
+        parser.parse( text, sink );
+        final Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "anchor", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "link", event.getName() );
+        assertEquals( "#Anchor", event.getArgs()[0].toString() );
+
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+
+        event = (SinkEventElement) it.next();
+        assertEquals( "link", event.getName() );
+        assertEquals( "url.com", event.getArgs()[0].toString() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testStyles()
+        throws Exception
+    {
+        final String text = "<para><emphasis>Italic</emphasis><emphasis role=\"bold\">Bold</emphasis>"
+                + "<literal>Monospaced</literal></para>";
+
+        final SinkEventTestingSink sink = new SinkEventTestingSink();
+        parser.parse( text, sink );
+        final Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "italic_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "monospaced", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "monospaced_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testLists()
+        throws Exception
+    {
+        String text = "<itemizedlist><listitem><para>item</para></listitem></itemizedlist>";
+
+        final SinkEventTestingSink sink = new SinkEventTestingSink();
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "list", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "listItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "list_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        text = "<orderedlist numeration=\"upperalpha\"><listitem><para>item</para></listitem></orderedlist>";
+        sink.reset();
+        parser.parse( text, sink );
+        it = sink.getEventList().iterator();
+
+        SinkEventElement event = (SinkEventElement) it.next();
+        assertEquals( "numberedList", event.getName() );
+        assertEquals( Sink.NUMBERING_UPPER_ALPHA, ( (Integer) event.getArgs()[0] ).intValue() );
+        assertEquals( "numberedListItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedListItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedList_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        text = "<variablelist><varlistentry><term>term</term><listitem><para>definition</para>"
+                + "</listitem></varlistentry></variablelist>";
+        sink.reset();
+        parser.parse( text, sink );
+        it = sink.getEventList().iterator();
+
+        assertEquals( "definitionList", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionListItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definedTerm", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definedTerm_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definition", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definition_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionListItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionList_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testTables()
+        throws Exception
+    {
+        String text = "<informaltable frame=\"none\"><tgroup cols=\"2\">"
+                + "<colspec colwidth=\"0.5in\"/><colspec colwidth=\"0.5in\"/>"
+                + "<thead><row><entry>head 1</entry><entry>head 2</entry></row></thead>"
+                + "<tbody><row><entry>1</entry><entry>2</entry></row></tbody></tgroup></informaltable>";
+
+        final SinkEventTestingSink sink = new SinkEventTestingSink();
+        parser.parse( text, sink );
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "table", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableHeaderCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableHeaderCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableHeaderCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableHeaderCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableCell_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRow_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "tableRows_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "table_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testHead()
+        throws Exception
+    {
+        String text = "<article><articleinfo><title>Title</title>"
+                + "<corpauthor>CorpAuthor</corpauthor><date>Date</date></articleinfo>"
+                + "<para>Paragraph</para></article>";
+
+        final SinkEventTestingSink sink = new SinkEventTestingSink();
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "author", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "author_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "date", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "date_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookSinkTest.java b/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookSinkTest.java
new file mode 100644
index 0000000..b4aabd1
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocBookSinkTest.java
@@ -0,0 +1,259 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import java.util.Locale;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkUtils;
+import org.apache.maven.doxia.util.DoxiaUtils;
+
+import org.codehaus.plexus.util.FileUtils;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: DocBookSinkTest.java 784100 2009-06-12 12:40:51Z ltheussl $
+ */
+public class DocBookSinkTest extends AbstractSinkTest
+{
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "docbook";
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new DocBookSink( writer, "UTF-8" );
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        return "<articleinfo><title>" + title + "</title>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        return "<corpauthor>" + author + "</corpauthor>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        return "<date>" + date + "</date>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE article PUBLIC \""
+                + SimplifiedDocbookMarkup.DEFAULT_XML_PUBLIC_ID + "\" "
+                + "\"" + SimplifiedDocbookMarkup.DEFAULT_XML_SYSTEM_ID + "\"><article>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return "</article>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return "<title>" + title + "</title>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return "<section><title>" + title + "</title>" + "</section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return "<section><title>" + title + "</title>" + "</section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        return "<section><title>" + title + "</title>" + "</section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        return "<section><title>" + title + "</title>" + "</section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        return "<section><title>" + title + "</title>" + "</section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return "<itemizedlist><listitem><para>" + item  + "</para></listitem>" + "</itemizedlist>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return "<orderedlist numeration=\"lowerroman\"><listitem><para>"
+            + item  + "</para></listitem>" + "</orderedlist>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return "<variablelist><varlistentry><term>" + definum
+            + "</term>" + "<listitem><para>" + definition
+            + "</para></listitem>" + "</varlistentry>" + "</variablelist>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        String format = FileUtils.extension( source ).toUpperCase( Locale.ENGLISH );
+
+        return "<mediaobject><imageobject>"
+                + "<imagedata fileref=\"" + source + "\" format=\"" + format + "\" />"
+                + "</imageobject><caption><para>" + caption + "</para></caption></mediaobject>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        // Using the same set ordering than the JVM
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( "frame", "none" );
+        att.addAttribute( "rowsep", "0" );
+        att.addAttribute( "colsep", "0" );
+
+        return "<table" + SinkUtils.getAttributeString( att ) + "><title>" + caption
+            + "</title>" + "<tgroup cols=\"1\"><colspec align=\"center\" />" + "<tbody><row><entry>"
+            + cell  + "</entry>" + "</row>" + "</tbody></tgroup>" + "</table>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        return "<para>" + text + "</para>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        return "<programlisting>" + text + "</programlisting>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return "<!-- HR -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return "<!-- PB -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        return "<anchor id=\"" + anchor + "\" />" + anchor + "<!-- anchor_end -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        String linkend = DoxiaUtils.isInternalLink( link ) ? link.substring( 1 ) : link;
+        return "<link linkend=\"" + linkend + "\">" + text + "</link>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        return "<emphasis>" + text + "</emphasis>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        return "<emphasis role=\"bold\">" + text + "</emphasis>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        return "<literal>" + text + "</literal>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return "<!-- LB -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return "&#x00A0;";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        // TODO: retreive those from the sink
+        return "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        // TODO
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "<!-- Simple comment with - - - - -->";
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocbookUtilsTest.java b/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocbookUtilsTest.java
new file mode 100644
index 0000000..1c10d38
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/test/java/org/apache/maven/doxia/module/docbook/DocbookUtilsTest.java
@@ -0,0 +1,132 @@
+package org.apache.maven.doxia.module.docbook;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import junit.framework.TestCase;
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Test DocbookUtils.
+ *
+ * @author ltheussl
+ * @version $Id: DocbookUtilsTest.java 784651 2009-06-15 04:54:34Z ltheussl $
+ */
+public class DocbookUtilsTest
+        extends TestCase
+{
+    /**
+     * Test of doxiaTableFrameAttribute method, of class DocbookUtils.
+     */
+    public void testDoxiaTableFrameAttribute()
+    {
+        assertEquals( "box", DocbookUtils.doxiaTableFrameAttribute( "all" ) );
+        assertEquals( "below", DocbookUtils.doxiaTableFrameAttribute( "bottom" ) );
+        assertEquals( "void", DocbookUtils.doxiaTableFrameAttribute( "none" ) );
+        assertEquals( "vsides", DocbookUtils.doxiaTableFrameAttribute( "sides" ) );
+        assertEquals( "above", DocbookUtils.doxiaTableFrameAttribute( "top" ) );
+        assertEquals( "hsides", DocbookUtils.doxiaTableFrameAttribute( "topbot" ) );
+
+        try
+        {
+            DocbookUtils.doxiaTableFrameAttribute( "" );
+            fail();
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+    /**
+     * Test of doxiaListNumbering method, of class DocbookUtils.
+     */
+    public void testDoxiaListNumbering()
+    {
+        assertEquals( Sink.NUMBERING_LOWER_ALPHA,
+                DocbookUtils.doxiaListNumbering( SimplifiedDocbookMarkup.LOWERALPHA_STYLE ) );
+        assertEquals( Sink.NUMBERING_LOWER_ROMAN,
+                DocbookUtils.doxiaListNumbering( SimplifiedDocbookMarkup.LOWERROMAN_STYLE ) );
+        assertEquals( Sink.NUMBERING_UPPER_ALPHA,
+                DocbookUtils.doxiaListNumbering( SimplifiedDocbookMarkup.UPPERALPHA_STYLE ) );
+        assertEquals( Sink.NUMBERING_UPPER_ROMAN,
+                DocbookUtils.doxiaListNumbering( SimplifiedDocbookMarkup.UPPERROMAN_STYLE ) );
+        assertEquals( Sink.NUMBERING_DECIMAL,
+                DocbookUtils.doxiaListNumbering( SimplifiedDocbookMarkup.ARABIC_STYLE ) );
+
+        try
+        {
+            DocbookUtils.doxiaListNumbering( "" );
+            fail();
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+    /**
+     * Test of docbookListNumbering method, of class DocbookUtils.
+     */
+    public void testDocbookListNumbering()
+    {
+        assertEquals( SimplifiedDocbookMarkup.UPPERALPHA_STYLE,
+                DocbookUtils.docbookListNumbering( Sink.NUMBERING_UPPER_ALPHA ) );
+        assertEquals( SimplifiedDocbookMarkup.LOWERALPHA_STYLE,
+                DocbookUtils.docbookListNumbering( Sink.NUMBERING_LOWER_ALPHA ) );
+        assertEquals( SimplifiedDocbookMarkup.UPPERROMAN_STYLE,
+                DocbookUtils.docbookListNumbering( Sink.NUMBERING_UPPER_ROMAN ) );
+        assertEquals( SimplifiedDocbookMarkup.LOWERROMAN_STYLE,
+                DocbookUtils.docbookListNumbering( Sink.NUMBERING_LOWER_ROMAN ) );
+        assertEquals( SimplifiedDocbookMarkup.ARABIC_STYLE,
+                DocbookUtils.docbookListNumbering( Sink.NUMBERING_DECIMAL ) );
+
+        try
+        {
+            DocbookUtils.docbookListNumbering( -1 );
+            fail();
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+    /**
+     * Test of trademarkFromClass method, of class DocbookUtils.
+     */
+    public void testTrademarkFromClass()
+    {
+        assertEquals( '\u00AE', DocbookUtils.trademarkFromClass( "registered" ) );
+        assertEquals( '\u00A9', DocbookUtils.trademarkFromClass( "copyright" ) );
+        assertEquals( '\u2120', DocbookUtils.trademarkFromClass( "service" ) );
+        assertEquals( '\u2122', DocbookUtils.trademarkFromClass( "trade" ) );
+
+        try
+        {
+            DocbookUtils.trademarkFromClass( "" );
+            fail();
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-docbook-simple/src/test/resources/book.xml b/doxia-modules/doxia-module-docbook-simple/src/test/resources/book.xml
new file mode 100644
index 0000000..ea2514a
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/test/resources/book.xml
@@ -0,0 +1,134 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<chapter id="index">
+  <section id="intro">
+    <title>Introduction</title>
+
+    <para>This document aims to help new geotools developers get up to speed
+    and track practices of existing developers in order to keep the project
+    consistent.</para>
+
+    <para>If you feel something is missing, feel free to contribute a new
+    section.</para>
+  </section>
+
+  <section id="design">
+    <title>Design</title>
+    <figure>
+        <title>Geotools Logo</title>
+        <mediaobject>
+            <imageobject>
+                <imagedata fileref="images/developersguide/geotools_logo.png"
+                    format="PNG"/>
+                </imageobject>
+            <textobject><phrase>Geotools Logo</phrase></textobject>
+        </mediaobject>
+    </figure>
+
+    <para>To do: Explain</para><itemizedlist><listitem><para>Key design structure
+    (core interfaces, data sources, rendering, tools)</para></listitem><listitem><para>Design
+    Drivers (Our design goals: modular, derives structure from netbeans,
+    support multiple version of java, ..)</para></listitem><listitem><para>Directory
+    structure</para></listitem><listitem><para></para></listitem></itemizedlist>
+  </section>
+
+  <section id="development">
+    <title>Development</title>
+
+    <section id="building">
+      <title>Building</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>using ant</para></listitem><listitem><para>what
+      ant checks</para></listitem><listitem><para>insert the
+      "howto-build.html" doc into here</para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section id="cvs">
+      <title>Code Versioning (using CVS)</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Point to sourceforge CVS howto
+      links.</para></listitem><listitem><para>Also point to some HOWTOs for
+      windows users.</para></listitem><listitem><para>What is acceptable to
+      commit to CVS, what is not. (Ie, code should compile)</para></listitem><listitem><para>Talk
+      about version numbering convention, use of branches, etc (if we decide
+      to do this).</para></listitem></itemizedlist>
+
+      <para>To do:</para>
+    </section>
+
+    <section id="CodingStandards">
+      <title>Coding Standards</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Using Sun's coding
+      standard, need to note any exceptions to it.</para></listitem><listitem><para>Note
+      the auto checking of coding standards.</para></listitem><listitem><para>Javadocs</para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section id="templates">
+      <title>Template File</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Provide a template file.</para></listitem><listitem><para>Link
+      it to sections explaining the different parts of the template.</para></listitem><listitem><para>Include
+      headers, footers, example logging, example coding style, CVS key tags,
+      sample javadocs.</para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section id="logging">
+      <title>Logging</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Talk about log4j, links to</para></listitem><listitem><para></para></listitem><listitem><para></para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section id="testing">
+      <title>Testing</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Explain use of junit. Might be
+      a link to another web site.</para></listitem><listitem><para></para></listitem><listitem><para></para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section>
+      <title>Documentation</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Talk about docbook, why we
+      decided to use it (most commonly used format of LDP, converts to
+      multiple formats, tools coming which can support it. Need to mention
+      which version of docbook we are using.</para></listitem><listitem><para>Mention
+      tools that can be used.</para></listitem><listitem><para>Links to
+      docbook references.</para></listitem><listitem><para>Process for
+      updating this document.</para></listitem><listitem><para>How to use ant
+      tools to build doc.</para></listitem></itemizedlist>
+    </section>
+  </section>
+
+  <section id="tools">
+    <title>Development Tools</title>
+
+    <section id="netbeans">
+      <title>Netbeans</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Where to get it.</para></listitem><listitem><para>Aim
+      is to make geotools a module within Netbeans</para></listitem><listitem><para>How
+      to setup getools to fit the multiple directory structure</para></listitem><listitem><para>The
+      extra Netbeans modules that are useful and should be downloaded.</para></listitem></itemizedlist>
+    </section>
+  </section>
+</chapter>
+
diff --git a/doxia-modules/doxia-module-docbook-simple/src/test/resources/sdocbook_full.xml b/doxia-modules/doxia-module-docbook-simple/src/test/resources/sdocbook_full.xml
new file mode 100644
index 0000000..f2e4bf8
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/test/resources/sdocbook_full.xml
@@ -0,0 +1,544 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V1.1//EN" "http://www.oasis-open.org/docbook/xml/simple/1.1/sdocbook.dtd">
+
+<article>
+
+  <articleinfo>
+
+    <title>Title</title>
+    <subtitle>Or How I Learned to Live</subtitle>
+    <citetitle>citetitle</citetitle>
+    <!-- Abbreviated titles are usually used only in specific contexts, such as headers and footers, and suppressed everywhere else. -->
+    <titleabbrev>Configuring Menus</titleabbrev>
+
+    <corpauthor>CorpAuthor</corpauthor><!-- May be formatted inline or as a displayed block, depending on context. Sometimes suppressed. -->
+
+    <date>Date</date><!-- DocBook does not specify the format of the date. -->
+
+    <abbrev>Assn.</abbrev>
+
+    <abstract><!-- Formatted as a displayed block. Sometimes suppressed. Often presented in alternate outputs. -->
+      <title>articleinfo Abstract Title</title>
+      <para>articleinfo Abstract Content</para>
+    </abstract>
+
+    <author><!-- May be formatted inline or as a displayed block, depending on context. Sometimes suppressed. -->
+      <honorific>PhD</honorific>
+      <firstname>Main</firstname>
+      <surname>Author</surname>
+      <lineage>Sr.</lineage>
+      <othername>D</othername>
+      <affiliation>
+        <jobtitle>Director of Cooperative Efforts</jobtitle>
+        <orgname>Cooperative Engineering</orgname>
+      </affiliation>
+      <authorblurb>
+        <title>Title</title>
+        <para>super hero.</para>
+      </authorblurb>
+    </author>
+
+    <honorific>Dr</honorific>
+    <firstname>Peter</firstname>
+    <surname>Doe</surname>
+    <lineage>Sr.</lineage>
+    <othername>Spiderman</othername>
+    <affiliation>
+      <jobtitle>Director of Cooperative Efforts</jobtitle>
+      <orgname>Cooperative Engineering</orgname>
+    </affiliation>
+    <authorblurb>
+      <title>Title</title>
+      <para>super hero.</para>
+    </authorblurb>
+
+    <authorgroup>
+      <corpauthor>Authorgroup CorpAuthor</corpauthor><!-- May be formatted inline or as a displayed block, depending on context. Sometimes suppressed. -->
+
+      <author><!-- May be formatted inline or as a displayed block, depending on context. Sometimes suppressed. -->
+        <honorific>Dr</honorific>
+        <firstname>Authorgroup</firstname>
+        <surname>Author</surname>
+        <lineage>Sr.</lineage>
+        <othername>D</othername>
+        <affiliation>
+          <jobtitle>Director of Cooperative Efforts</jobtitle>
+          <orgname>Cooperative Engineering</orgname>
+        </affiliation>
+        <authorblurb>
+          <title>authorblurb title</title>
+          <para>He's a super hero in his spare time.</para>
+        </authorblurb>
+      </author>
+
+      <editor><!-- May be formatted inline or as a displayed block, depending on context. Sometimes suppressed. -->
+        <honorific>Dr</honorific>
+        <firstname>Authorgroup</firstname>
+        <surname>Editor</surname>
+        <lineage>Sr.</lineage>
+        <othername>Spiderman</othername>
+        <affiliation>
+          <jobtitle>Director of Cooperative Efforts</jobtitle>
+          <orgname>Cooperative Engineering</orgname>
+        </affiliation>
+        <authorblurb>
+          <title>authorblurb title</title>
+          <para>He's a super hero in his spare time.</para>
+        </authorblurb>
+      </editor>
+
+      <othercredit><!-- May be formatted inline or as a displayed block, depending on context. Sometimes suppressed. -->
+        <honorific>Dr</honorific>
+        <firstname>Authorgroup</firstname>
+        <surname>OtherCredit</surname>
+        <lineage>Sr.</lineage>
+        <othername>AlterEgo</othername>
+        <affiliation>
+          <jobtitle>Director of Cooperative Efforts</jobtitle>
+          <orgname>Cooperative Engineering</orgname>
+        </affiliation>
+        <authorblurb>
+          <title>authorblurb title</title>
+          <para>He's a super hero in his spare time.</para>
+        </authorblurb>
+      </othercredit>
+    </authorgroup>
+
+    <bibliomisc role="url"><ulink url="http://www.net/"/></bibliomisc>
+
+    <copyright>
+      <year>1996</year>
+      <year>1997</year>
+      <holder>me</holder>
+    </copyright>
+
+    <edition>Edition</edition>
+
+    <editor><!-- May be formatted inline or as a displayed block, depending on context. Sometimes suppressed. -->
+      <honorific>Dr</honorific>
+      <firstname>Peter</firstname>
+      <surname>Parker</surname>
+      <lineage>Sr.</lineage>
+      <othername>Spiderman</othername>
+      <affiliation>
+        <jobtitle>Director of Cooperative Efforts</jobtitle>
+        <orgname>Cooperative Engineering</orgname>
+      </affiliation>
+      <authorblurb>
+        <title>Title</title>
+        <para>Peter's a super hero in his spare time.</para>
+      </authorblurb>
+    </editor>
+
+    <keywordset>
+      <keyword>key1</keyword>
+      <keyword>key2</keyword>
+    </keywordset>
+
+    <mediaobject>
+      <textobject><phrase>The Eiffel Tower</phrase></textobject>
+    </mediaobject>
+
+    <othercredit><!-- May be formatted inline or as a displayed block, depending on context. Sometimes suppressed. -->
+      <honorific>Dr</honorific>
+      <firstname>John</firstname>
+      <surname>Doe</surname>
+      <lineage>Sr.</lineage>
+      <othername>Spiderman</othername>
+      <affiliation>
+        <jobtitle>Director of Cooperative Efforts</jobtitle>
+        <orgname>Cooperative Engineering</orgname>
+      </affiliation>
+      <authorblurb>
+        <title>Title</title>
+        <para>Peter's a super hero in his spare time.</para>
+      </authorblurb>
+    </othercredit>
+
+    <pubdate>1994</pubdate>
+    <publishername>The TeX User's Group</publishername>
+    <releaseinfo>beta</releaseinfo>
+    <volumenum>15</volumenum>
+    <issuenum>3</issuenum>
+    <legalnotice><para>No notice is required.</para></legalnotice>
+
+    <revhistory>
+      <revision>
+        <revnumber>0.91</revnumber>
+        <date>11 Dec 1996</date>
+        <authorinitials>ndw</authorinitials>
+        <revremark>Bug fixes</revremark>
+      </revision>
+      <revision>
+        <revnumber>0.90</revnumber>
+        <date>30 Nov 1996</date>
+        <authorinitials>ndw</authorinitials>
+        <revdescription><para>long write up</para></revdescription>
+      </revision>
+    </revhistory>
+
+    <subjectset scheme="libraryofcongress">
+      <subject><subjectterm>Electronic Publishing</subjectterm></subject>
+      <subject><subjectterm>SGML (Computer program language)</subjectterm></subject>
+    </subjectset>
+
+  </articleinfo>
+
+
+  <abstract>
+    <title>Abstract Title</title>
+    <para>In brief.</para>
+  </abstract>
+
+  <section>
+    <sectioninfo>
+      <abstract><para>A trivial example of recursive sections.</para></abstract>
+    </sectioninfo>
+    <title>Section title</title>
+    <section><title>Sub-section title</title>
+      <section><title>Sub-sub-section title</title>
+        <section><title>Sub-sub-sub-section title</title>
+          <section id="ch02"><title>Sub-sub-sub-sub-section title</title>
+
+  <epigraph>
+    <attribution>William Shakespeare</attribution>
+    <literallayout>Epigraph: What say you?</literallayout>
+  </epigraph>
+
+  <para>
+    The <abbrev>Assn.</abbrev> of Computing Machinery
+    has published <citetitle pubwork="book">Developing SGML DTDs</citetitle>.
+  </para>
+
+  <para>
+    In <acronym>UNIX</acronym> command <command>ls</command>
+    with the <option>-a</option> option is used to get a directory listing.
+  </para>
+
+  <para>
+    The output from the date command is eg
+    <computeroutput>Sun  Nov 16, 1997  21:03:29</computeroutput>.
+  </para>
+
+  <para>
+    An email address is eg <email>yo at tu.com</email>.
+  </para>
+
+  <para>
+    The symbolic constants for error numbers are defined in
+    <filename class="headerfile">errno.h</filename> in
+    <filename class="directory">/usr/include/sys</filename>.
+      </para>
+
+  <para>
+    This software is provided <quote>as is</quote>.
+  </para>
+
+  <para>
+    Execute the command with
+    <command>command</command>
+    <option>options</option>
+    <replaceable>filename</replaceable>.
+  </para>
+
+  <para>
+    This is hosted at <systemitem class="systemname">helio.com</systemitem>.
+    The name <trademark>pipipo</trademark> is traded.
+    The name <trademark class="registered">pipipo</trademark> is registered.
+    The name <trademark class="copyright">pipipo</trademark> is copyrighted.
+    The name <trademark class="service">pipipo</trademark> is serviced.
+    The name <trademark class="trade">pipipo</trademark> is traded.
+  </para>
+
+  <para>
+    At the system prompt, enter <userinput>xyzzy</userinput>.
+  </para>
+
+  <sidebar>
+    <title>A Sidebar</title>
+    <para>Sidebar content.</para>
+  </sidebar>
+
+  <para>
+    A link to an element with an XRefLabel: <xref linkend="ch03"/>.
+    A straight link generates the cross-reference text: <xref linkend="ch02"/>.
+  </para>
+
+  <mediaobject>
+    <audioobject>
+      <objectinfo><title>Phaser sound effect</title></objectinfo>
+      <audiodata fileref="phaser.wav"/>
+    </audioobject>
+    <textobject><phrase>A sound effect.</phrase></textobject>
+  </mediaobject>
+
+  <mediaobject>
+    <videoobject><videodata fileref="movie.avi"/></videoobject>
+  </mediaobject>
+
+  <para>
+    <emphasis>Italic</emphasis> font.
+    <emphasis role="bold">Bold</emphasis> font.
+    <literal>Monospaced</literal> font.
+  </para>
+
+  <example>
+    <title>A Real Function</title>
+    <programlisting>
+      real function()<lineannotation>Error!</lineannotation>
+    </programlisting>
+  </example>
+
+  <blockquote>
+    <attribution>William Shakespeare</attribution>
+    <literallayout>Blockquote: What say you?</literallayout>
+  </blockquote>
+
+  <para>
+    An annual percentage rate of
+    <literal>3.27</literal>%<footnote id="footnote"><para>The prime rate.</para></footnote>
+    will be charged on all balances<footnoteref linkend="footnote"/>.
+  </para>
+
+  <itemizedlist>
+    <listitem><para>List item 1.</para></listitem>
+    <listitem><para>List item 2.</para><para>Paragraph contained in list item 2.</para>
+    <itemizedlist>
+      <listitem><para>Sub-list item 1.</para></listitem>
+      <listitem><para>Sub-list item 2.</para></listitem>
+    </itemizedlist>
+    </listitem>
+    <listitem><para>List item 3. Force end of list:</para></listitem>
+  </itemizedlist>
+
+  <programlisting>Verbatim text not contained in list item 3</programlisting>
+
+  <orderedlist numeration="arabic">
+    <listitem><para>Numbered item 1.</para>
+    <orderedlist numeration="upperalpha">
+      <listitem><para>Numbered item A.</para></listitem>
+      <listitem><para>Numbered item B.</para></listitem>
+    </orderedlist>
+    </listitem>
+    <listitem><para>Numbered item 2.</para></listitem>
+  </orderedlist>
+
+  <para>List numbering schemes: [[1]], [[a]], [[A]], [[i]], [[I]].</para>
+
+  <variablelist>
+    <varlistentry><term>Defined term 1</term><listitem><para>of definition list.</para></listitem></varlistentry>
+    <varlistentry><term>Defined term 2</term><listitem><para>of definition list.</para><programlisting>Verbatim text
+              in a box        </programlisting></listitem></varlistentry>
+  </variablelist>
+
+  <figure>
+    <title>Figure title</title>
+    <mediaobject>
+      <imageobject><imagedata fileref="figure.png" format="JPEG" /></imageobject>
+      <caption id="ch03" xreflabel="Chapter the Third"><para>Figure caption</para></caption>
+    </mediaobject>
+  </figure>
+
+<para>
+  Here is an inline image: <inlinemediaobject><imageobject><imagedata fileref="figure.png"/></imageobject></inlinemediaobject>.
+</para>
+
+  <table frame="all" rowsep="1" colsep="1">
+    <title>Table caption</title>
+    <tgroup cols="3">
+      <colspec align="center" /><colspec align="left" /><colspec align="right" />
+      <tbody>
+        <row>
+          <entry>Centered<!-- LB -->cell 1,1</entry>
+          <entry>Left-aligned<!-- LB -->cell 1,2</entry>
+          <entry>Right-aligned<!-- LB -->cell 1,3</entry>
+        </row>
+        <row>
+          <entry>cell 2,1</entry>
+          <entry>cell 2,2</entry>
+          <entry>cell 2,3</entry>
+        </row>
+      </tbody>
+    </tgroup>
+  </table>
+
+  <para>No grid, no caption:</para>
+
+  <informaltable frame="none" rowsep="0" colsep="0">
+    <tgroup cols="2">
+      <colspec align="center" /><colspec align="center" />
+      <tbody>
+        <row>
+          <entry>cell</entry>
+          <entry>cell</entry>
+        </row>
+        <row>
+          <entry>cell</entry>
+          <entry>cell</entry>
+        </row>
+      </tbody>
+    </tgroup>
+  </informaltable>
+
+  <informaltable frame="all" rowsep="1" colsep="1">
+    <tgroup cols="2">
+      <colspec align="center" /><colspec align="center" />
+      <thead>
+        <row>
+          <entry>header</entry>
+          <entry>header</entry>
+        </row>
+      </thead>
+      <tbody>
+        <row>
+          <entry>cell</entry>
+          <entry>cell</entry>
+        </row>
+      </tbody>
+    </tgroup>
+  </informaltable>
+
+  <informaltable frame="all">
+    <tgroup cols="5" align="left" colsep="1" rowsep="1">
+      <colspec colname="c1"/>
+      <colspec colname="c2"/>
+      <colspec colname="c3"/>
+      <colspec colnum="5" colname="c5"/>
+      <thead>
+        <row>
+          <entry namest="c1" nameend="c2" align="center">Horizontal Span</entry>
+          <entry>a3</entry><entry>a4</entry><entry>a5</entry>
+        </row>
+      </thead>
+      <tfoot>
+        <row>
+          <entry>f1</entry><entry>f2</entry><entry>f3</entry><entry>f4</entry><entry>f5</entry>
+        </row>
+      </tfoot>
+      <tbody>
+        <row>
+          <entry>b1</entry><entry>b2</entry><entry>b3</entry><entry>b4</entry>
+          <entry morerows="1" valign="middle"><para>  <!-- Pernicous Mixed Content -->
+          Vertical Span</para></entry>
+        </row>
+        <row>
+          <entry>c1</entry>
+          <entry namest="c2" nameend="c3" align="center" morerows="1" valign="bottom">Span Both</entry>
+          <entry>c4</entry>
+        </row>
+        <row>
+          <entry>d1</entry><entry>d4</entry><entry>d5</entry>
+        </row>
+      </tbody>
+    </tgroup>
+  </informaltable>
+
+  <informaltable frame="all">
+    <tgroup cols="5" align="left" colsep="1" rowsep="1">
+      <colspec colname="c1"/>
+      <colspec colname="c2"/>
+      <colspec colname="c3"/>
+      <colspec colname="c4"/>
+      <colspec colname="c5"/>
+      <spanspec spanname="span" namest="c2" nameend="c4"/>
+      <tbody>
+        <row>
+          <entry>a1</entry><entry align="center" spanname="span">a2</entry><entry>a5</entry>
+        </row>
+        <row>
+          <entry>b1</entry><entry>b2</entry><entry>b3</entry><entry>b4</entry><entry>b5</entry>
+        </row>
+        <row>
+          <entry>c1</entry><entry spanname="span">c2</entry><entry>c5</entry>
+        </row>
+      </tbody>
+    </tgroup>
+  </informaltable>
+
+  <informaltable frame="all">
+    <tgroup cols="3">
+      <tbody>
+        <row>
+          <entry>a1</entry><entry>b1</entry><entry>c1</entry>
+        </row>
+        <row>
+          <entry>a2</entry>
+          <entrytbl cols="3">
+            <tbody>
+              <row>
+                <entry>b2a1</entry><entry>b2b1</entry><entry>b2c1</entry>
+              </row>
+              <row>
+                <entry>b2a2</entry><entry>b2b2</entry><entry>b2c2</entry>
+              </row>
+              <row>
+                <entry>b2a3</entry><entry>b2b3</entry><entry>b2c3</entry>
+              </row>
+            </tbody>
+          </entrytbl>
+          <entry>c2</entry>
+        </row>
+        <row>
+          <entry>a3</entry><entry>b3</entry><entry>c3</entry>
+        </row>
+      </tbody>
+    </tgroup>
+  </informaltable>
+
+  <para>
+    <anchor id="Anchor" />Anchor<!-- anchor_end -->.
+    Link to <link linkend="Anchor">Anchor</link>.
+    Link to <ulink url="http://www.pixware.fr">http://www.pixware.fr</ulink>.
+    Link to <link linkend="Anchor">showing alternate text</link>.
+    Link to <ulink url="http://www.pixware.fr">Pixware home page</ulink>.
+  </para>
+
+  <note>
+    <title>Upcoming Changes</title>
+    <para>Future versions of this feature may not be backward-compatible.</para>
+  </note>
+          </section>
+        </section>
+      </section>
+    </section>
+  </section>
+
+  <bibliography>
+    <title>A Test Bibliography</title>
+    <bibliodiv><title>Books</title>
+      <bibliomixed>
+        <bibliomset relation="journal">
+          <title>The World Wide Web Journal</title>
+          <volumenum>2</volumenum><issuenum>1</issuenum>.
+          <publishername>O'Reilly & Associates, Inc.</publishername> and
+          <pubdate>Winter, 1996</pubdate></bibliomset>.
+      </bibliomixed>
+    </bibliodiv>
+  </bibliography>
+
+  <appendix>
+    <title>Demonstration Appendix</title>
+    <para>This appendix demonstrates an appendix.</para>
+  </appendix>
+
+</article>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-docbook-simple/src/test/resources/test.xml b/doxia-modules/doxia-module-docbook-simple/src/test/resources/test.xml
new file mode 100644
index 0000000..c602f37
--- /dev/null
+++ b/doxia-modules/doxia-module-docbook-simple/src/test/resources/test.xml
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<!DOCTYPE article PUBLIC "-//OASIS//DTD Simplified DocBook XML V1.1//EN"
+ "http://www.oasis-open.org/docbook/xml/simple/1.1/sdocbook.dtd" [
+  <!ENTITY flo "&#x159;">
+  ]>
+<article id="index">
+  <articleinfo>
+    <title>Geotools2 Developer Guide&flo;</title>
+
+    <author>
+      <firstname>Cameron</firstname>
+
+      <surname>Shorter</surname>
+    </author>
+
+    <date>22 May 2002</date>
+
+    <abstract>
+      <para>Guide to aid <filename>geotools2</filename> development.</para>
+    </abstract>
+  </articleinfo>
+
+  <section id="intro">
+    <title>Introduction</title>
+
+    <para>This document aims to help new geotools developers get up to speed
+    and track practices of existing developers in order to keep the project
+    consistent.</para>
+
+    <para>If you feel something is missing, feel free to contribute a new
+    section.</para>
+  </section>
+
+  <section id="design">
+    <title>Design</title>
+    <figure>
+        <title>Geotools Logo</title>
+        <mediaobject>
+            <imageobject>
+                <imagedata fileref="images/developersguide/geotools_logo.png"
+                    format="PNG"/>
+                </imageobject>
+            <textobject><phrase>Geotools Logo</phrase></textobject>
+        </mediaobject>
+    </figure>
+
+    <para>To do: Explain</para><itemizedlist><listitem><para>Key design structure
+    (core interfaces, data sources, rendering, tools)</para></listitem><listitem><para>Design
+    Drivers (Our design goals: modular, derives structure from netbeans,
+    support multiple version of java, ..)</para></listitem><listitem><para>Directory
+    structure</para></listitem><listitem><para></para></listitem></itemizedlist>
+  </section>
+
+  <section id="development">
+    <title>Development</title>
+
+    <section id="building">
+      <title>Building</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>using ant</para></listitem><listitem><para>what
+      ant checks</para></listitem><listitem><para>insert the
+      "howto-build.html" doc into here</para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section id="cvs">
+      <title>Code Versioning (using CVS)</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Point to sourceforge CVS howto
+      links.</para></listitem><listitem><para>Also point to some HOWTOs for
+      windows users.</para></listitem><listitem><para>What is acceptable to
+      commit to CVS, what is not. (Ie, code should compile)</para></listitem><listitem><para>Talk
+      about version numbering convention, use of branches, etc (if we decide
+      to do this).</para></listitem></itemizedlist>
+
+      <para>To do:</para>
+    </section>
+
+    <section id="CodingStandards">
+      <title>Coding Standards</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Using Sun's coding
+      standard, need to note any exceptions to it.</para></listitem><listitem><para>Note
+      the auto checking of coding standards.</para></listitem><listitem><para>Javadocs</para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section id="templates">
+      <title>Template File</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Provide a template file.</para></listitem><listitem><para>Link
+      it to sections explaining the different parts of the template.</para></listitem><listitem><para>Include
+      headers, footers, example logging, example coding style, CVS key tags,
+      sample javadocs.</para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section id="logging">
+      <title>Logging</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Talk about log4j, links to</para></listitem><listitem><para></para></listitem><listitem><para></para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section id="testing">
+      <title>Testing</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Explain use of junit. Might be
+      a link to another web site.</para></listitem><listitem><para></para></listitem><listitem><para></para></listitem><listitem><para></para></listitem></itemizedlist>
+    </section>
+
+    <section>
+      <title>Documentation</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Talk about docbook, why we
+      decided to use it (most commonly used format of LDP, converts to
+      multiple formats, tools coming which can support it. Need to mention
+      which version of docbook we are using.</para></listitem><listitem><para>Mention
+      tools that can be used.</para></listitem><listitem><para>Links to
+      docbook references.</para></listitem><listitem><para>Process for
+      updating this document.</para></listitem><listitem><para>How to use ant
+      tools to build doc.</para></listitem></itemizedlist>
+    </section>
+  </section>
+
+  <section id="tools">
+    <title>Development Tools</title>
+
+    <section id="netbeans">
+      <title>Netbeans</title>
+
+      <para>To do:</para><itemizedlist><listitem><para>Where to get it.</para></listitem><listitem><para>Aim
+      is to make geotools a module within Netbeans</para></listitem><listitem><para>How
+      to setup getools to fit the multiple directory structure</para></listitem><listitem><para>The
+      extra Netbeans modules that are useful and should be downloaded.</para></listitem></itemizedlist>
+    </section>
+  </section>
+</article>
+
diff --git a/doxia-modules/doxia-module-fml/pom.xml b/doxia-modules/doxia-module-fml/pom.xml
new file mode 100644
index 0000000..2afafe8
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/pom.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-fml</artifactId>
+
+  <name>Doxia :: FML Module</name>
+  <description>A Doxia module for FML source documents.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+
+    <!-- test -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-test-docs</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>xerces</groupId>
+      <artifactId>xercesImpl</artifactId>
+      <version>2.9.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.modello</groupId>
+        <artifactId>modello-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>descriptor</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>java</goal>
+            </goals>
+            <configuration>
+              <models>
+                <model>src/main/mdo/fml.mdo</model>
+              </models>
+              <version>1.0.0</version>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-maven-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/main/components/components.xml</descriptor>
+            <descriptor>target/generated-resources/plexus/META-INF/plexus/components.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>merge-descriptors</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>reporting</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <phase>site</phase>
+                <configuration>
+                  <tasks>
+                    <taskdef name="xsddoc" classname="net.sf.xframe.xsddoc.Task" />
+
+                    <condition property="dir" value="${project.reporting.outputDirectory}/xsddoc">
+                      <matches string="${project.reporting.outputDirectory}" pattern="^${basedir}" />
+                    </condition>
+                    <condition property="dir" value="${project.reporting.outputDirectory}/xsddoc">
+                      <not>
+                        <isset property="dir" />
+                      </not>
+                    </condition>
+
+                    <mkdir dir="${dir}" />
+
+                    <xsddoc file="${basedir}/src/main/resources/fml-1.0.1.xsd" out="${dir}" doctitle="Reference of Schema FML 1.0.1" verbose="true" />
+                  </tasks>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+            <dependencies>
+              <dependency>
+                <groupId>xsddoc</groupId>
+                <artifactId>xsddoc</artifactId>
+                <version>1.0</version>
+                <exclusions>
+                  <exclusion>
+                    <groupId>ant</groupId>
+                    <artifactId>ant</artifactId>
+                  </exclusion>
+                </exclusions>
+              </dependency>
+              <dependency>
+                <groupId>org.apache.ant</groupId>
+                <artifactId>ant-apache-regexp</artifactId>
+                <version>1.7.1</version>
+              </dependency>
+              <dependency>
+                <groupId>xalan</groupId>
+                <artifactId>xalan</artifactId>
+                <version>2.7.1</version>
+              </dependency>
+            </dependencies>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/doxia-modules/doxia-module-fml/src/main/components/components.xml b/doxia-modules/doxia-module-fml/src/main/components/components.xml
new file mode 100644
index 0000000..715d981
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/main/components/components.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<component-set>
+  <components>
+    <component>
+      <role>org.apache.maven.doxia.parser.Parser</role>
+      <role-hint>fml</role-hint>
+      <implementation>org.apache.maven.doxia.module.fml.FmlParser</implementation>
+      <description>Parse an xdoc model and emit events into the specified doxia Sink.</description>
+      <requirements>
+        <requirement>
+          <role>org.apache.maven.doxia.macro.manager.MacroManager</role>
+          <field-name>macroManager</field-name>
+        </requirement>
+      </requirements>
+    </component>
+  </components>
+</component-set>
diff --git a/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlContentParser.java b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlContentParser.java
new file mode 100644
index 0000000..9859d0a
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlContentParser.java
@@ -0,0 +1,120 @@
+package org.apache.maven.doxia.module.fml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.parser.XhtmlBaseParser;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Parse Fml questions and answers, these may contain arbitrary xdoc elements.
+ *
+ * @author ltheussl
+ * @version $Id: FmlContentParser.java 807169 2009-08-24 12:05:48Z vsiveton $
+ * @since 1.0
+ */
+public class FmlContentParser
+    extends XhtmlBaseParser
+    implements FmlMarkup
+{
+    /** Empty elements don't write a closing tag. */
+    private boolean isEmptyElement;
+
+    /** {@inheritDoc} */
+    protected void handleStartTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        isEmptyElement = parser.isEmptyElementTag();
+
+        if ( parser.getName().equals( QUESTION_TAG.toString() )
+                || parser.getName().equals( TITLE.toString() )
+            || parser.getName().equals( ANSWER_TAG.toString() ) )
+        {
+            // ignore
+            return;
+        }
+        else if ( parser.getName().equals( SOURCE_TAG.toString() ) )
+        {
+            verbatim();
+
+            sink.verbatim( SinkEventAttributeSet.BOXED );
+        }
+        else if ( !baseStartTag( parser, sink ) )
+        {
+            if ( isEmptyElement )
+            {
+                handleUnknown( parser, sink, TAG_TYPE_SIMPLE );
+            }
+            else
+            {
+                handleUnknown( parser, sink, TAG_TYPE_START );
+            }
+
+            if ( getLog().isDebugEnabled() )
+            {
+                String position = "[" + parser.getLineNumber() + ":"
+                    + parser.getColumnNumber() + "]";
+                String tag = "<" + parser.getName() + ">";
+
+                getLog().debug( "Unrecognized fml tag: " + tag + " at " + position );
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleEndTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( parser.getName().equals( QUESTION_TAG.toString() )
+                || parser.getName().equals( TITLE.toString() )
+            || parser.getName().equals( ANSWER_TAG.toString() ) )
+        {
+            // ignore
+            return;
+        }
+        else if ( parser.getName().equals( SOURCE_TAG.toString() ) )
+        {
+            verbatim_();
+
+            sink.verbatim_();
+        }
+        else if ( !baseEndTag( parser, sink ) )
+        {
+            if ( !isEmptyElement )
+            {
+                handleUnknown( parser, sink, TAG_TYPE_END );
+            }
+        }
+
+        isEmptyElement = false;
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.isEmptyElement = false;
+    }
+}
diff --git a/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlMarkup.java b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlMarkup.java
new file mode 100644
index 0000000..62805b4
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlMarkup.java
@@ -0,0 +1,121 @@
+package org.apache.maven.doxia.module.fml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.markup.HtmlMarkup;
+
+/**
+ * List of <code>FML</code> markups.
+ * <br/>
+ * FML uses several  {@link javax.swing.text.html.HTML.Tag} and {@link javax.swing.text.html.HTML.Attribute}
+ * as markups and custom tags.
+ *
+ * @author ltheussl
+ * @version $Id: FmlMarkup.java 784718 2009-06-15 10:28:18Z vsiveton $
+ * @since 1.0
+ */
+public interface FmlMarkup
+    extends HtmlMarkup
+{
+    /** FML namespace: "http://maven.apache.org/FML/1.0.1" */
+    String FML_NAMESPACE = "http://maven.apache.org/FML/1.0.1";
+
+    /** FML system id: "http://maven.apache.org/xsd/fml-1.0.1.xsd" */
+    String FML_SYSTEM_ID = "http://maven.apache.org/xsd/fml-1.0.1.xsd";
+
+    // ----------------------------------------------------------------------
+    // Specific Fml tags
+    // ----------------------------------------------------------------------
+
+    /** Fml tag for <code>faqs</code> */
+    Tag FAQS_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "faqs";
+        }
+    };
+
+    /** Fml tag for <code>part</code> */
+    Tag PART_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "part";
+        }
+    };
+
+    /** Fml tag for <code>faq</code> */
+    Tag FAQ_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "faq";
+        }
+    };
+
+    /** Fml tag for <code>question</code> */
+    Tag QUESTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "question";
+        }
+    };
+
+    /** Fml tag for <code>answer</code> */
+    Tag ANSWER_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "answer";
+        }
+    };
+
+    /** Fml tag for <code>source</code> */
+    Tag SOURCE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "source";
+        }
+    };
+
+    /**
+     * Fml tag for <code>macro</code>
+     * @since 1.1.1
+     */
+    Tag MACRO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "macro";
+        }
+    };
+}
diff --git a/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlParser.java b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlParser.java
new file mode 100644
index 0000000..c38ce23
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlParser.java
@@ -0,0 +1,763 @@
+package org.apache.maven.doxia.module.fml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.swing.text.html.HTML.Attribute;
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.macro.manager.MacroNotFoundException;
+import org.apache.maven.doxia.module.fml.model.Faq;
+import org.apache.maven.doxia.module.fml.model.Faqs;
+import org.apache.maven.doxia.module.fml.model.Part;
+import org.apache.maven.doxia.parser.AbstractXmlParser;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.XhtmlBaseSink;
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Parse a fml model and emit events into the specified doxia Sink.
+ *
+ * @author <a href="mailto:evenisse at codehaus.org">Emmanuel Venisse</a>
+ * @author ltheussl
+ * @version $Id: FmlParser.java 807169 2009-08-24 12:05:48Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="fml"
+ */
+public class FmlParser
+    extends AbstractXmlParser
+    implements FmlMarkup
+{
+    /** Collect a faqs model. */
+    private Faqs faqs;
+
+    /** Collect a part. */
+    private Part currentPart;
+
+    /** Collect a single faq. */
+    private Faq currentFaq;
+
+    /** Used to collect text events. */
+    private StringBuffer buffer;
+
+    /** Map of warn messages with a String as key to describe the error type and a Set as value.
+     * Using to reduce warn messages. */
+    private Map warnMessages;
+
+    /** The source content of the input reader. Used to pass into macros. */
+    private String sourceContent;
+
+    /** A macro name. */
+    private String macroName;
+
+    /** The macro parameters. */
+    private Map macroParameters = new HashMap();
+
+    /** {@inheritDoc} */
+    public void parse( Reader source, Sink sink )
+        throws ParseException
+    {
+        this.faqs = null;
+        this.sourceContent = null;
+        init();
+
+        try
+        {
+            StringWriter contentWriter = new StringWriter();
+            IOUtil.copy( source, contentWriter );
+            sourceContent = contentWriter.toString();
+        }
+        catch ( IOException ex )
+        {
+            throw new ParseException( "Error reading the input source: " + ex.getMessage(), ex );
+        }
+        finally
+        {
+            IOUtil.close( source );
+        }
+
+        try
+        {
+            Reader tmp = new StringReader( sourceContent );
+
+            this.faqs = new Faqs();
+
+            // this populates faqs
+            super.parse( tmp, sink );
+
+            writeFaqs( sink );
+        }
+        finally
+        {
+            logWarnings();
+
+            this.faqs = null;
+            this.sourceContent = null;
+            setSecondParsing( false );
+            init();
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleStartTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( parser.getName().equals( FAQS_TAG.toString() ) )
+        {
+            String title = parser.getAttributeValue( null, "title" );
+
+            if ( title != null )
+            {
+                faqs.setTitle( title );
+            }
+
+            String toplink = parser.getAttributeValue( null, "toplink" );
+
+            if ( toplink != null )
+            {
+                if ( toplink.equalsIgnoreCase( "true" ) )
+                {
+                    faqs.setToplink( true );
+                }
+                else
+                {
+                    faqs.setToplink( false );
+                }
+            }
+        }
+        else if ( parser.getName().equals( PART_TAG.toString() ) )
+        {
+            currentPart = new Part();
+
+            currentPart.setId( parser.getAttributeValue( null, Attribute.ID.toString() ) );
+
+            if ( currentPart.getId() == null )
+            {
+                throw new XmlPullParserException( "id attribute required for <part> at: ("
+                    + parser.getLineNumber() + ":" + parser.getColumnNumber() + ")" );
+            }
+            else if ( !DoxiaUtils.isValidId( currentPart.getId() ) )
+            {
+                String linkAnchor = DoxiaUtils.encodeId( currentPart.getId(), true );
+
+                String msg = "Modified invalid link: '" + currentPart.getId() + "' to '" + linkAnchor + "'";
+                logMessage( "modifiedLink", msg );
+
+                currentPart.setId( linkAnchor );
+            }
+        }
+        else if ( parser.getName().equals( TITLE.toString() ) )
+        {
+            buffer = new StringBuffer();
+
+            buffer.append( String.valueOf( LESS_THAN ) ).append( parser.getName() )
+                .append( String.valueOf( GREATER_THAN ) );
+        }
+        else if ( parser.getName().equals( FAQ_TAG.toString() ) )
+        {
+            currentFaq = new Faq();
+
+            currentFaq.setId( parser.getAttributeValue( null, Attribute.ID.toString() ) );
+
+            if ( currentFaq.getId() == null )
+            {
+                throw new XmlPullParserException( "id attribute required for <faq> at: ("
+                    + parser.getLineNumber() + ":" + parser.getColumnNumber() + ")" );
+            }
+            else if ( !DoxiaUtils.isValidId( currentFaq.getId() ) )
+            {
+                String linkAnchor = DoxiaUtils.encodeId( currentFaq.getId(), true );
+
+                String msg = "Modified invalid link: '" + currentFaq.getId() + "' to '" + linkAnchor + "'";
+                logMessage( "modifiedLink", msg );
+
+                currentFaq.setId( linkAnchor );
+            }
+        }
+        else if ( parser.getName().equals( QUESTION_TAG.toString() ) )
+        {
+            buffer = new StringBuffer();
+
+            buffer.append( String.valueOf( LESS_THAN ) ).append( parser.getName() )
+                .append( String.valueOf( GREATER_THAN ) );
+        }
+        else if ( parser.getName().equals( ANSWER_TAG.toString() ) )
+        {
+            buffer = new StringBuffer();
+
+            buffer.append( String.valueOf( LESS_THAN ) ).append( parser.getName() )
+                .append( String.valueOf( GREATER_THAN ) );
+
+        }
+
+        // ----------------------------------------------------------------------
+        // Macro
+        // ----------------------------------------------------------------------
+
+        else if ( parser.getName().equals( MACRO_TAG.toString() ) )
+        {
+            handleMacroStart( parser );
+        }
+        else if ( parser.getName().equals( PARAM.toString() ) )
+        {
+            handleParamStart( parser, sink );
+        }
+        else if ( buffer != null )
+        {
+            buffer.append( String.valueOf( LESS_THAN ) ).append( parser.getName() );
+
+            int count = parser.getAttributeCount();
+
+            for ( int i = 0; i < count; i++ )
+            {
+                buffer.append( String.valueOf( SPACE ) ).append( parser.getAttributeName( i ) );
+
+                buffer.append( String.valueOf( EQUAL ) ).append( String.valueOf( QUOTE ) );
+
+                // TODO: why are attribute values HTML-encoded?
+                buffer.append( HtmlTools.escapeHTML( parser.getAttributeValue( i ) ) );
+
+                buffer.append( String.valueOf( QUOTE ) );
+            }
+
+            buffer.append( String.valueOf( GREATER_THAN ) );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleEndTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( parser.getName().equals( FAQS_TAG.toString() ) )
+        {
+            // Do nothing
+            return;
+        }
+        else if ( parser.getName().equals( PART_TAG.toString() ) )
+        {
+            faqs.addPart( currentPart );
+
+            currentPart = null;
+        }
+        else if ( parser.getName().equals( FAQ_TAG.toString() ) )
+        {
+            if ( currentPart == null )
+            {
+                throw new XmlPullParserException( "Missing <part>  at: ("
+                    + parser.getLineNumber() + ":" + parser.getColumnNumber() + ")" );
+            }
+
+            currentPart.addFaq( currentFaq );
+
+            currentFaq = null;
+        }
+        else if ( parser.getName().equals( QUESTION_TAG.toString() ) )
+        {
+            if ( currentFaq == null )
+            {
+                throw new XmlPullParserException( "Missing <faq> at: ("
+                    + parser.getLineNumber() + ":" + parser.getColumnNumber() + ")" );
+            }
+
+            buffer.append( String.valueOf( LESS_THAN ) ).append( String.valueOf( SLASH ) )
+                .append( parser.getName() ).append( String.valueOf( GREATER_THAN ) );
+
+            currentFaq.setQuestion( buffer.toString() );
+
+            buffer = null;
+        }
+        else if ( parser.getName().equals( ANSWER_TAG.toString() ) )
+        {
+            if ( currentFaq == null )
+            {
+                throw new XmlPullParserException( "Missing <faq> at: ("
+                    + parser.getLineNumber() + ":" + parser.getColumnNumber() + ")" );
+            }
+
+            buffer.append( String.valueOf( LESS_THAN ) ).append( String.valueOf( SLASH ) )
+                .append( parser.getName() ).append( String.valueOf( GREATER_THAN ) );
+
+            currentFaq.setAnswer( buffer.toString() );
+
+            buffer = null;
+        }
+        else if ( parser.getName().equals( TITLE.toString() ) )
+        {
+            if ( currentPart == null )
+            {
+                throw new XmlPullParserException( "Missing <part> at: ("
+                    + parser.getLineNumber() + ":" + parser.getColumnNumber() + ")" );
+            }
+
+            buffer.append( String.valueOf( LESS_THAN ) ).append( String.valueOf( SLASH ) )
+                .append( parser.getName() ).append( String.valueOf( GREATER_THAN ) );
+
+            currentPart.setTitle( buffer.toString() );
+
+            buffer = null;
+        }
+
+        // ----------------------------------------------------------------------
+        // Macro
+        // ----------------------------------------------------------------------
+
+        else if ( parser.getName().equals( MACRO_TAG.toString() ) )
+        {
+            handleMacroEnd( buffer );
+        }
+        else if ( parser.getName().equals( PARAM.toString() ) )
+        {
+            if ( !StringUtils.isNotEmpty( macroName ) )
+            {
+                handleUnknown( parser, sink, TAG_TYPE_END );
+            }
+        }
+        else if ( buffer != null )
+        {
+            if ( buffer.length() > 0 && buffer.charAt( buffer.length() - 1 ) == SPACE )
+            {
+                buffer.deleteCharAt( buffer.length() - 1 );
+            }
+
+            buffer.append( String.valueOf( LESS_THAN ) ).append( String.valueOf( SLASH ) )
+                .append( parser.getName() ).append( String.valueOf( GREATER_THAN ) );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleText( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        if ( buffer != null )
+        {
+            buffer.append( parser.getText() );
+        }
+        // only significant text content in fml files is in <question>, <answer> or <title>
+    }
+
+    /** {@inheritDoc} */
+    protected void handleCdsect( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        String cdSection = parser.getText();
+
+        if ( buffer != null )
+        {
+            buffer.append( LESS_THAN ).append( BANG ).append( LEFT_SQUARE_BRACKET ).append( CDATA )
+                    .append( LEFT_SQUARE_BRACKET ).append( cdSection ).append( RIGHT_SQUARE_BRACKET )
+                    .append( RIGHT_SQUARE_BRACKET ).append( GREATER_THAN );
+        }
+        else
+        {
+            sink.text( cdSection );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleComment( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        String comment = parser.getText();
+
+        if ( buffer != null )
+        {
+            buffer.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS )
+                    .append( comment ).append( MINUS ).append( MINUS ).append( GREATER_THAN );
+        }
+        else
+        {
+            sink.comment( comment.trim() );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleEntity( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException
+    {
+        if ( buffer != null )
+        {
+            if ( parser.getText() != null )
+            {
+                String text = parser.getText();
+
+                // parser.getText() returns the entity replacement text
+                // (< -> <), need to re-escape them
+                if ( text.length() == 1 )
+                {
+                    text = HtmlTools.escapeHTML( text );
+                }
+
+                buffer.append( text );
+            }
+        }
+        else
+        {
+            super.handleEntity( parser, sink );
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.currentFaq = null;
+        this.currentPart = null;
+        this.buffer = null;
+        this.warnMessages = null;
+        this.macroName = null;
+        this.macroParameters = null;
+    }
+
+    /**
+     * TODO import from XdocParser, probably need to be generic.
+     *
+     * @param parser not null
+     * @throws MacroExecutionException if any
+     */
+    private void handleMacroStart( XmlPullParser parser )
+            throws MacroExecutionException
+    {
+        if ( !isSecondParsing() )
+        {
+            macroName = parser.getAttributeValue( null, Attribute.NAME.toString() );
+
+            if ( macroParameters == null )
+            {
+                macroParameters = new HashMap();
+            }
+
+            if ( StringUtils.isEmpty( macroName ) )
+            {
+                throw new MacroExecutionException( "The '" + Attribute.NAME.toString()
+                        + "' attribute for the '" + MACRO_TAG.toString() + "' tag is required." );
+            }
+        }
+    }
+
+    /**
+     * TODO import from XdocParser, probably need to be generic.
+     *
+     * @param buffer not null
+     * @throws MacroExecutionException if any
+     */
+    private void handleMacroEnd( StringBuffer buffer )
+            throws MacroExecutionException
+    {
+        if ( !isSecondParsing() )
+        {
+            if ( StringUtils.isNotEmpty( macroName ) )
+            {
+                // TODO handles specific macro attributes
+                macroParameters.put( "sourceContent", sourceContent );
+                FmlParser fmlParser = new FmlParser();
+                fmlParser.setSecondParsing( true );
+                macroParameters.put( "parser", fmlParser );
+
+                MacroRequest request = new MacroRequest( macroParameters, getBasedir() );
+
+                try
+                {
+                    StringWriter sw = new StringWriter();
+                    XhtmlBaseSink sink = new XhtmlBaseSink(sw);
+                    executeMacro( macroName, request, sink );
+                    sink.close();
+                    buffer.append( sw.toString() );
+                } catch ( MacroNotFoundException me )
+                {
+                    throw new MacroExecutionException( "Macro not found: " + macroName, me );
+                }
+            }
+        }
+
+        // Reinit macro
+        macroName = null;
+        macroParameters = null;
+    }
+
+    /**
+     * TODO import from XdocParser, probably need to be generic.
+     *
+     * @param parser not null
+     * @param sink not null
+     * @throws MacroExecutionException if any
+     */
+    private void handleParamStart( XmlPullParser parser, Sink sink )
+            throws MacroExecutionException
+    {
+        if ( !isSecondParsing() )
+        {
+            if ( StringUtils.isNotEmpty( macroName ) )
+            {
+                String paramName = parser.getAttributeValue( null, Attribute.NAME.toString() );
+                String paramValue = parser.getAttributeValue( null,
+                        Attribute.VALUE.toString() );
+
+                if ( StringUtils.isEmpty( paramName ) || StringUtils.isEmpty( paramValue ) )
+                {
+                    throw new MacroExecutionException( "'" + Attribute.NAME.toString()
+                            + "' and '" + Attribute.VALUE.toString() + "' attributes for the '" + PARAM.toString()
+                            + "' tag are required inside the '" + MACRO_TAG.toString() + "' tag." );
+                }
+
+                macroParameters.put( paramName, paramValue );
+            }
+            else
+            {
+                // param tag from non-macro object, see MSITE-288
+                handleUnknown( parser, sink, TAG_TYPE_START );
+            }
+        }
+    }
+
+    /**
+     * Writes the faqs to the specified sink.
+     *
+     * @param faqs The faqs to emit.
+     * @param sink The sink to consume the event.
+     * @throws ParseException if something goes wrong.
+     */
+    private void writeFaqs( Sink sink )
+        throws ParseException
+    {
+        FmlContentParser xdocParser = new FmlContentParser();
+        xdocParser.enableLogging( getLog() );
+
+        sink.head();
+        sink.title();
+        sink.text( faqs.getTitle() );
+        sink.title_();
+        sink.head_();
+
+        sink.body();
+        sink.section1();
+        sink.sectionTitle1();
+        sink.anchor( "top" );
+        sink.text( faqs.getTitle() );
+        sink.anchor_();
+        sink.sectionTitle1_();
+
+        // ----------------------------------------------------------------------
+        // Write summary
+        // ----------------------------------------------------------------------
+
+        for ( Iterator partIterator = faqs.getParts().iterator(); partIterator.hasNext(); )
+        {
+            Part part = (Part) partIterator.next();
+
+            if ( StringUtils.isNotEmpty( part.getTitle() ) )
+            {
+                sink.paragraph();
+                sink.bold();
+                xdocParser.parse( part.getTitle(), sink );
+                sink.bold_();
+                sink.paragraph_();
+            }
+
+            sink.numberedList( Sink.NUMBERING_DECIMAL );
+
+            for ( Iterator faqIterator = part.getFaqs().iterator(); faqIterator.hasNext(); )
+            {
+                Faq faq = (Faq) faqIterator.next();
+                sink.numberedListItem();
+                sink.link( "#" + faq.getId() );
+
+                if ( StringUtils.isNotEmpty( faq.getQuestion() ) )
+                {
+                    xdocParser.parse( faq.getQuestion(), sink );
+                }
+                else
+                {
+                    throw new ParseException( "Missing <question> for FAQ '" + faq.getId() + "'" );
+                }
+
+                sink.link_();
+                sink.numberedListItem_();
+            }
+
+            sink.numberedList_();
+        }
+
+        sink.section1_();
+
+        // ----------------------------------------------------------------------
+        // Write content
+        // ----------------------------------------------------------------------
+
+        for ( Iterator partIterator = faqs.getParts().iterator(); partIterator.hasNext(); )
+        {
+            Part part = (Part) partIterator.next();
+
+            if ( StringUtils.isNotEmpty( part.getTitle() ) )
+            {
+                sink.section1();
+
+                sink.sectionTitle1();
+                xdocParser.parse( part.getTitle(), sink );
+                sink.sectionTitle1_();
+            }
+
+            sink.definitionList();
+
+            for ( Iterator faqIterator = part.getFaqs().iterator(); faqIterator.hasNext(); )
+            {
+                Faq faq = (Faq) faqIterator.next();
+
+                sink.definedTerm();
+                sink.anchor( faq.getId() );
+
+                if ( StringUtils.isNotEmpty( faq.getQuestion() ) )
+                {
+                    xdocParser.parse( faq.getQuestion(), sink );
+                }
+                else
+                {
+                    throw new ParseException( "Missing <question> for FAQ '" + faq.getId() + "'" );
+                }
+
+                sink.anchor_();
+                sink.definedTerm_();
+
+                sink.definition();
+
+                if ( StringUtils.isNotEmpty( faq.getAnswer() ) )
+                {
+                    xdocParser.parse( faq.getAnswer(), sink );
+                }
+                else
+                {
+                    throw new ParseException( "Missing <answer> for FAQ '" + faq.getId() + "'" );
+                }
+
+                if ( faqs.isToplink() )
+                {
+                    writeTopLink( sink );
+                }
+
+                if ( faqIterator.hasNext() )
+                {
+                    sink.horizontalRule();
+                }
+
+                sink.definition_();
+            }
+
+            sink.definitionList_();
+
+            if ( StringUtils.isNotEmpty( part.getTitle() ) )
+            {
+                sink.section1_();
+            }
+        }
+
+        sink.body_();
+    }
+
+    /**
+     * Writes a toplink element.
+     *
+     * @param sink The sink to consume the event.
+     */
+    private void writeTopLink( Sink sink )
+    {
+        SinkEventAttributeSet atts = new SinkEventAttributeSet();
+        atts.addAttribute( SinkEventAttributeSet.ALIGN, "right" );
+        sink.paragraph( atts );
+        sink.link( "#top" );
+        sink.text( "[top]" );
+        sink.link_();
+        sink.paragraph_();
+    }
+
+    /**
+     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
+     *
+     * @param key not null
+     * @param msg not null
+     * @see #parse(Reader, Sink)
+     * @since 1.1.1
+     */
+    private void logMessage( String key, String msg )
+    {
+        msg = "[FML Parser] " + msg;
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( msg );
+
+            return;
+        }
+
+        if ( warnMessages == null )
+        {
+            warnMessages = new HashMap();
+        }
+
+        Set set = (Set) warnMessages.get( key );
+        if ( set == null )
+        {
+            set = new TreeSet();
+        }
+        set.add( msg );
+        warnMessages.put( key, set );
+    }
+
+    /**
+     * @since 1.1.1
+     */
+    private void logWarnings()
+    {
+        if ( getLog().isWarnEnabled() && this.warnMessages != null && !isSecondParsing() )
+        {
+            for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                Set set = (Set) entry.getValue();
+                for ( Iterator it2 = set.iterator(); it2.hasNext(); )
+                {
+                    String msg = (String) it2.next();
+
+                    getLog().warn( msg );
+                }
+            }
+
+            this.warnMessages = null;
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlSiteModule.java b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlSiteModule.java
new file mode 100644
index 0000000..fabadc0
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlSiteModule.java
@@ -0,0 +1,42 @@
+package org.apache.maven.doxia.module.fml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.site.AbstractSiteModule;
+
+/**
+ * <p>FmlSiteModule class.</p>
+ *
+ * @author <a href="mailto:evenisse at codehaus.org">Emmanuel Venisse</a>
+ * @version $Id: FmlSiteModule.java 782392 2009-06-07 14:14:16Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.module.site.SiteModule" role-hint="fml"
+ */
+public class FmlSiteModule
+    extends AbstractSiteModule
+{
+    /**
+     * Default constructor.
+     */
+    public FmlSiteModule()
+    {
+        super( "fml", "fml", "fml" );
+    }
+}
diff --git a/doxia-modules/doxia-module-fml/src/main/mdo/fml.mdo b/doxia-modules/doxia-module-fml/src/main/mdo/fml.mdo
new file mode 100644
index 0000000..2fce49d
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/main/mdo/fml.mdo
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<model xmlns="http://modello.codehaus.org/MODELLO/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://modello.codehaus.org/MODELLO/1.0.0 http://modello.codehaus.org/xsd/modello-1.0.0.xsd">
+  <id>fml</id>
+  <name>Fml</name>
+  <description><![CDATA[
+    Model object of the FML (FAQ Markup Language) descriptor used in Maven Doxia.
+  ]]></description>
+  <defaults>
+    <default>
+      <key>package</key>
+      <value>org.apache.maven.doxia.module.fml.model</value>
+    </default>
+  </defaults>
+  <classes>
+    <class rootElement="true" xml.tagName="faqs" xsd.compositor="sequence">
+      <name>Faqs</name>
+      <description><![CDATA[
+         The <code><faqs></code> element is the root of the FML descriptor.
+         The following table lists all of the possible child elements.
+      ]]></description>
+      <version>1.0.0</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>title</name>
+          <description><![CDATA[
+            The title name of this FAQ.
+          ]]></description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <defaultValue>FAQ</defaultValue>
+          <identifier>true</identifier>
+        </field>
+        <field xml.attribute="true">
+          <name>toplink</name>
+          <description><![CDATA[
+            Boolean to generate optionally [top] links.
+          ]]></description>
+          <version>1.0.0</version>
+          <type>boolean</type>
+          <defaultValue>true</defaultValue>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>parts</name>
+          <description><![CDATA[
+            List of FAQ part.
+          ]]></description>
+          <version>1.0.0</version>
+          <association xml.itemsStyle="flat">
+            <type>Part</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class xsd.compositor="sequence">
+      <name>Part</name>
+      <description><![CDATA[
+        FAQ part.
+      ]]></description>
+      <version>1.0.0</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>id</name>
+          <version>1.0.0</version>
+          <description><![CDATA[
+            The identifier of the part.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>title</name>
+          <version>1.0.0</version>
+          <description><![CDATA[
+            The title of the FAQ part.
+          ]]></description>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>faqs</name>
+          <description><![CDATA[
+            A list of FAQ.
+          ]]></description>
+          <version>1.0.0</version>
+          <association xml.itemsStyle="flat">
+            <type>Faq</type>
+            <multiplicity>*</multiplicity>
+          </association>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+    <class xsd.compositor="sequence">
+      <name>Faq</name>
+      <description><![CDATA[
+        A Faq.
+      ]]></description>
+      <version>1.0.0</version>
+      <fields>
+        <field xml.attribute="true">
+          <name>id</name>
+          <description><![CDATA[
+            The FAQ identifier.
+          ]]></description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>question</name>
+          <description><![CDATA[
+            The question.
+          ]]></description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+        <field>
+          <name>answer</name>
+          <description><![CDATA[
+            The answer.
+          ]]></description>
+          <version>1.0.0</version>
+          <type>String</type>
+          <identifier>true</identifier>
+        </field>
+      </fields>
+    </class>
+  </classes>
+</model>
diff --git a/doxia-modules/doxia-module-fml/src/main/resources/fml-1.0.1.xsd b/doxia-modules/doxia-module-fml/src/main/resources/fml-1.0.1.xsd
new file mode 100644
index 0000000..2ae8009
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/main/resources/fml-1.0.1.xsd
@@ -0,0 +1,2928 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<xs:schema version="1.0"
+  xml:lang="en"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  targetNamespace="http://maven.apache.org/FML/1.0.1"
+  xmlns="http://maven.apache.org/FML/1.0.1"
+  xmlns:xml="http://www.w3.org/XML/1998/namespace"
+  elementFormDefault="qualified">
+
+  <xs:annotation>
+    <xs:documentation source="description">
+      Doxia FML (FAQ Markup Language) 1.0.1 XML Schema.
+
+      This is based on: Extensible HTML version 1.0 Transitional XML Schema
+      http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd
+
+      For further information, see:
+      http://maven.apache.org/doxia/references/fml-format.html
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================ Character mnemonic entities =========================
+
+    XHTML entity sets are identified by the PUBLIC and SYSTEM identifiers:
+
+    PUBLIC "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+    SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent"
+
+    PUBLIC "-//W3C//ENTITIES Special for XHTML//EN"
+    SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent"
+
+    PUBLIC "-//W3C//ENTITIES Symbols for XHTML//EN"
+    SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent"
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== Imported Names ====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:simpleType name="ContentType">
+    <xs:annotation>
+      <xs:documentation>
+      media type, as per [RFC2045]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="ContentTypes">
+    <xs:annotation>
+      <xs:documentation>
+      comma-separated list of media types, as per [RFC2045]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Charset">
+    <xs:annotation>
+      <xs:documentation>
+      a character encoding, as per [RFC2045]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Charsets">
+    <xs:annotation>
+      <xs:documentation>
+      a space separated list of character encodings, as per [RFC2045]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="LanguageCode">
+    <xs:annotation>
+      <xs:documentation>
+      a language code, as per [RFC3066]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:language"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Character">
+    <xs:annotation>
+      <xs:documentation>
+      a single character, as per section 2.2 of [XML]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:length value="1" fixed="true"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Number">
+    <xs:annotation>
+      <xs:documentation>
+      one or more digits
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:nonNegativeInteger">
+      <xs:pattern value="[0-9]+"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="tabindexNumber">
+    <xs:annotation>
+      <xs:documentation>
+      tabindex attribute specifies the position of the current element
+      in the tabbing order for the current document. This value must be
+      a number between 0 and 32767. User agents should ignore leading zeros.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="Number">
+      <xs:minInclusive value="0"/>
+      <xs:maxInclusive value="32767"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="LinkTypes">
+    <xs:annotation>
+      <xs:documentation>
+      space-separated list of link types
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:NMTOKENS"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="MediaDesc">
+    <xs:annotation>
+      <xs:documentation>
+      single or comma-separated list of media descriptors
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[^,]+(,\s*[^,]+)*"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="URI">
+    <xs:annotation>
+      <xs:documentation>
+      a Uniform Resource Identifier, see [RFC2396]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:anyURI"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="UriList">
+    <xs:annotation>
+      <xs:documentation>
+      a space separated list of Uniform Resource Identifiers
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Datetime">
+    <xs:annotation>
+      <xs:documentation>
+      date and time information. ISO date format
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:dateTime"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Script">
+    <xs:annotation>
+      <xs:documentation>
+      script expression
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="StyleSheet">
+    <xs:annotation>
+      <xs:documentation>
+      style sheet data
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Text">
+    <xs:annotation>
+      <xs:documentation>
+      used for titles etc.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="FrameTarget">
+    <xs:annotation>
+      <xs:documentation>
+      render in this frame
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:NMTOKEN">
+      <xs:pattern value="_(blank|self|parent|top)|[A-Za-z]\c*"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Length">
+    <xs:annotation>
+      <xs:documentation>
+      nn for pixels or nn% for percentage length
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[\-+]?(\d+|\d+(\.\d+)?%)"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="MultiLength">
+    <xs:annotation>
+      <xs:documentation>
+      pixel, percentage, or relative
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[\-+]?(\d+|\d+(\.\d+)?%)|[1-9]?(\d+)?\*"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Pixels">
+    <xs:annotation>
+      <xs:documentation>
+      integer representing length in pixels
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:nonNegativeInteger"/>
+  </xs:simpleType>
+
+  <xs:annotation>
+    <xs:documentation>
+    these are used for image maps
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:simpleType name="Shape">
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="rect"/>
+      <xs:enumeration value="circle"/>
+      <xs:enumeration value="poly"/>
+      <xs:enumeration value="default"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Coords">
+    <xs:annotation>
+      <xs:documentation>
+      comma separated list of lengths
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[\-+]?(\d+|\d+(\.\d+)?%)(,\s*[\-+]?(\d+|\d+(\.\d+)?%))*"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="ImgAlign">
+    <xs:annotation>
+      <xs:documentation>
+      used for object, applet, img, input and iframe
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="top"/>
+      <xs:enumeration value="middle"/>
+      <xs:enumeration value="bottom"/>
+      <xs:enumeration value="left"/>
+      <xs:enumeration value="right"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Color">
+    <xs:annotation>
+      <xs:documentation>
+      a color using sRGB: #RRGGBB as Hex values
+
+      There are also 16 widely known color names with their sRGB values:
+
+      Black  = #000000    Green  = #008000
+      Silver = #C0C0C0    Lime   = #00FF00
+      Gray   = #808080    Olive  = #808000
+      White  = #FFFFFF    Yellow = #FFFF00
+      Maroon = #800000    Navy   = #000080
+      Red    = #FF0000    Blue   = #0000FF
+      Purple = #800080    Teal   = #008080
+      Fuchsia= #FF00FF    Aqua   = #00FFFF
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[A-Za-z]+|#[0-9A-Fa-f]{3}|#[0-9A-Fa-f]{6}"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Generic Attributes ===============================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:attributeGroup name="coreattrs">
+    <xs:annotation>
+      <xs:documentation>
+      core attributes common to most elements
+      id       document-wide unique id
+      class    space separated list of classes
+      style    associated style info
+      title    advisory title/amplification
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="id" type="xs:ID"/>
+    <xs:attribute name="class" type="xs:NMTOKENS"/>
+    <xs:attribute name="style" type="StyleSheet"/>
+    <xs:attribute name="title" type="Text"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="i18n">
+    <xs:annotation>
+      <xs:documentation>
+      internationalization attributes
+      lang        language code (backwards compatible)
+      xml:lang    language code (as per XML 1.0 spec)
+      dir         direction for weak/neutral text
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="lang" type="LanguageCode"/>
+    <xs:attribute ref="xml:lang"/>
+    <xs:attribute name="dir">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="ltr"/>
+          <xs:enumeration value="rtl"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="events">
+    <xs:annotation>
+      <xs:documentation>
+      attributes for common UI events
+      onclick     a pointer button was clicked
+      ondblclick  a pointer button was double clicked
+      onmousedown a pointer button was pressed down
+      onmouseup   a pointer button was released
+      onmousemove a pointer was moved onto the element
+      onmouseout  a pointer was moved away from the element
+      onkeypress  a key was pressed and released
+      onkeydown   a key was pressed down
+      onkeyup     a key was released
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="onclick" type="Script"/>
+    <xs:attribute name="ondblclick" type="Script"/>
+    <xs:attribute name="onmousedown" type="Script"/>
+    <xs:attribute name="onmouseup" type="Script"/>
+    <xs:attribute name="onmouseover" type="Script"/>
+    <xs:attribute name="onmousemove" type="Script"/>
+    <xs:attribute name="onmouseout" type="Script"/>
+    <xs:attribute name="onkeypress" type="Script"/>
+    <xs:attribute name="onkeydown" type="Script"/>
+    <xs:attribute name="onkeyup" type="Script"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="focus">
+    <xs:annotation>
+      <xs:documentation>
+      attributes for elements that can get the focus
+      accesskey   accessibility key character
+      tabindex    position in tabbing order
+      onfocus     the element got the focus
+      onblur      the element lost the focus
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="accesskey" type="Character"/>
+    <xs:attribute name="tabindex" type="tabindexNumber"/>
+    <xs:attribute name="onfocus" type="Script"/>
+    <xs:attribute name="onblur" type="Script"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="attrs">
+    <xs:attributeGroup ref="coreattrs"/>
+    <xs:attributeGroup ref="i18n"/>
+    <xs:attributeGroup ref="events"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="TextAlign">
+    <xs:annotation>
+      <xs:documentation>
+      text alignment for p, div, h1-h6. The default is
+      align="left" for ltr headings, "right" for rtl
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="align">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="left"/>
+          <xs:enumeration value="center"/>
+          <xs:enumeration value="right"/>
+          <xs:enumeration value="justify"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:attributeGroup>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Text Elements ====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:group name="special.extra">
+    <xs:choice>
+      <xs:element ref="object"/>
+      <xs:element ref="applet"/>
+      <xs:element ref="img"/>
+      <xs:element ref="map"/>
+      <xs:element ref="iframe"/>
+      <xs:element ref="source"/> <!-- FML specific -->
+      <xs:element ref="macro"/> <!-- FML specific -->
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="special.basic">
+    <xs:choice>
+      <xs:element ref="br"/>
+      <xs:element ref="span"/>
+      <xs:element ref="bdo"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="special">
+    <xs:choice>
+      <xs:group ref="special.basic"/>
+      <xs:group ref="special.extra"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="fontstyle.extra">
+    <xs:choice>
+      <xs:element ref="big"/>
+      <xs:element ref="small"/>
+      <xs:element ref="font"/>
+      <xs:element ref="basefont"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="fontstyle.basic">
+    <xs:choice>
+      <xs:element ref="tt"/>
+      <xs:element ref="i"/>
+      <xs:element ref="b"/>
+      <xs:element ref="u"/>
+      <xs:element ref="s"/>
+      <xs:element ref="strike"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="fontstyle">
+    <xs:choice>
+      <xs:group ref="fontstyle.basic"/>
+      <xs:group ref="fontstyle.extra"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="phrase.extra">
+    <xs:choice>
+      <xs:element ref="sub"/>
+      <xs:element ref="sup"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="phrase.basic">
+    <xs:choice>
+      <xs:element ref="em"/>
+      <xs:element ref="strong"/>
+      <xs:element ref="dfn"/>
+      <xs:element ref="code"/>
+      <xs:element ref="q"/>
+      <xs:element ref="samp"/>
+      <xs:element ref="kbd"/>
+      <xs:element ref="var"/>
+      <xs:element ref="cite"/>
+      <xs:element ref="abbr"/>
+      <xs:element ref="acronym"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="phrase">
+    <xs:choice>
+      <xs:group ref="phrase.basic"/>
+      <xs:group ref="phrase.extra"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="inline.forms">
+    <xs:choice>
+      <xs:element ref="input"/>
+      <xs:element ref="select"/>
+      <xs:element ref="textarea"/>
+      <xs:element ref="label"/>
+      <xs:element ref="button"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="misc.inline">
+    <xs:annotation>
+      <xs:documentation>
+      these can only occur at block level
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice>
+      <xs:element ref="ins"/>
+      <xs:element ref="del"/>
+      <xs:element ref="script"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="misc">
+    <xs:annotation>
+      <xs:documentation>
+      these can only occur at block level
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice>
+      <xs:element ref="noscript"/>
+      <xs:group ref="misc.inline"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="inline">
+    <xs:choice>
+      <xs:element ref="a"/>
+      <xs:group ref="special"/>
+      <xs:group ref="fontstyle"/>
+      <xs:group ref="phrase"/>
+      <xs:group ref="inline.forms"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:complexType name="Inline" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      "Inline" covers inline or "text-level" element
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:group ref="inline"/>
+      <xs:group ref="misc.inline"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== Block level elements ==============================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:group name="heading">
+    <xs:choice>
+      <xs:element ref="h1"/>
+      <xs:element ref="h2"/>
+      <xs:element ref="h3"/>
+      <xs:element ref="h4"/>
+      <xs:element ref="h5"/>
+      <xs:element ref="h6"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="lists">
+    <xs:choice>
+      <xs:element ref="ul"/>
+      <xs:element ref="ol"/>
+      <xs:element ref="dl"/>
+      <xs:element ref="menu"/>
+      <xs:element ref="dir"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="blocktext">
+    <xs:choice>
+      <xs:element ref="pre"/>
+      <xs:element ref="hr"/>
+      <xs:element ref="blockquote"/>
+      <xs:element ref="address"/>
+      <xs:element ref="center"/>
+      <xs:element ref="noframes"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="block">
+    <xs:choice>
+      <xs:element ref="p"/>
+      <xs:group ref="heading"/>
+      <xs:element ref="div"/>
+      <xs:group ref="lists"/>
+      <xs:group ref="blocktext"/>
+      <xs:element ref="isindex"/>
+      <xs:element ref="fieldset"/>
+      <xs:element ref="table"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:complexType name="Flow" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      "Flow" mixes block and inline and is used for list items etc.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:group ref="block"/>
+      <xs:element ref="form"/>
+      <xs:group ref="inline"/>
+      <xs:group ref="misc"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== Content models for exclusions =====================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:complexType name="a.content" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      a elements use "Inline" excluding a
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:group ref="special"/>
+      <xs:group ref="fontstyle"/>
+      <xs:group ref="phrase"/>
+      <xs:group ref="inline.forms"/>
+      <xs:group ref="misc.inline"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:complexType name="pre.content" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      pre uses "Inline" excluding img, object, applet, big, small,
+      font, or basefont
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element ref="a"/>
+      <xs:group ref="special.basic"/>
+      <xs:group ref="fontstyle.basic"/>
+      <xs:group ref="phrase.basic"/>
+      <xs:group ref="inline.forms"/>
+      <xs:group ref="misc.inline"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:complexType name="form.content" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      form uses "Flow" excluding form
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:group ref="block"/>
+      <xs:group ref="inline"/>
+      <xs:group ref="misc"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:complexType name="button.content" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      button uses "Flow" but excludes a, form, form controls, iframe
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element ref="p"/>
+      <xs:group ref="heading"/>
+      <xs:element ref="div"/>
+      <xs:group ref="lists"/>
+      <xs:group ref="blocktext"/>
+      <xs:element ref="table"/>
+      <xs:element ref="br"/>
+      <xs:element ref="span"/>
+      <xs:element ref="bdo"/>
+      <xs:element ref="object"/>
+      <xs:element ref="applet"/>
+      <xs:element ref="img"/>
+      <xs:element ref="map"/>
+      <xs:group ref="fontstyle"/>
+      <xs:group ref="phrase"/>
+      <xs:group ref="misc"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================ Document Head =======================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:group name="head.misc">
+    <xs:sequence>
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element ref="script"/>
+        <xs:element ref="style"/>
+        <xs:element ref="meta"/>
+        <xs:element ref="link"/>
+        <xs:element ref="object"/>
+        <xs:element ref="isindex"/>
+      </xs:choice>
+    </xs:sequence>
+  </xs:group>
+
+  <xs:element name="head">
+    <xs:annotation>
+      <xs:documentation>
+      content model is "head.misc" combined with a single
+      title and an optional base element in any order
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:group ref="head.misc"/>
+        <xs:choice>
+          <xs:sequence>
+            <xs:element ref="title"/>
+            <xs:group ref="head.misc"/>
+            <xs:sequence minOccurs="0">
+              <xs:element ref="base"/>
+              <xs:group ref="head.misc"/>
+            </xs:sequence>
+          </xs:sequence>
+          <xs:sequence>
+            <xs:element ref="base"/>
+            <xs:group ref="head.misc"/>
+            <xs:element ref="title"/>
+            <xs:group ref="head.misc"/>
+          </xs:sequence>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="profile" type="URI"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="title">
+    <xs:annotation>
+      <xs:documentation>
+      The title element is not considered part of the flow of text.
+      It should be displayed, for example as the page header or
+      window title. Exactly one title is required per document.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:ID"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="base">
+    <xs:annotation>
+      <xs:documentation>
+      document base URI
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="href" type="URI"/>
+      <xs:attribute name="target" type="FrameTarget"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="meta">
+    <xs:annotation>
+      <xs:documentation>
+      generic metainformation
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="http-equiv"/>
+      <xs:attribute name="name"/>
+      <xs:attribute name="content" use="required"/>
+      <xs:attribute name="scheme"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="link">
+    <xs:annotation>
+      <xs:documentation>
+      Relationship values can be used in principle:
+
+      a) for document specific toolbars/menus when used
+         with the link element in document head e.g.
+           start, contents, previous, next, index, end, help
+      b) to link to a separate style sheet (rel="stylesheet")
+      c) to make a link to a script (rel="script")
+      d) by stylesheets to control how collections of
+         html nodes are rendered into printed documents
+      e) to make a link to a printable version of this document
+         e.g. a PostScript or PDF version (rel="alternate" media="print")
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="charset" type="Charset"/>
+      <xs:attribute name="href" type="URI"/>
+      <xs:attribute name="hreflang" type="LanguageCode"/>
+      <xs:attribute name="type" type="ContentType"/>
+      <xs:attribute name="rel" type="LinkTypes"/>
+      <xs:attribute name="rev" type="LinkTypes"/>
+      <xs:attribute name="media" type="MediaDesc"/>
+      <xs:attribute name="target" type="FrameTarget"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="style">
+    <xs:annotation>
+      <xs:documentation>
+      style info, which may include CDATA sections
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="type" use="required" type="ContentType"/>
+      <xs:attribute name="media" type="MediaDesc"/>
+      <xs:attribute name="title" type="Text"/>
+      <xs:attribute ref="xml:space" fixed="preserve"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="script">
+    <xs:annotation>
+      <xs:documentation>
+      script statements, which may include CDATA sections
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="charset" type="Charset"/>
+      <xs:attribute name="type" use="required" type="ContentType"/>
+      <xs:attribute name="language"/>
+      <xs:attribute name="src" type="URI"/>
+      <xs:attribute name="defer">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="defer"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute ref="xml:space" fixed="preserve"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="noscript">
+    <xs:annotation>
+      <xs:documentation>
+      alternate content container for non script-based rendering
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ======================= Frames =======================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="iframe">
+    <xs:annotation>
+      <xs:documentation>
+      inline subwindow
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="coreattrs"/>
+          <xs:attribute name="longdesc" type="URI"/>
+          <xs:attribute name="name" type="xs:NMTOKEN"/>
+          <xs:attribute name="src" type="URI"/>
+          <xs:attribute name="frameborder" default="1">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="1"/>
+                <xs:enumeration value="0"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="marginwidth" type="Pixels"/>
+          <xs:attribute name="marginheight" type="Pixels"/>
+          <xs:attribute name="scrolling" default="auto">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="yes"/>
+                <xs:enumeration value="no"/>
+                <xs:enumeration value="auto"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="align" type="ImgAlign"/>
+          <xs:attribute name="height" type="Length"/>
+          <xs:attribute name="width" type="Length"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="noframes">
+    <xs:annotation>
+      <xs:documentation>
+      alternate content container for non frame-based rendering
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Document Body ====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="div">
+    <xs:annotation>
+      <xs:documentation>
+      generic language/style container
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Paragraphs =======================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="p">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Headings =========================================
+
+    There are six levels of headings from h1 (the most important)
+    to h6 (the least important).
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="h1">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h2">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h3">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h4">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h5">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h6">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Lists ============================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:simpleType name="ULStyle">
+    <xs:annotation>
+      <xs:documentation>
+      Unordered list bullet styles
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="disc"/>
+      <xs:enumeration value="square"/>
+      <xs:enumeration value="circle"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="ul">
+    <xs:annotation>
+      <xs:documentation>
+      Unordered list
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="li"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="type" type="ULStyle"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="OLStyle">
+    <xs:annotation>
+      <xs:documentation>
+      Ordered list numbering style
+
+      1   arabic numbers      1, 2, 3, ...
+      a   lower alpha         a, b, c, ...
+      A   upper alpha         A, B, C, ...
+      i   lower roman         i, ii, iii, ...
+      I   upper roman         I, II, III, ...
+
+      The style is applied to the sequence number which by default
+      is reset to 1 for the first list item in an ordered list.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:element name="ol">
+    <xs:annotation>
+      <xs:documentation>
+      Ordered (numbered) list
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="li"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="type" type="OLStyle"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="start" type="Number"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="menu">
+    <xs:annotation>
+      <xs:documentation>
+      single column list (DEPRECATED)
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="li"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="dir">
+    <xs:annotation>
+      <xs:documentation>
+      multiple column list (DEPRECATED)
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="li"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="LIStyle">
+    <xs:annotation>
+      <xs:documentation>
+      LIStyle is constrained to: "(ULStyle|OLStyle)"
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:element name="li">
+    <xs:annotation>
+      <xs:documentation>
+      list item
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="type" type="LIStyle"/>
+          <xs:attribute name="value" type="Number"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    definition lists - dt for term, dd for its definition
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="dl">
+    <xs:complexType>
+      <xs:choice maxOccurs="unbounded">
+        <xs:element ref="dt"/>
+        <xs:element ref="dd"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="dt">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="dd">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Address ==========================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="address">
+    <xs:annotation>
+      <xs:documentation>
+      information on author
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:group ref="inline"/>
+        <xs:group ref="misc.inline"/>
+        <xs:element ref="p"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Horizontal Rule ==================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="hr">
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="align">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="left"/>
+            <xs:enumeration value="center"/>
+            <xs:enumeration value="right"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="noshade">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="noshade"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="size" type="Pixels"/>
+      <xs:attribute name="width" type="Length"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Preformatted Text ================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="pre">
+    <xs:annotation>
+      <xs:documentation>
+      content is "Inline" excluding
+         "img|object|applet|big|small|sub|sup|font|basefont"
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="pre.content">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="width" type="Number"/>
+          <xs:attribute ref="xml:space" fixed="preserve"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Block-like Quotes ================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="blockquote">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="cite" type="URI"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Text alignment ===================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="center">
+    <xs:annotation>
+      <xs:documentation>
+      center content
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Inserted/Deleted Text ============================
+
+    ins/del are allowed in block and inline content, but its
+    inappropriate to include block content within an ins element
+    occurring in inline content.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="ins">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="cite" type="URI"/>
+          <xs:attribute name="datetime" type="Datetime"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="del">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="cite" type="URI"/>
+          <xs:attribute name="datetime" type="Datetime"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== The Anchor Element ================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="a">
+    <xs:annotation>
+      <xs:documentation>
+      content is "Inline" except that anchors shouldn't be nested
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="a.content">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="focus"/>
+          <xs:attribute name="charset" type="Charset"/>
+          <xs:attribute name="type" type="ContentType"/>
+          <xs:attribute name="name" type="xs:NMTOKEN"/>
+          <xs:attribute name="href" type="URI"/>
+          <xs:attribute name="hreflang" type="LanguageCode"/>
+          <xs:attribute name="rel" type="LinkTypes"/>
+          <xs:attribute name="rev" type="LinkTypes"/>
+          <xs:attribute name="shape" default="rect" type="Shape"/>
+          <xs:attribute name="coords" type="Coords"/>
+          <xs:attribute name="target" type="FrameTarget"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ===================== Inline Elements ================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="span">
+    <xs:annotation>
+      <xs:documentation>
+      generic language/style container
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="bdo">
+    <xs:annotation>
+      <xs:documentation>
+      I18N BiDi over-ride
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="coreattrs"/>
+          <xs:attributeGroup ref="events"/>
+          <xs:attribute name="lang" type="LanguageCode"/>
+          <xs:attribute ref="xml:lang"/>
+          <xs:attribute name="dir" use="required">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="ltr"/>
+                <xs:enumeration value="rtl"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="br">
+    <xs:annotation>
+      <xs:documentation>
+      forced line break
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="coreattrs"/>
+      <xs:attribute name="clear" default="none">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="left"/>
+            <xs:enumeration value="all"/>
+            <xs:enumeration value="right"/>
+            <xs:enumeration value="none"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="em">
+    <xs:annotation>
+      <xs:documentation>
+      emphasis
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="strong">
+    <xs:annotation>
+      <xs:documentation>
+      strong emphasis
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="dfn">
+    <xs:annotation>
+      <xs:documentation>
+      definitional
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="code">
+    <xs:annotation>
+      <xs:documentation>
+      program code
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="samp">
+    <xs:annotation>
+      <xs:documentation>
+      sample
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="kbd">
+    <xs:annotation>
+      <xs:documentation>
+      something user would type
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="var">
+    <xs:annotation>
+      <xs:documentation>
+      variable
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="cite">
+    <xs:annotation>
+      <xs:documentation>
+      citation
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="abbr">
+    <xs:annotation>
+      <xs:documentation>
+      abbreviation
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="acronym">
+    <xs:annotation>
+      <xs:documentation>
+      acronym
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="q">
+    <xs:annotation>
+      <xs:documentation>
+      inlined quote
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="cite" type="URI"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="sub">
+    <xs:annotation>
+      <xs:documentation>
+      subscript
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="sup">
+    <xs:annotation>
+      <xs:documentation>
+      superscript
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="tt">
+    <xs:annotation>
+      <xs:documentation>
+      fixed pitch font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="i">
+    <xs:annotation>
+      <xs:documentation>
+      italic font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="b">
+    <xs:annotation>
+      <xs:documentation>
+      bold font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="big">
+    <xs:annotation>
+      <xs:documentation>
+      bigger font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="small">
+    <xs:annotation>
+      <xs:documentation>
+      smaller font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="u">
+    <xs:annotation>
+      <xs:documentation>
+      underline
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="s">
+    <xs:annotation>
+      <xs:documentation>
+      strike-through
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="strike">
+    <xs:annotation>
+      <xs:documentation>
+      strike-through
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="basefont">
+    <xs:annotation>
+      <xs:documentation>
+      base font size
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="size" use="required"/>
+      <xs:attribute name="color" type="Color"/>
+      <xs:attribute name="face"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="font">
+    <xs:annotation>
+      <xs:documentation>
+      local change to font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="coreattrs"/>
+          <xs:attributeGroup ref="i18n"/>
+          <xs:attribute name="size"/>
+          <xs:attribute name="color" type="Color"/>
+          <xs:attribute name="face"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ==================== Object ======================================
+
+    object is used to embed objects as part of HTML pages.
+    param elements should precede other content. Parameters
+    can also be expressed as attribute/value pairs on the
+    object element itself when brevity is desired.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="object">
+    <xs:complexType mixed="true">
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element ref="param"/>
+        <xs:group ref="block"/>
+        <xs:element ref="form"/>
+        <xs:group ref="inline"/>
+        <xs:group ref="misc"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="declare">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="declare"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="classid" type="URI"/>
+      <xs:attribute name="codebase" type="URI"/>
+      <xs:attribute name="data" type="URI"/>
+      <xs:attribute name="type" type="ContentType"/>
+      <xs:attribute name="codetype" type="ContentType"/>
+      <xs:attribute name="archive" type="UriList"/>
+      <xs:attribute name="standby" type="Text"/>
+      <xs:attribute name="height" type="Length"/>
+      <xs:attribute name="width" type="Length"/>
+      <xs:attribute name="usemap" type="URI"/>
+      <xs:attribute name="name" type="xs:NMTOKEN"/>
+      <xs:attribute name="tabindex" type="Number"/>
+      <xs:attribute name="align" type="ImgAlign"/>
+      <xs:attribute name="border" type="Pixels"/>
+      <xs:attribute name="hspace" type="Pixels"/>
+      <xs:attribute name="vspace" type="Pixels"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="param">
+    <xs:annotation>
+      <xs:documentation>
+      param is used to supply a named property value.
+      In XML it would seem natural to follow RDF and support an
+      abbreviated syntax where the param elements are replaced
+      by attribute value pairs on the object start tag.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="name" use="required"/>
+      <xs:attribute name="value"/>
+      <xs:attribute name="valuetype" default="data">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="data"/>
+            <xs:enumeration value="ref"/>
+            <xs:enumeration value="object"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="type" type="ContentType"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Java applet ==================================
+
+    One of code or object attributes must be present.
+    Place param elements before other content.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="applet">
+    <xs:complexType mixed="true">
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element ref="param"/>
+        <xs:group ref="block"/>
+        <xs:element ref="form"/>
+        <xs:group ref="inline"/>
+        <xs:group ref="misc"/>
+      </xs:choice>
+      <xs:attributeGroup ref="coreattrs"/>
+      <xs:attribute name="codebase" type="URI"/>
+      <xs:attribute name="archive"/>
+      <xs:attribute name="code"/>
+      <xs:attribute name="object"/>
+      <xs:attribute name="alt" type="Text"/>
+      <xs:attribute name="name" type="xs:NMTOKEN"/>
+      <xs:attribute name="width" use="required" type="Length"/>
+      <xs:attribute name="height" use="required" type="Length"/>
+      <xs:attribute name="align" type="ImgAlign"/>
+      <xs:attribute name="hspace" type="Pixels"/>
+      <xs:attribute name="vspace" type="Pixels"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Images ===========================================
+
+    To avoid accessibility problems for people who aren't
+    able to see the image, you should provide a text
+    description using the alt and longdesc attributes.
+    In addition, avoid the use of server-side image maps.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="img">
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="src" use="required" type="URI"/>
+      <xs:attribute name="alt" use="required" type="Text"/>
+      <xs:attribute name="name" type="xs:NMTOKEN"/>
+      <xs:attribute name="longdesc" type="URI"/>
+      <xs:attribute name="height" type="Length"/>
+      <xs:attribute name="width" type="Length"/>
+      <xs:attribute name="usemap" type="URI">
+  <xs:annotation>
+    <xs:documentation>
+          usemap points to a map element which may be in this document
+          or an external document, although the latter is not widely supported
+          </xs:documentation>
+  </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="ismap">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="ismap"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="align" type="ImgAlign"/>
+      <xs:attribute name="border" type="Length"/>
+      <xs:attribute name="hspace" type="Pixels"/>
+      <xs:attribute name="vspace" type="Pixels"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== Client-side image maps ============================
+
+    These can be placed in the same document or grouped in a
+    separate document although this isn't yet widely supported
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="map">
+    <xs:complexType>
+      <xs:choice>
+        <xs:choice maxOccurs="unbounded">
+          <xs:group ref="block"/>
+          <xs:element ref="form"/>
+          <xs:group ref="misc"/>
+        </xs:choice>
+        <xs:element maxOccurs="unbounded" ref="area"/>
+      </xs:choice>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attributeGroup ref="events"/>
+      <xs:attribute name="id" use="required" type="xs:ID"/>
+      <xs:attribute name="class"/>
+      <xs:attribute name="style" type="StyleSheet"/>
+      <xs:attribute name="title" type="Text"/>
+      <xs:attribute name="name"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="area">
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="focus"/>
+      <xs:attribute name="shape" default="rect" type="Shape"/>
+      <xs:attribute name="coords" type="Coords"/>
+      <xs:attribute name="href" type="URI"/>
+      <xs:attribute name="nohref">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="nohref"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="alt" use="required" type="Text"/>
+      <xs:attribute name="target" type="FrameTarget"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================ Forms ===============================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="form">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="form.content">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="action" use="required" type="URI"/>
+          <xs:attribute name="method" default="get">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="get"/>
+                <xs:enumeration value="post"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="enctype" type="ContentType" default="application/x-www-form-urlencoded"/>
+          <xs:attribute name="onsubmit" type="Script"/>
+          <xs:attribute name="onreset" type="Script"/>
+          <xs:attribute name="accept" type="ContentTypes"/>
+          <xs:attribute name="accept-charset" type="Charsets"/>
+          <xs:attribute name="target" type="FrameTarget"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="label">
+    <xs:annotation>
+      <xs:documentation>
+      Each label must not contain more than ONE field
+      Label elements shouldn't be nested.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="for" type="xs:IDREF"/>
+          <xs:attribute name="accesskey" type="Character"/>
+          <xs:attribute name="onfocus" type="Script"/>
+          <xs:attribute name="onblur" type="Script"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="InputType">
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="text"/>
+      <xs:enumeration value="password"/>
+      <xs:enumeration value="checkbox"/>
+      <xs:enumeration value="radio"/>
+      <xs:enumeration value="submit"/>
+      <xs:enumeration value="reset"/>
+      <xs:enumeration value="file"/>
+      <xs:enumeration value="hidden"/>
+      <xs:enumeration value="image"/>
+      <xs:enumeration value="button"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="input">
+    <xs:annotation>
+      <xs:documentation>
+      form control
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="focus"/>
+      <xs:attribute name="type" default="text" type="InputType"/>
+      <xs:attribute name="name">
+  <xs:annotation>
+    <xs:documentation>
+          the name attribute is required for all but submit & reset
+          </xs:documentation>
+  </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="value"/>
+      <xs:attribute name="checked">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="checked"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="readonly">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="readonly"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="size"/>
+      <xs:attribute name="maxlength" type="Number"/>
+      <xs:attribute name="src" type="URI"/>
+      <xs:attribute name="alt"/>
+      <xs:attribute name="usemap" type="URI"/>
+      <xs:attribute name="onselect" type="Script"/>
+      <xs:attribute name="onchange" type="Script"/>
+      <xs:attribute name="accept" type="ContentTypes"/>
+      <xs:attribute name="align" type="ImgAlign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="select">
+    <xs:annotation>
+      <xs:documentation>
+      option selector
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:choice maxOccurs="unbounded">
+        <xs:element ref="optgroup"/>
+        <xs:element ref="option"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="name"/>
+      <xs:attribute name="size" type="Number"/>
+      <xs:attribute name="multiple">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="multiple"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="tabindex" type="tabindexNumber"/>
+      <xs:attribute name="onfocus" type="Script"/>
+      <xs:attribute name="onblur" type="Script"/>
+      <xs:attribute name="onchange" type="Script"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="optgroup">
+    <xs:annotation>
+      <xs:documentation>
+      option group
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="option"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="label" use="required" type="Text"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="option">
+    <xs:annotation>
+      <xs:documentation>
+      selectable choice
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="selected">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="selected"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="label" type="Text"/>
+      <xs:attribute name="value"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="textarea">
+    <xs:annotation>
+      <xs:documentation>
+      multi-line text field
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="focus"/>
+      <xs:attribute name="name"/>
+      <xs:attribute name="rows" use="required" type="Number"/>
+      <xs:attribute name="cols" use="required" type="Number"/>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="readonly">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="readonly"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="onselect" type="Script"/>
+      <xs:attribute name="onchange" type="Script"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="fieldset">
+    <xs:annotation>
+      <xs:documentation>
+      The fieldset element is used to group form fields.
+      Only one legend element should occur in the content
+      and if present should only be preceded by whitespace.
+
+      NOTE: this content model is different from the XHTML 1.0 DTD,
+      closer to the intended content model in HTML4 DTD
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:sequence>
+        <xs:element ref="legend"/>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:group ref="block"/>
+          <xs:element ref="form"/>
+          <xs:group ref="inline"/>
+          <xs:group ref="misc"/>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="LAlign">
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="top"/>
+      <xs:enumeration value="bottom"/>
+      <xs:enumeration value="left"/>
+      <xs:enumeration value="right"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="legend">
+    <xs:annotation>
+      <xs:documentation>
+      fieldset label
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="accesskey" type="Character"/>
+          <xs:attribute name="align" type="LAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="button">
+    <xs:annotation>
+      <xs:documentation>
+      Content is "Flow" excluding a, form and form controls
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="button.content">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="focus"/>
+          <xs:attribute name="name"/>
+          <xs:attribute name="value"/>
+          <xs:attribute name="type" default="submit">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="button"/>
+                <xs:enumeration value="submit"/>
+                <xs:enumeration value="reset"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="disabled">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="disabled"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="isindex">
+    <xs:annotation>
+      <xs:documentation>
+      single-line text input control (DEPRECATED)
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="coreattrs"/>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="prompt" type="Text"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ======================= Tables =======================================
+
+    Derived from IETF HTML table standard, see [RFC1942]
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:simpleType name="TFrame">
+    <xs:annotation>
+      <xs:documentation>
+      The border attribute sets the thickness of the frame around the
+      table. The default units are screen pixels.
+
+      The frame attribute specifies which parts of the frame around
+      the table should be rendered. The values are not the same as
+      CALS to avoid a name clash with the valign attribute.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="void"/>
+      <xs:enumeration value="above"/>
+      <xs:enumeration value="below"/>
+      <xs:enumeration value="hsides"/>
+      <xs:enumeration value="lhs"/>
+      <xs:enumeration value="rhs"/>
+      <xs:enumeration value="vsides"/>
+      <xs:enumeration value="box"/>
+      <xs:enumeration value="border"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="TRules">
+    <xs:annotation>
+      <xs:documentation>
+      The rules attribute defines which rules to draw between cells:
+
+      If rules is absent then assume:
+          "none" if border is absent or border="0" otherwise "all"
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="none"/>
+      <xs:enumeration value="groups"/>
+      <xs:enumeration value="rows"/>
+      <xs:enumeration value="cols"/>
+      <xs:enumeration value="all"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="TAlign">
+    <xs:annotation>
+      <xs:documentation>
+      horizontal placement of table relative to document
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="left"/>
+      <xs:enumeration value="center"/>
+      <xs:enumeration value="right"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:attributeGroup name="cellhalign">
+    <xs:annotation>
+      <xs:documentation>
+      horizontal alignment attributes for cell contents
+
+      char        alignment char, e.g. char=':'
+      charoff     offset for alignment char
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="align">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="left"/>
+          <xs:enumeration value="center"/>
+          <xs:enumeration value="right"/>
+          <xs:enumeration value="justify"/>
+          <xs:enumeration value="char"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+    <xs:attribute name="char" type="Character"/>
+    <xs:attribute name="charoff" type="Length"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="cellvalign">
+    <xs:annotation>
+      <xs:documentation>
+      vertical alignment attributes for cell contents
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="valign">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="top"/>
+          <xs:enumeration value="middle"/>
+          <xs:enumeration value="bottom"/>
+          <xs:enumeration value="baseline"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:attributeGroup>
+
+  <xs:element name="table">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element minOccurs="0" ref="caption"/>
+        <xs:choice>
+          <xs:element minOccurs="0" maxOccurs="unbounded" ref="col"/>
+          <xs:element minOccurs="0" maxOccurs="unbounded" ref="colgroup"/>
+        </xs:choice>
+        <xs:element minOccurs="0" ref="thead"/>
+        <xs:element minOccurs="0" ref="tfoot"/>
+        <xs:choice>
+          <xs:element maxOccurs="unbounded" ref="tbody"/>
+          <xs:element maxOccurs="unbounded" ref="tr"/>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="summary" type="Text"/>
+      <xs:attribute name="width" type="Length"/>
+      <xs:attribute name="border" type="Pixels"/>
+      <xs:attribute name="frame" type="TFrame"/>
+      <xs:attribute name="rules" type="TRules"/>
+      <xs:attribute name="cellspacing" type="Length"/>
+      <xs:attribute name="cellpadding" type="Length"/>
+      <xs:attribute name="align" type="TAlign"/>
+      <xs:attribute name="bgcolor" type="Color"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="CAlign">
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="top"/>
+      <xs:enumeration value="bottom"/>
+      <xs:enumeration value="left"/>
+      <xs:enumeration value="right"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="caption">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="align" type="CAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    Use thead to duplicate headers when breaking table
+    across page boundaries, or for static headers when
+    tbody sections are rendered in scrolling panel.
+
+    Use tfoot to duplicate footers when breaking table
+    across page boundaries, or for static footers when
+    tbody sections are rendered in scrolling panel.
+
+    Use multiple tbody sections when rules are needed
+    between groups of table rows.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="thead">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="tr"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="tfoot">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="tr"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="tbody">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="tr"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="colgroup">
+    <xs:annotation>
+      <xs:documentation>
+      colgroup groups a set of col elements. It allows you to group
+      several semantically related columns together.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element minOccurs="0" maxOccurs="unbounded" ref="col"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="span" default="1" type="Number"/>
+      <xs:attribute name="width" type="MultiLength"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="col">
+    <xs:annotation>
+      <xs:documentation>
+      col elements define the alignment properties for cells in
+      one or more columns.
+
+      The width attribute specifies the width of the columns, e.g.
+
+          width=64        width in screen pixels
+          width=0.5*      relative width of 0.5
+
+      The span attribute causes the attributes of one
+      col element to apply to more than one column.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="span" default="1" type="Number"/>
+      <xs:attribute name="width" type="MultiLength"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="tr">
+    <xs:complexType>
+      <xs:choice maxOccurs="unbounded">
+        <xs:element ref="th"/>
+        <xs:element ref="td"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+      <xs:attribute name="bgcolor" type="Color"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="Scope">
+    <xs:annotation>
+      <xs:documentation>
+      Scope is simpler than headers attribute for common tables
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="row"/>
+      <xs:enumeration value="col"/>
+      <xs:enumeration value="rowgroup"/>
+      <xs:enumeration value="colgroup"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:annotation>
+    <xs:documentation>
+    th is for headers, td for data and for cells acting as both
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="th">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="abbr" type="Text"/>
+          <xs:attribute name="axis"/>
+          <xs:attribute name="headers" type="xs:IDREFS"/>
+          <xs:attribute name="scope" type="Scope"/>
+          <xs:attribute name="rowspan" default="1" type="Number"/>
+          <xs:attribute name="colspan" default="1" type="Number"/>
+          <xs:attributeGroup ref="cellhalign"/>
+          <xs:attributeGroup ref="cellvalign"/>
+          <xs:attribute name="nowrap">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="nowrap"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="bgcolor" type="Color"/>
+          <xs:attribute name="width" type="Length"/>
+          <xs:attribute name="height" type="Length"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="td">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="abbr" type="Text"/>
+          <xs:attribute name="axis"/>
+          <xs:attribute name="headers" type="xs:IDREFS"/>
+          <xs:attribute name="scope" type="Scope"/>
+          <xs:attribute name="rowspan" default="1" type="Number"/>
+          <xs:attribute name="colspan" default="1" type="Number"/>
+          <xs:attributeGroup ref="cellhalign"/>
+          <xs:attributeGroup ref="cellvalign"/>
+          <xs:attribute name="nowrap">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="nowrap"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="bgcolor" type="Color"/>
+          <xs:attribute name="width" type="Length"/>
+          <xs:attribute name="height" type="Length"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+      ================== FML Specific =====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:annotation>
+    <xs:documentation>
+      ================ Document Structure ==================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="faqs">
+    <xs:annotation>
+      <xs:documentation source="version">1.0.0</xs:documentation>
+      <xs:documentation source="description">
+        The <faqs/> element is the root of the FML descriptor.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="part" minOccurs="1" maxOccurs="unbounded">
+          <xs:annotation>
+            <xs:documentation source="version">1.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Required part element for this faqs element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+      </xs:sequence>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:string" default="FAQ">
+        <xs:annotation>
+          <xs:documentation source="version">1.0.0</xs:documentation>
+          <xs:documentation source="description">
+            The identifier for this faqs element.
+          </xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="title" type="Text" default="Frequently Asked Questions">
+        <xs:annotation>
+          <xs:documentation source="version">1.0.0</xs:documentation>
+          <xs:documentation source="description">
+            The title for this faqs element.
+          </xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="toplink" type="xs:boolean" default="true">
+        <xs:annotation>
+          <xs:documentation source="version">1.0.0</xs:documentation>
+          <xs:documentation source="description">
+            Boolean to generate optionally [top] links.
+          </xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="part">
+    <xs:annotation>
+      <xs:documentation source="version">1.0</xs:documentation>
+      <xs:documentation source="description">
+        A part element of the faqs element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="title" minOccurs="0" maxOccurs="1">
+          <xs:annotation>
+            <xs:documentation source="version">1.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Optional title for this part element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+        <xs:element ref="faq" minOccurs="1" maxOccurs="unbounded">
+          <xs:annotation>
+            <xs:documentation source="version">1.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Required faq element for this part element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+      </xs:sequence>
+      <xs:attribute name="id" type="xs:string">
+        <xs:annotation>
+          <xs:documentation source="version">1.0.0</xs:documentation>
+          <xs:documentation source="description">
+            The identifier for this part element.
+          </xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="faq">
+    <xs:annotation>
+      <xs:documentation source="version">1.0.0</xs:documentation>
+      <xs:documentation source="description">
+        A faq element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:sequence>
+        <xs:element ref="question" minOccurs="1" maxOccurs="1">
+          <xs:annotation>
+            <xs:documentation source="version">1.0.0</xs:documentation>
+            <xs:documentation source="description">
+              The question of this faq element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+        <xs:element ref="answer" minOccurs="1" maxOccurs="1">
+          <xs:annotation>
+            <xs:documentation source="version">1.0.0</xs:documentation>
+            <xs:documentation source="description">
+              The answer of this faq element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+      </xs:sequence>
+      <xs:attribute name="id" type="xs:string">
+        <xs:annotation>
+          <xs:documentation source="version">1.0.0</xs:documentation>
+          <xs:documentation source="description">
+            The identifier of this faq element.
+          </xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="question">
+    <xs:annotation>
+      <xs:documentation source="version">1.0.0</xs:documentation>
+      <xs:documentation source="description">
+        A faq question element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:sequence>
+        <xs:group ref="fontstyle" minOccurs="0" maxOccurs="unbounded"/>
+        <xs:group ref="phrase"  minOccurs="0" maxOccurs="unbounded"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="answer">
+    <xs:annotation>
+      <xs:documentation source="version">1.0.0</xs:documentation>
+      <xs:documentation source="description">
+        A faq answer element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:group ref="block"/>
+        <xs:element ref="form"/>
+        <xs:group ref="inline"/>
+        <xs:group ref="misc"/>
+      </xs:choice>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+      ================ Document Addons =====================================
+      See "special.extra" group.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="source">
+    <xs:annotation>
+      <xs:documentation source="version">1.0.0</xs:documentation>
+      <xs:documentation source="description">
+        A source element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType  mixed="true">
+      <xs:sequence>
+        <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="macro">
+    <xs:annotation>
+      <xs:documentation source="version">1.0.1</xs:documentation>
+      <xs:documentation source="description">
+        A macro element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element ref="param"/>
+      </xs:choice>
+      <xs:attribute name="name" use="required" type="Text"/>
+    </xs:complexType>
+  </xs:element>
+</xs:schema>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-fml/src/site/apt/using-fml-xsd.apt b/doxia-modules/doxia-module-fml/src/site/apt/using-fml-xsd.apt
new file mode 100644
index 0000000..12fce0b
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/site/apt/using-fml-xsd.apt
@@ -0,0 +1,43 @@
+ -----
+ Using Schema FML 1.0.1
+ -----
+ Vincent Siveton
+ ------
+ 2009-06-15
+ ------
+
+~~ Licensed to the Apache Software Foundation (ASF) under one
+~~ or more contributor license agreements.  See the NOTICE file
+~~ distributed with this work for additional information
+~~ regarding copyright ownership.  The ASF licenses this file
+~~ to you under the Apache License, Version 2.0 (the
+~~ "License"); you may not use this file except in compliance
+~~ with the License.  You may obtain a copy of the License at
+~~
+~~   http://www.apache.org/licenses/LICENSE-2.0
+~~
+~~ Unless required by applicable law or agreed to in writing,
+~~ software distributed under the License is distributed on an
+~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+~~ KIND, either express or implied.  See the License for the
+~~ specific language governing permissions and limitations
+~~ under the License.
+
+~~ NOTE: For help with the syntax of this file, see:
+~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Using Schema FML 1.0.1
+
+  The FML XSD is located {{{http://maven.apache.org/xsd/fml-1.0.1.xsd}here}}.
+
+  Your favorite IDE probably supports XSD schema's for .fml files. You need to specify the following:
+
++-----+
+
+<faqs xmlns="http://maven.apache.org/FML/1.0.1"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/FML/1.0.1 http://maven.apache.org/xsd/fml-1.0.1.xsd"
+  title="...">
+...
+</faqs>
++-----+
diff --git a/doxia-modules/doxia-module-fml/src/site/site.xml b/doxia-modules/doxia-module-fml/src/site/site.xml
new file mode 100644
index 0000000..66b3525
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/site/site.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 ../../../../../doxia-sitetools/doxia-decoration-model/target/generated-site/xsd/decoration-1.0.0.xsd">
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu name="Schema FML 1.0.1">
+      <item name="Reference of Schema FML" href="xsddoc/index.html"/>
+      <item name="Using Schema FML" href="using-fml-xsd.html"/>
+    </menu>
+
+    <menu ref="reports"/>
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-fml/src/test/java/org/apache/maven/doxia/module/fml/FmlParserTest.java b/doxia-modules/doxia-module-fml/src/test/java/org/apache/maven/doxia/module/fml/FmlParserTest.java
new file mode 100644
index 0000000..05c245f
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/test/java/org/apache/maven/doxia/module/fml/FmlParserTest.java
@@ -0,0 +1,298 @@
+package org.apache.maven.doxia.module.fml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.Reader;
+import java.io.Writer;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.doxia.parser.AbstractParserTest;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+import org.apache.maven.doxia.sink.XhtmlBaseSink;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * @author <a href="mailto:evenisse at codehaus.org">Emmanuel Venisse</a>
+ * @version $Id: FmlParserTest.java 784948 2009-06-15 19:53:35Z ltheussl $
+ */
+public class FmlParserTest
+    extends AbstractParserTest
+{
+    private FmlParser parser;
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        parser = (FmlParser) lookup( Parser.ROLE, "fml" );
+
+        // AbstractXmlParser.CachedFileEntityResolver downloads DTD/XSD files in ${java.io.tmpdir}
+        // Be sure to delete them
+        String tmpDir = System.getProperty( "java.io.tmpdir" );
+        String excludes = "fml-*.xsd, xml.xsd";
+        List tmpFiles = FileUtils.getFileNames( new File( tmpDir ), excludes, null, true );
+        for ( Iterator it = tmpFiles.iterator(); it.hasNext(); )
+        {
+            File tmpFile = new File( it.next().toString() );
+            tmpFile.delete();
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return parser;
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "fml";
+    }
+
+    /** @throws Exception */
+    public void testFaqEventsList()
+        throws Exception
+    {
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        Reader reader = null;
+        try
+        {
+            reader = getTestReader( "simpleFaq" );
+
+            createParser().parse( reader, sink );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "comment", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "anchor", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedList", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedListItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedListItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedList_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionList", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definedTerm", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "anchor", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definedTerm_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definition", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definition_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionList_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception */
+    public void testEntities()
+        throws Exception
+    {
+        final String text = "<!DOCTYPE test [<!ENTITY Alpha \"Α\">]>"
+                + "<faqs title=\"&Α\"><part id=\"General\"><title><Α</title>"
+                + "<faq id=\"id\"><question>>Α</question>"
+                + "<answer><p><code><img></code>"Α</p></answer>"
+                + "</faq></part></faqs>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.setValidate( false );
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title", ( (SinkEventElement) it.next() ).getName() );
+
+        // head title TODO: should be two events
+        assertTextEvent( (SinkEventElement) it.next(), "&Α" );
+
+        assertEquals( "title_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "anchor", ( (SinkEventElement) it.next() ).getName() );
+
+        // faq title TODO: should be two events
+        assertTextEvent( (SinkEventElement) it.next(), "&Α" );
+
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "bold", ( (SinkEventElement) it.next() ).getName() );
+
+        // part title in TOC
+        assertTextEvent( (SinkEventElement) it.next(), "<" );
+        assertTextEvent( (SinkEventElement) it.next(), "\u0391" );
+
+        assertEquals( "bold_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedList", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedListItem", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+
+        // question in TOC
+        assertTextEvent( (SinkEventElement) it.next(), ">" );
+        assertTextEvent( (SinkEventElement) it.next(), "\u0391" );
+
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedListItem_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "numberedList_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+
+        // part title
+        assertTextEvent( (SinkEventElement) it.next(), "<" );
+        assertTextEvent( (SinkEventElement) it.next(), "\u0391" );
+
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionList", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definedTerm", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "anchor", ( (SinkEventElement) it.next() ).getName() );
+
+        // question
+        assertTextEvent( (SinkEventElement) it.next(), ">" );
+        assertTextEvent( (SinkEventElement) it.next(), "\u0391" );
+
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definedTerm_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definition", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+
+        // answer
+        assertEquals( "monospaced", ( (SinkEventElement) it.next() ).getName() );
+        assertTextEvent( (SinkEventElement) it.next(), "<" );
+        assertTextEvent( (SinkEventElement) it.next(), "img" );
+        assertTextEvent( (SinkEventElement) it.next(), ">" );
+        assertEquals( "monospaced_", ( (SinkEventElement) it.next() ).getName() );
+        assertTextEvent( (SinkEventElement) it.next(), "\"" );
+        assertTextEvent( (SinkEventElement) it.next(), "\u0391" );
+
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definition_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "definitionList_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertFalse( it.hasNext() );
+    }
+
+    /**
+     * @throws Exception if any
+     * @since 1.1.1
+     */
+    public void testFaqMacro()
+        throws Exception
+    {
+        Writer output = null;
+        Reader reader = null;
+        try
+        {
+            output = getTestWriter( "macro" );
+            reader = getTestReader( "macro" );
+
+            Sink sink = new XhtmlBaseSink( output );
+            createParser().parse( reader, sink );
+            sink.close();
+        }
+        finally
+        {
+            IOUtil.close( output );
+            IOUtil.close( reader );
+        }
+
+        File f = getTestFile( getBasedir(), outputBaseDir() + getOutputDir() + "macro.fml" );
+        assertTrue( "The file " + f.getAbsolutePath() + " was not created", f.exists() );
+
+        String content;
+        try
+        {
+            reader = new FileReader( f );
+            content = IOUtil.toString( reader );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+
+        assertTrue( content.indexOf( "<a name=\"macro-definition\">Macro Question</a>" ) != -1 );
+    }
+
+    private void assertTextEvent( SinkEventElement textEvt, String string )
+    {
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( string, textEvt.getArgs()[0] );
+    }
+}
diff --git a/doxia-modules/doxia-module-fml/src/test/java/org/apache/maven/doxia/module/fml/FmlValidatorTest.java b/doxia-modules/doxia-module-fml/src/test/java/org/apache/maven/doxia/module/fml/FmlValidatorTest.java
new file mode 100644
index 0000000..bb7a194
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/test/java/org/apache/maven/doxia/module/fml/FmlValidatorTest.java
@@ -0,0 +1,86 @@
+package org.apache.maven.doxia.module.fml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.doxia.markup.XmlMarkup;
+import org.apache.maven.doxia.xsd.AbstractXmlValidatorTest;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Test FML files with namespace.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: FmlValidatorTest.java 784718 2009-06-15 10:28:18Z vsiveton $
+ * @since 1.0
+ */
+public class FmlValidatorTest
+    extends AbstractXmlValidatorTest
+{
+    /** The xsd to use */
+    private static final File FML_XSD = new File( getBasedir(), "/src/main/resources/fml-1.0.1.xsd" );
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+    }
+
+    /** {@inheritDoc} */
+    protected void tearDown()
+        throws Exception
+    {
+        super.tearDown();
+    }
+
+    /** {@inheritDoc} */
+    protected String[] getIncludes()
+    {
+        return new String[] { "**/*.fml" };
+    }
+
+    /** {@inheritDoc} */
+    protected String addNamespaces( String content )
+    {
+        Pattern pattern = Pattern.compile( ".*<([A-Za-z][A-Za-z0-9:_.-]*)([^>]*)>.*" );
+        Matcher matcher = pattern.matcher( content );
+        if ( matcher.find() )
+        {
+            String root = matcher.group( 1 );
+            String value = matcher.group( 2 );
+
+            if ( value.indexOf( FML_XSD.getName() ) == -1 )
+            {
+                String faqs =
+                    "<" + root + " xmlns=\"" + FmlMarkup.FML_NAMESPACE + "\""
+                        + "  xmlns:xsi=\"" + XmlMarkup.XML_NAMESPACE + "\""
+                        + "  xsi:schemaLocation=\"" + FmlMarkup.FML_NAMESPACE + " " + FML_XSD.toURI() + "\" ";
+
+                return StringUtils.replace( content, "<" + root, faqs );
+            }
+        }
+
+        return content;
+    }
+}
diff --git a/doxia-modules/doxia-module-fml/src/test/resources/macro.fml b/doxia-modules/doxia-module-fml/src/test/resources/macro.fml
new file mode 100644
index 0000000..d7e997a
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/test/resources/macro.fml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs xmlns="http://maven.apache.org/FML/1.0.1"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/FML/1.0.1 file:../../main/resources/fml-1.0.1.xsd"
+  title="Frequently Asked Questions">
+
+  <part id="general">
+    <title>Test the macro support</title>
+
+    <faq id="test-macro">
+      <question>Question</question>
+      <answer>
+        <p>Answer with macro</p>
+        <macro name="snippet">
+          <param name="id" value="superpom"/>
+          <param name="file" value="src/test/resources/macro.fml"/>
+        </macro>
+      </answer>
+    </faq>
+
+<!-- START SNIPPET: fmlmacro -->
+    <faq id="macro-definition">
+      <question>Macro Question</question>
+      <answer>
+        <p>Macro Answer</p>
+      </answer>
+    </faq>
+<!-- END SNIPPET: fmlmacro -->
+  </part>
+</faqs>
diff --git a/doxia-modules/doxia-module-fml/src/test/resources/simpleFaq.fml b/doxia-modules/doxia-module-fml/src/test/resources/simpleFaq.fml
new file mode 100644
index 0000000..aced132
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/test/resources/simpleFaq.fml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs xmlns="http://maven.apache.org/FML/1.0.1"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/FML/1.0.1 file:../../main/resources/fml-1.0.1.xsd"
+  title="Frequently Asked Questions">
+
+  <part id="general">
+    <title>General</title>
+
+    <faq id="maven-definition">
+      <question>What does Maven mean?</question>
+      <answer><p>A maven (yi.=meyvn) is an experienced or knowledgeable person, such as an expert or freak.</p></answer>
+    </faq>
+
+  </part>
+
+</faqs>
+
diff --git a/doxia-modules/doxia-module-fml/src/test/resources/test.fml b/doxia-modules/doxia-module-fml/src/test/resources/test.fml
new file mode 100644
index 0000000..8128fe9
--- /dev/null
+++ b/doxia-modules/doxia-module-fml/src/test/resources/test.fml
@@ -0,0 +1,769 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<!-- TODO [later]: faqs need some sorting -->
+<faqs xmlns="http://maven.apache.org/FML/1.0.1"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/FML/1.0.1 file:../../main/resources/fml-1.0.1.xsd"
+  title="Frequently Asked Questions">
+
+  <part id="general">
+    <title>General</title>
+
+    <faq id="maven-definition">
+      <question>What does Maven mean?</question>
+      <answer><p>A maven (yi.=meyvn) is an experienced or knowledgeable person, such as an expert or freak.</p></answer>
+    </faq>
+
+  </part>
+  <part id="help">
+    <title>Where Can I Get Help?</title>
+
+    <faq id="where-get-help">
+      <question>Where do I get help on Maven?</question>
+      <answer>
+        <p>
+          For help getting started, or basic use of Maven, refer to the documentation
+          that can be found from the left navigation of this site.
+        </p>
+        <p>
+          If these documents, and the other questions in this FAQ don't help you with your problem, the
+          <a href="mail-lists.html">Maven User List</a> is a good source for help.
+          Lots of problems have already been discussed there, so please search the mailing list archive
+          before posting a question or a new idea.
+          Most of the Maven developers are subscribed to the Maven User List, so there is no need to post
+          to the Maven Developers list unless you want to discuss making a change to Maven itself.
+        </p>
+        <p>
+          Maven developers meet via IRC: <a href="irc://irc.codehaus.org#maven">irc.codehaus.org</a>,
+          channel <code>#maven</code>.
+          But please don't ask for solutions to Maven problems there, as
+          Maven user problems should be discussed at the mailing list
+          for several good reasons (e.g. mail archive, more subscribers) and
+          usually you get a quick answer on the mailing list. But feel free to drop in and say hi.
+        </p>
+        <p>
+          You should not mail developers directly for Maven related issues, for 2 reasons. The most important
+          is that the project operates in the public, so all discussions should be kept on the list (for the
+          same reasons as given above). Secondly, they are busy and in various timezones, so mailing to the
+          list ensures you get the most prompt response from someone available and able to commit their time
+          at the moment. Direct questions to developers will rarely be answered.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="plugin-docs">
+      <question>How do I find help on a specific goal?</question>
+      <answer>
+        <p>
+          All Maven goals are provided by plugins. For example, the goals <code>jar</code> and <code>jar:install</code>
+          are provided by the <a href="reference/plugins/jar/index.html">jar plugin</a>. You can find a list of
+          plugins and there documentation <a href="reference/plugins/index.html">here</a>.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="jelly-help">
+      <question>Where can I get help on Jelly?</question>
+      <answer>
+        <p>
+          Jelly is a reasonably active project of it's own, used beyond Maven. If you have any questions about
+          it, including how to do certain Jelly things in a Maven build file, you should ask the question on
+          the <a href="http://jakarta.apache.org/commons/jelly/mail-lists.html">Jelly mailing lists</a>.
+        </p>
+        <p>
+          While the Maven User List archive is a good place to search for answers, it is preferred that you
+          only ask Jelly specific questions there if you were unable to find an answer on the Jelly lists.
+        </p>
+      </answer>
+    </faq>
+  </part>
+
+  <!-- TODO [later]: probably a document -->
+  <part id="contributing">
+    <title>Contributing</title>
+    <faq id="bug-reporting">
+      <question>I found a bug. How do I report it?</question>
+      <answer>
+        <p>
+          First, we'd appreciate if you search the <a href="/mail-lists.html">Mailing List Archives</a>
+          to see if anyone else has encountered it and found a resolution or a workaround.
+        </p>
+        <p>
+          If you are not using the current release of Maven, it is also worth trying that, and specifically
+          checking the release notes to see if that bug might have already been addressed.
+        </p>
+        <p>
+          If you are sure it is a bug, then it should go into JIRA, the issue tracking application for Maven.
+          First, search the Maven project (or related plugin) to see if the bug has already been reported.
+          If not, create a new issue. You must be registered and logged in to do so. This enables you to be
+          contacted if the bug is fixed or more information is required.
+        </p>
+        <p>
+          The location of Maven's JIRA instance is listed on the <a href="/issue-tracking.html">Issue Tracking</a>
+          page.
+        </p>
+        <p>
+          Please be patient. While the issue will usually be reviewed immediately, bugs do not always get fixed as
+          quickly. However, if you are able to submit your own fix, it will usually be applied for the next release.
+          See <a href="#submitting-patches">Submitting Patches</a> for more information.
+        </p>
+      </answer>
+    </faq>
+    <faq id="feature-suggestions">
+      <question>I have such a cool new idea for a feature. Where do I suggest it?</question>
+      <answer>
+        <p>
+          Great! The process is very similar as for <a href="#bug-reporting">Filing a Bug Report</a>.
+        </p>
+        <p>
+          Firstly - are you sure its a new idea? Try searching the <a href="/mail-lists.html">Mailing List Archives</a>
+          for <i>both</i> the user and developer lists to see if a similar idea has already been discussed.
+        </p>
+        <p>
+          Likewise, you should also search <a href="/issue-tracking.html">JIRA</a> to see if someone has proposed
+          it as a feature request already.
+        </p>
+        <p>
+          If not, there are two ways to proceed. If you have a rough idea but think it needs some discussion with
+          the developers, try posting to the developers mailing list. So that they know initially that you have
+          already thought this through, briefly detail what you did or didn't find when searching the mail archives.
+        </p>
+        <p>
+          Once you are confident that the idea is solid and fits the current direction of the project, submit it to
+          JIRA as a feature request.
+        </p>
+        <p>
+          Please be patient. While the issue will usually be reviewed immediately, features are usually not
+          implemented until the start of the next major development cycle.
+          However, if you are able to submit your own implementation, it will usually be applied for the next release.
+          See <a href="#submitting-patches">Submitting Patches</a> for more information.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="submitting-patches">
+      <question>How do I submit my own fix or new feature?</question>
+      <answer>
+        <p>
+          Bug fixes and features submitted by non-committers of the project take the form of a patch.
+          Submitting your own patch will ensure that the bug or feature gets addressed sooner, and gives
+          the submitter the warm fuzzy feeling from helping out!
+        </p>
+        <p>
+          Before working on a patch for a bug fix or new feature, it is essential that the steps above are followed
+          to ensure that there isn't already a patch, or that a new feature has been previously decided against
+          because it does not match the direction of the project. You don't want to waste time preparing a patch
+          if it won't be used, so please take the time to consult the current developers list in advance.
+        </p>
+        <p>
+          When preparing the patch, make sure it is against the latest code in version control by doing a full update
+          and testing it again. The easiest way to prepare the patch is then to run this in the base directory of
+          your source control checkout:
+        </p>
+        <source>maven scm:create-patch</source>
+        <p>
+          This is basically equivalent to running <code>svn diff</code>. Attach the resulting patch file to a JIRA
+          issue. Please rename it to the name of the JIRA issue so a developer can save it and still know what it is.
+          Do not mail it directly to a particular developer, or to the developers mailing list as attachments are
+          often stripped or the mails lost.
+        </p>
+        <p>
+          If you did not create the original JIRA issue, it is important that you select to "watch" the issue so
+          that feedback on the patch can be given.
+        </p>
+        <p>
+          If you are fixing a bug, make sure you submit a test case that fails without the patch, but succeeds with
+          the patch applied, proving that it works.
+        </p>
+        <p>
+          If you are submitting a new feature, it is important that you include test cases to verify the feature
+          works, and documentation for users on how it works.
+        </p>
+        <p>
+          It is important that you <b>don't</b> submit whole replacement files instead of differences or differences
+          where unrelated code is changed - such as changing formatting or spacing. Patches that violate these
+          rules will often not be applied.
+        </p>
+        <p>
+          Finally, adhere to the coding standards of the project, respecting the settings of the code surrounding
+          that of the change. This includes whitespace, and ensuring that spaces are used instead of tab characters.
+        </p>
+        <p>
+          If these rules are followed, you will usually find that developers will joyfully and quickly apply
+          the patch, and be appreciative of the efforts put in to help out.
+        </p>
+      </answer>
+    </faq>
+  </part>
+
+  <part id="using">
+    <title>Using Maven</title>
+    <faq id="using-entities">
+      <question>What's the problem with entities in <code>project.xml</code>?</question>
+      <answer>
+        <p>
+          As of Maven 1.1, external entities will not be enabled by default in
+          <code>project.xml</code>, and their use is discouraged in Maven 1.0.x as well.
+          There will still be the ability to use them, but it will have to be enabled.
+        </p>
+        <p>
+          There are several reasons for this, but the main reason is that
+          the content of <code>project.xml</code> needs to be completely self-contained and
+          and able to be reproduced from a history at any point in time.
+        </p>
+        <p>
+          For this reason, using Jelly expressions other than <code>${pom.*}</code> references is
+          also not recommended and likely to be unsupported in future.
+        </p>
+        <p>
+          The most common use of this technique is to manage dependencies
+          across multiple projects. You should strongly consider using inheritence for this purpose.
+        </p>
+        <p>
+          <b>Note:</b> special character entities will always be supported and should
+          not have any current issues.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="masters-of-the-inheritence">
+      <question>How do I stop my top level properties being inherited in subprojects? I only want to inherit the
+        <code>project.xml</code> file.</question>
+      <answer>
+        <p>
+          This is a result of using the same project file at the top level of your multiple project structure as the
+          <i>master build</i> (ie, where you run your <code>multiproject</code> goals from) and the root of your
+          project inheritence tree.
+        </p>
+        <p>
+          We recommend that you separate these concerns by having both a master build project and a parent project
+          for extension (see the <code>maven-plugins</code> CVS tree for an example). Ther master build should remain
+          in the top level directory, but the shared project file should be in a subdirectory such as
+          <code>common-build</code>.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="ignoring-broken-tests">
+      <question>How do I make my build complete even with broken tests?</question>
+      <answer>
+        <p>
+          See the <a href="./reference/plugins/test/properties.html">Test Plugin Reference</a>.
+          Most notably, <code>maven.test.skip</code> and <code>maven.test.failure.ignore</code>.
+          <b>Heed the warnings!</b>
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="find-junit-messages">
+      <question>Where does the output from my JUnit tests go?</question>
+      <answer>
+        <p>If you are running <code>test:test</code>, the exceptions will usually be output to
+        <code>./target/test-reports/some.package.SomeClassTest.txt</code>.
+        If you want to see the errors in the output, set the property <code>maven.junit.usefile</code> to
+        <code>false</code>.</p>
+      </answer>
+    </faq>
+
+    <faq id="disable-reports">
+      <question>How do I disable a report on my site?</question>
+      <answer>
+        <p>
+          The preferred way is to specify your own <code><reports/></code> section in the POM.
+          Reports are not inherited from parent projects, so only those included will be used.
+          The default reports are:
+        </p>
+        <source><![CDATA[<reports>
+  <report>maven-jdepend-plugin</report>
+  <report>maven-checkstyle-plugin</report>
+  <report>maven-changes-plugin</report>
+  <report>maven-changelog-plugin</report>
+  <report>maven-developer-activity-plugin</report>
+  <report>maven-file-activity-plugin</report>
+  <report>maven-license-plugin</report>
+  <report>maven-javadoc-plugin</report>
+  <report>maven-jxr-plugin</report>
+  <report>maven-junit-report-plugin</report>
+  <report>maven-linkcheck-plugin</report>
+  <report>maven-tasklist-plugin</report>
+</reports>]]></source>
+        <p>
+          If there is one specific report you want to disable, you can do so with a post-goal. For example,
+          to disable linkcheck whenever the <code>maven.linkcheck.disable</code> property is set, add this to
+          your <code>maven.xml</code> file:
+        </p>
+        <source><![CDATA[<!-- Conditionally disable linkcheck based on a property. -->
+<postGoal name="xdoc:register-reports">
+  <j:if test="${maven.linkcheck.disable}">
+    <attainGoal name="maven-linkcheck-plugin:deregister"/>
+    <echo>linkcheck is disabled.</echo>
+  </j:if>
+</postGoal>]]></source>
+      </answer>
+    </faq>
+
+    <faq id="using-xdoclet">
+      <question>How do I use Maven with XDoclet?</question>
+      <answer>
+        <p>The XDoclet plugin is provided by the XDoclet developers. All questions
+        about it should be directed to the XDoclet mailing lists.</p>
+      </answer>
+    </faq>
+
+    <faq id="speeding-maven">
+      <question>Maven takes a long time to load. Is there anyway to speed things up?</question>
+      <answer>
+        <p>You can use the
+        <a href="reference/plugins/console/index.html">Console Plugin</a> to
+        get an interactive shell that will let load Maven once and run as many
+        goals as you want. On average machines it takes something like
+        ten seconds to compile and run unit tests, so that you can build often
+        and test your code often.</p>
+      </answer>
+    </faq>
+
+    <faq id="transitive-dependencies">
+      <question>Do I need to specify all the dependencies in the POM?</question>
+      <answer>
+        <p>The short answer is YES. Maven 2 will have a transitive dependency discovery mechanism
+        that will avoid this.</p>
+      </answer>
+    </faq>
+
+    <faq id="multiple-source-directories">
+      <question>How do I provide multiple source directories in my <code>project.xml</code>?</question>
+      <answer>
+        <p>You can't. However, if you really need it, you can use a snippet in <code>maven.xml</code>:</p>
+<source><![CDATA[<preGoal name="java:compile">
+   <ant:path
+       id="my.other.src.dir"
+       location="${basedir}/debug/src"/>
+   <maven:addPath
+       id="maven.compile.src.set"
+       refid="my.other.src.dir"/>
+</preGoal>]]></source>
+        <p>Please think about the reason you need this, and carefully consider whether it is necessary. Usually this
+        is used for writing plugins that handle source generation.</p>
+      </answer>
+    </faq>
+
+    <faq id="site-configuration">
+      <question>How can I customise the configuration for an entire installation?</question>
+      <answer>
+        <p>
+          Currently you can only configure settings at a project and per-user level.
+          There are no site-wise configuration settings available.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="log-output">
+      <question>How can I customise Maven's logging?</question>
+      <answer>
+        <p>
+          Maven uses <a href="http://logging.apache.org/log4j/">Log4J</a> to log all of its output.
+        </p>
+        <p>
+          If you would like to write certain information to a file and piping is not an option or you want
+          greater control over what is controlled, you can override the log4j configuration. Refer to the log4j
+          documentation for how to override this using system properties.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="classloader-property">
+      <question>Why shouldn't I use the dependency classloader override property?</question>
+      <answer>
+        <p>
+          Because in most cases it isn't needed. <code>root.maven</code> is equivalent to the project classloader,
+          so is never needed.
+          While <code>root</code> is the Ant classloader and has some <a href="#BadXSLT">valid uses</a>, you should
+          not load tasks into it unless absolutely necessary as it will then force itself
+          on the other plugins executed afterwards. In particular any jakarta-commons libraries should not be in the
+          root classloader as these can clash with Jelly.
+        </p>
+        <p>
+          The correct way to use ant tasks in <code>maven.xml</code> or a plugin is something like:
+        </p>
+        <source><![CDATA[<ant:taskdef name="checkstyle"
+  classname="com.puppycrawl.tools.checkstyle.CheckStyleTask">
+  <ant:classpath>
+    <ant:pathelement location="${plugin.getDependencyPath('checkstyle:checkstyle')}"/>
+    <ant:path refid="maven.dependency.classpath"/>
+  </ant:classpath>
+</ant:taskdef>]]></source>
+      </answer>
+    </faq>
+
+    <faq id="add-jar-to-local-repository">
+      <question>How do I add a JAR from a non-Maven project to my local repository?</question>
+      <answer>
+        <p>
+          If it is a JAR that cannot be uploaded to Ibiblio because of a license, or it is private,
+          you must manually copy it to your local repository. After picking a sensible group ID, and making
+          sure the filename is in the format <code>artifactId-version.jar</code>, copy it to
+          <code>${maven.repo.local}/groupId/jars/artifactId-version.jar</code>.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="share-local-repository">
+      <question>I share a development machine. How can I share the local repository to save downloading?</question>
+      <answer>
+        <p>
+          It is recommended that you <b>do not</b> share your local repository. The reason for this is that as you
+          build your projects, part of the process is usually to install your changes there for sharing with other
+          projects you are working on that depend on it.
+        </p>
+        <p>
+          If you share this with multiple developers, you will have to communicate with them about when you will
+          be developing a certain project to ensure your changes don't clash, and ensure each person is always
+          completely up to date.
+        </p>
+        <p>
+          Usually, it is better to work with a shared remote repository that you run yourself. This means that
+          dependencies are only downloaded once from the internet, and then downloaded to the local cache for each
+          developer as they need it. Company artifacts can also be published there.
+        </p>
+        <p>
+          See <a href="using/repositories.html">Working with Repositories</a> for more information.
+        </p>
+        <p>
+          If after this you really want to share a local repository, you can set the <code>maven.repo.local</code>
+          property. This is a directory (not a URL). The directory pointed to must be readable by all of the users
+          and may need to be writable if the users will be allowed to download dependencies or publish their changes.
+          The file system mask must also be set correctly so that changes retain the correct permissions.
+        </p>
+        <p>
+          Please note that this solution will not be supported by the Maven Users Mailing List, however.
+        </p>
+      </answer>
+    </faq>
+  </part>
+
+  <part id="ibiblio">
+    <title>Ibiblio</title>
+
+    <faq id="ibiblio-repository">
+      <question>Why is the Maven repository on Ibiblio and not at Apache?</question>
+      <answer>
+        <p>There are several reasons why the Maven Repository was setup at
+        Ibiblio. Ibiblio is a massive archive of almost everything you could
+        imagine but one of the stated goals of Ibiblio is to "Expand and improve the distribution
+        of open source software". There is really no limit to how much we can stuff
+        in the repository at Ibiblio and it will be archived indefinitely. They have
+        a lot of bandwith, good redundancy and have a very secure setup. Another reason
+        we placed the repository there was that it is Apache policy not to store
+        any (L)GPL artifacts on our servers. We wanted Maven to work for as many
+        Java developers as possible so we chose Ibiblio where there is no restriction
+        on store (L)GPL artifacts. You can find out more about Ibiblio
+        <a href="http://www.ibiblio.org/about.html">here</a>.</p>
+      </answer>
+    </faq>
+
+    <faq id="ibiblio-upload">
+      <question>How do I upload a resource to or update a resource on http://www.ibiblio.org/maven?</question>
+      <answer>
+        <p>Read <a href="reference/repository-upload.html">Uploading to Ibiblio Instructions</a>.</p>
+      </answer>
+    </faq>
+
+    <faq id="ibiblio-mirrors">
+      <question>Are there any mirrors for the Maven repository at ibiblio?</question>
+      <answer>
+        <p>Yes, there are at least the following:</p>
+        <ul>
+          <li>http://www.ibiblio.org/maven/</li>
+          <li>http://public.planetmirror.com/pub/maven/</li>
+          <li>http://mirrors.sunsite.dk/maven/</li>
+          <li>http://ftp.up.ac.za/pub/linux/maven/</li>
+          <li>http://download.au.kde.org/pub/maven/</li>
+        </ul>
+      </answer>
+    </faq>
+
+    <faq id="search-repositories">
+      <question>Can I search the repositories?</question>
+      <answer>
+        <p>
+         There is a service available at
+         <a href="http://maven.ozacc.com/">http://maven.ozacc.com/</a>
+         that provides a search service, though not affiliated with the
+         Maven project.
+        </p>
+      </answer>
+    </faq>
+  </part>
+
+  <part id="scripting">
+    <title>Scripting</title>
+    <faq id="plugin-variables">
+      <question>How do I get or set plugin properties from Jelly?</question>
+      <answer>
+        <p>Plugin properties can be used with the following tags:
+        <a href="reference/maven-jelly-tags/tags.html#maven:get">maven:get</a> and
+        <a href="reference/maven-jelly-tags/tags.html#maven:set">maven:set</a>.
+        (These replace the deprecated versions of <code>${pom.getPluginContext(...).get/setVariable()}</code>
+        and <code>maven:pluginVar</code>.)</p>
+        <p>Example:</p>
+        <source><![CDATA[<maven:get plugin="maven-war-plugin" property="maven.war.src" var="warSourceDir" />
+<echo>The WAR source directory is ${warSourceDir}</echo>
+...
+<maven:set plugin="maven-multiproject-plugin" property="maven.multiproject.includes" value="subprojects/*/project.xml"/>]]></source>
+      </answer>
+    </faq>
+
+    <faq id="multiple-threads">
+      <question>How do I spin off a background process in a goal?</question>
+      <answer>
+        <p>
+          For example, before starting unit tests you might need to start a DB server. The DB server blocks until it is
+          terminated, so it needs to be started in the background. <code><ant:parallel/></code> does not seem to
+          work in this case because it blocks the main execution thread, which is exactly what needs to be avoided.
+        </p>
+        <p>
+          The solution is given in
+          <a href="http://marc.theaimsgroup.com/?l=turbine-maven-user&m=105911458328637&w=2">this
+          thread</a>.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="BadXSLT">
+      <question>How do I get the XSLT tasks to work?</question>
+      <answer>
+        <p>
+          A common symptom is that the Jelly or Ant tag are output instead of being processed.
+          See <a href="http://jira.codehaus.org/secure/ViewIssue.jspa?key=MAVEN-156">MAVEN-156</a>.
+        </p>
+        <p>
+          The solution is to add the JAXP system property via the Jelly script.
+        </p>
+        <source><![CDATA[${systemScope.setProperty('javax.xml.transform.TransformerFactory','org.apache.xalan.processor.TransformerFactoryImpl')}
+<ant:style in="${basedir}/some.xml" out="${maven.build.dest}/other.xml" style="${basedir}/sheet.xsl" processor="trax"/>
+]]></source>
+        <p>
+          Also make sure that Xalan is declared as dependencies in your project file, and added to the root classloader
+          so that Ant can find it:
+        </p>
+        <source><![CDATA[<dependency>
+  <groupId>xalan</groupId>
+  <artifactId>xalan</artifactId>
+  <version>2.3.1</version>
+  <url>http://xml.apache.org/xalan/</url>
+  <properties>
+    <classloader>root</classloader>
+  </properties>
+</dependency>]]></source>
+      </answer>
+    </faq>
+
+    <faq id="shareCode">
+      <question>How do I share build code between projects?</question>
+      <answer>
+        <p>
+          Write your own Maven plugin. It's not as difficult as you may
+          think it is, and it will probably save you much time when
+          your code grows in size.
+        </p>
+        <p>
+          Please read the <a href="using/developing-plugins.html">Developing Plugins</a>
+          documentation for instructions on how to do this.
+        </p>
+        <p>
+          It can also be helpful to refer to the source code for the existing
+          Maven plugins which you already have installed.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="sharing-plugins">
+      <question>How do I share my Maven plugin with others?</question>
+      <answer>
+        Read <a href="reference/sharing-plugins.html">Sharing Plugins</a>.
+      </answer>
+    </faq>
+  </part>
+
+  <part id="troubleshooting">
+    <title>Troubleshooting Maven</title>
+
+    <faq id="debug-maven">
+      <question>How can I get Maven to give more verbose output?</question>
+      <answer>
+        <p>
+          If you received an exception at the end and want a full stack trace for more
+          information, you can run the same <code>maven</code> command again with the <code>-e</code>
+          switch, eg:
+        </p>
+        <source>maven -e jar:jar</source>
+        <p>
+          If you would like a full set of debugging information to trace what Maven is doing,
+          you can run the same <code>maven</code> command again with the <code>-X</code>
+          switch, eg:
+        </p>
+        <source>maven -X jar:jar</source>
+        <p>Note that <code>-X</code> implies <code>-e</code>, so there is no need to use both.</p>
+      </answer>
+    </faq>
+
+    <faq id="unit-test-14">
+      <question>Why do the unit tests fail under Java 1.4?</question>
+      <answer>
+        <p>It is possible that the XML parser included with Maven is
+        interfering with the XML parser included in Java 1.4. Please set
+        the <code>${maven.junit.fork}</code>
+        <a href="reference/plugins/test/properties.html">property</a> to
+        <code>yes</code>.</p>
+      </answer>
+    </faq>
+
+    <faq id="changelog-no-local-copy">
+      <question>Why does change log ask me to check out the source code?</question>
+      <answer>
+        <p>When you run the cvs change log report in Maven, you may see an
+        error occasionally, such as:</p>
+        <source><![CDATA[cvs [log aborted]: there is no version here; do 'cvs checkout' first
+ ChangeLog found: 5 entries]]></source>
+        <p>This is caused by the cvs log command finding a directory in it's
+        repository that you don't have locally. Note: The directory may not
+        appear on a checkout or update if it is empty in the repository.
+        Please do a clean checkout of the code and retry the report.</p>
+      </answer>
+    </faq>
+
+    <faq id="changelog-broken">
+      <question>I have problems generating the changelog report. Why?</question>
+      <answer>
+        <p>
+        When you run the cvs change log report in Maven, the report hangs or the
+        final output is blank.
+        </p>
+        <p>
+          This is typically caused by the cvs command not running correctly.
+          The first port of call is to check Maven's output, search the lines containing for "SCM".
+        </p>
+        <p>
+          <source><![CDATA[SCM Working Directory: D:\Data\workspace\maven
+SCM Command Line[0]: cvs
+SCM Command Line[1]: -d
+SCM Command Line[2]: :pserver:bwalding at cvs.apache.org:/home/cvsroot
+SCM Command Line[3]: log
+SCM Command Line[4]: -d 2003-01-27]]></source>
+        </p>
+        <p>
+          Try running the command that you find in the log file manually. The results typically
+          speak for themselves.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="jelly-site-error">
+      <question>maven site fails with bizarre Jelly errors, what can I do?</question>
+      <answer>
+        When I try to generate my site I get something like this:
+
+        <source><![CDATA[BUILD FAILED
+null:58:46:
+<x:parse> Invalid source argument. Must be a String, Reader,
+InputStream or URL. Was type; java.io.File with value:
+/home/jvanzyl/js/com.werken/drools/target/jdepend-raw-report.xml
+Total time:  12 seconds]]></source>
+        <p>
+          This problem has been observed when a version of Jelly used as a
+          dependency is different than the one distributed with Maven.
+          If you align your versions of Jelly you should be able to generate
+          your site.
+        </p>
+      </answer>
+    </faq>
+  </part>
+
+  <part id="ant">
+    <title>Ant</title>
+
+    <faq id="project-help">
+      <question>What is the equivalent of <code>ant -projecthelp</code> in Maven?</question>
+      <answer>
+        <p>
+          To some extent, <code>maven -u</code> behaves the same way. For more information, please read the
+          <a href="start/quick-start.html">Quick Start</a> guide.
+        </p>
+      </answer>
+    </faq>
+    <faq id="maven-vs-ant-speed">
+      <question>I've heard Maven is much slower than Ant. Is there anything I can do to make it faster?</question>
+      <answer>
+        <p>
+          This has become a bit of an urban myth now, as Maven takes very little more than Ant
+          to initialise (with the exception of the very first run when plugins must be unpacked and parsed).
+        </p>
+        <p>
+          Part of the misconception comes from claims that building the site or building 30 projects
+          takes a lot of CPU and memory. Well, this would happen in Ant too if it were attempted!
+          Some extensions to Ant that build a web site take considerably longer than Maven to do that task.
+          This area is also a focus for future development so that generating these parts of the build
+          are much faster.
+        </p>
+        <p>
+          When it comes down to your day to day development and edit-build-test cycle, you
+          <i>can</i> speed up Maven's initialisation time by running the console, as shown in
+          <a href="#speeding-maven">this FAQ answer</a>.
+          This console keeps Maven loaded and ready to do your bidding for a specific project, and
+          <b>makes Maven faster than Ant for performing equivalent, subsequent builds!</b>
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="filtering-resources">
+      <question>How can I filter properties into resource files as part of the build?</question>
+      <answer>
+        <p>
+          This can be done using resource filtering. In your POM, add the filtering property to your existing
+          resources definition. Please refer to <a href="using/resources.html">Resources</a> for more information.
+        </p>
+      </answer>
+    </faq>
+  </part>
+
+  <part id="building">
+    <title>Building Maven</title>
+
+    <faq id="how-to-build">
+      <question>How do I build Maven?</question>
+      <answer>
+        <p>Please see the <a href="developers/building-from-source.html">Building Maven from Source</a> document.</p>
+      </answer>
+    </faq>
+
+    <faq id="build-firewall">
+      <question>How do I build Maven from behind a firewall?</question>
+      <answer>
+        <p>You typically need to set your HTTP proxy host and port details so that Maven can tunnel through your
+        HTTP Proxy. To do this you typically need to set the <code>maven.proxy.host</code> and
+        <code>maven.proxy.port</code> properties.</p>
+        See the
+        <a href="./reference/properties.html#Proxy_Properties">Properties Reference</a> for more details.
+      </answer>
+    </faq>
+  </part>
+</faqs>
+
diff --git a/doxia-modules/doxia-module-fo/pom.xml b/doxia-modules/doxia-module-fo/pom.xml
new file mode 100644
index 0000000..57e2c35
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/pom.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-fo</artifactId>
+
+  <name>Doxia :: FO Module</name>
+  <description>A Doxia module for FO (Formatting Objects) source documents.</description>
+
+  <developers>
+    <developer>
+      <id>ltheussl</id>
+      <name>Lukas Theussl</name>
+      <email>ltheussl at apache.org</email>
+      <organization>Apache Software Foundation</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+      <timezone>+1</timezone>
+    </developer>
+  </developers>
+
+  <dependencies>
+    <!-- Doxia -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-logging-api</artifactId>
+    </dependency>
+
+    <!-- plexus -->
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+
+    <!-- misc -->
+    <dependency>
+      <groupId>commons-configuration</groupId>
+      <artifactId>commons-configuration</artifactId>
+      <version>1.6</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.xmlgraphics</groupId>
+      <artifactId>fop</artifactId>
+      <version>0.95</version>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.12</version>
+      <!-- runtime dep for fop -->
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>xerces</groupId>
+      <artifactId>xercesImpl</artifactId>
+      <version>2.9.1</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <!-- To be sure that JVM will implement AWT in software -->
+          <systemProperties>
+            <property>
+              <name>java.awt.headless</name>
+              <value>true</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>reporting</id>
+      <reporting>
+        <plugins>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>l10n-maven-plugin</artifactId>
+            <version>1.0-alpha-2</version>
+            <configuration>
+              <locales>
+                <locale>en</locale>
+              </locales>
+              <excludes>
+                <exclude>log4j*</exclude>
+              </excludes>
+            </configuration>
+          </plugin>
+        </plugins>
+      </reporting>
+    </profile>
+  </profiles>
+</project>
diff --git a/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoAggregateSink.java b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoAggregateSink.java
new file mode 100644
index 0000000..72d6789
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoAggregateSink.java
@@ -0,0 +1,1272 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.Stack;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.document.DocumentCover;
+import org.apache.maven.doxia.document.DocumentMeta;
+import org.apache.maven.doxia.document.DocumentModel;
+import org.apache.maven.doxia.document.DocumentTOC;
+import org.apache.maven.doxia.document.DocumentTOCItem;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * A Doxia Sink that produces an aggregated FO model. The usage is similar to the following:
+ *
+ * <pre>
+ * FoAggregateSink sink = new FoAggregateSink( writer );
+ * sink.setDocumentModel( documentModel );
+ * sink.beginDocument();
+ * sink.coverPage();
+ * sink.toc();
+ * ...
+ * sink.endDocument();
+ * </pre>
+ *
+ * <b>Note</b>: the documentModel object contains several
+ * <a href="http://maven.apache.org/doxia/doxia/doxia-core/document.html">document metadata</a>, but only a few
+ * of them are used in this sink (i.e. author, confidential, date and title), the others are ignored.
+ *
+ * @author ltheussl
+ * @version $Id: FoAggregateSink.java 811802 2009-09-06 10:49:37Z vsiveton $
+ * @since 1.1
+ */
+public class FoAggregateSink
+    extends FoSink
+{
+    /**
+     * No Table Of Content.
+     * @see #setDocumentModel(DocumentModel, int)
+     */
+    public static int TOC_NONE = 0;
+
+    /**
+     * Table Of Content at the start of the document.
+     * @see #setDocumentModel(DocumentModel, int)
+     */
+    public static int TOC_START = 1;
+
+    /**
+     * Table Of Content at the end of the document.
+     * @see #setDocumentModel(DocumentModel, int)
+     */
+    public static int TOC_END = 2;
+
+    // TODO: make configurable
+    private static final String COVER_HEADER_HEIGHT = "1.5in";
+
+    /** The document model to be used by this sink. */
+    private DocumentModel docModel;
+
+    /** Counts the current chapter level. */
+    private int chapter = 0;
+
+    /** Name of the source file of the current document, relative to the source root. */
+    private String docName;
+
+    /** Title of the chapter, used in the page header. */
+    private String docTitle = "";
+
+    /** Content in head is ignored in aggregated documents. */
+    private boolean ignoreText;
+
+    /** Current position of the TOC, see {@link #TOC_POSITION} */
+    private int tocPosition;
+
+    /** Used to get the current position in the TOC. */
+    private final Stack tocStack = new Stack();
+
+    /**
+     * Constructor.
+     *
+     * @param writer The writer for writing the result.
+     */
+    public FoAggregateSink( Writer writer )
+    {
+        super( writer );
+    }
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        head( null );
+    }
+
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        init();
+
+        ignoreText = true;
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        ignoreText = false;
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        title( null );
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        // ignored
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        // ignored
+    }
+
+    /** {@inheritDoc} */
+    public void author()
+    {
+        author( null );
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        // ignored
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        // ignored
+    }
+
+    /** {@inheritDoc} */
+    public void date()
+    {
+        date( null );
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        // ignored
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        // ignored
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        body( null );
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        chapter++;
+
+        resetSectionCounter();
+
+        startPageSequence( getHeaderText(), getFooterText() );
+
+        if ( docName == null )
+        {
+            getLog().warn( "No document root specified, local links will not be resolved correctly!" );
+        }
+        else
+        {
+            writeStartTag( BLOCK_TAG, "id", docName );
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        writeEOL();
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( FLOW_TAG );
+        writeEndTag( PAGE_SEQUENCE_TAG );
+
+        // reset document name
+        docName = null;
+    }
+
+    /**
+     * Sets the title of the current document. This is used as a chapter title in the page header.
+     *
+     * @param title the title of the current document.
+     */
+    public void setDocumentTitle( String title )
+    {
+        this.docTitle = title;
+
+        if ( title == null )
+        {
+            this.docTitle = "";
+        }
+    }
+
+    /**
+     * Sets the name of the current source document, relative to the source root.
+     * Used to resolve links to other source documents.
+     *
+     * @param name the name for the current document.
+     */
+    public void setDocumentName( String name )
+    {
+        this.docName = getIdName( name );
+    }
+
+    /**
+     * Sets the DocumentModel to be used by this sink. The DocumentModel provides all the meta-information
+     * required to render a document, eg settings for the cover page, table of contents, etc.
+     * <br/>
+     * By default, a TOC will be added at the beginning of the document.
+     *
+     * @param model the DocumentModel.
+     * @see #setDocumentModel(DocumentModel, String)
+     * @see #TOC_START
+     */
+    public void setDocumentModel( DocumentModel model )
+    {
+        setDocumentModel( model, TOC_START );
+    }
+
+    /**
+     * Sets the DocumentModel to be used by this sink. The DocumentModel provides all the meta-information
+     * required to render a document, eg settings for the cover page, table of contents, etc.
+     *
+     * @param model the DocumentModel, could be null.
+     * @param tocPos should be one of these values: {@link #TOC_NONE}, {@link #TOC_START} and {@link #TOC_END}.
+     * @since 1.1.2
+     */
+    public void setDocumentModel( DocumentModel model, int tocPos )
+    {
+        this.docModel = model;
+        if ( !( tocPos == TOC_NONE || tocPos == TOC_START || tocPos == TOC_END ) )
+        {
+            if ( getLog().isDebugEnabled() )
+            {
+                getLog().debug( "Unrecognized value for tocPosition: " + tocPos + ", using no toc." );
+            }
+            tocPos = TOC_NONE;
+        }
+        this.tocPosition = tocPos;
+
+        if ( this.docModel != null && this.docModel.getToc() != null && this.tocPosition != TOC_NONE )
+        {
+            DocumentTOCItem tocItem = new DocumentTOCItem();
+            tocItem.setName( this.docModel.getToc().getName() );
+            tocItem.setRef( "./toc" );
+            List items = new LinkedList();
+            if ( this.tocPosition == TOC_START )
+            {
+                items.add( tocItem );
+            }
+            items.addAll( this.docModel.getToc().getItems() );
+            if ( this.tocPosition == TOC_END )
+            {
+                items.add( tocItem );
+            }
+
+            this.docModel.getToc().setItems( items );
+        }
+    }
+
+    /**
+     * Translates the given name to a usable id.
+     * Prepends "./" and strips any extension.
+     *
+     * @param name the name for the current document.
+     * @return String
+     */
+    private String getIdName( String name )
+    {
+        if ( StringUtils.isEmpty( name ) )
+        {
+            getLog().warn( "Empty document reference, links will not be resolved correctly!" );
+            return "";
+        }
+
+        String idName = name.replace( '\\', '/' );
+
+        // prepend "./" and strip extension
+        if ( !idName.startsWith( "./" ) )
+        {
+            idName = "./" + idName;
+        }
+
+        if ( idName.substring( 2 ).lastIndexOf( "." ) != -1 )
+        {
+            idName = idName.substring( 0, idName.lastIndexOf( "." ) );
+        }
+
+        while ( idName.indexOf( "//" ) != -1 )
+        {
+            idName = StringUtils.replace( idName, "//", "/" );
+        }
+
+        return idName;
+    }
+
+    // -----------------------------------------------------------------------
+    //
+    // -----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        figureGraphics( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        String anchor = src;
+
+        while ( anchor.startsWith( "./" ) )
+        {
+            anchor = anchor.substring( 2 );
+        }
+
+        if ( anchor.startsWith( "../" ) && docName != null )
+        {
+            anchor = resolveLinkRelativeToBase( anchor );
+        }
+
+        super.figureGraphics( anchor, attributes );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        anchor( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Anchor name cannot be null!" );
+        }
+
+        String anchor = name;
+
+        if ( !DoxiaUtils.isValidId( anchor ) )
+        {
+            anchor = DoxiaUtils.encodeId( name, true );
+
+            String msg = "Modified invalid anchor name: '" + name + "' to '" + anchor + "'";
+            logMessage( "modifiedLink", msg );
+        }
+
+        anchor = "#" + anchor;
+
+        if ( docName != null )
+        {
+            anchor = docName + anchor;
+        }
+
+        writeStartTag( INLINE_TAG, "id", anchor );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        link( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Link name cannot be null!" );
+        }
+
+        if ( DoxiaUtils.isExternalLink( name ) )
+        {
+            // external links
+            writeStartTag( BASIC_LINK_TAG, "external-destination", HtmlTools.escapeHTML( name ) );
+            writeStartTag( INLINE_TAG, "href.external" );
+            return;
+        }
+
+        while ( name.indexOf( "//" ) != -1 )
+        {
+            name = StringUtils.replace( name, "//", "/" );
+        }
+
+        if ( DoxiaUtils.isInternalLink( name ) )
+        {
+            // internal link (ie anchor is in the same source document)
+            String anchor = name.substring( 1 );
+
+            if ( !DoxiaUtils.isValidId( anchor ) )
+            {
+                String tmp = anchor;
+                anchor = DoxiaUtils.encodeId( anchor, true );
+
+                String msg = "Modified invalid anchor name: '" + tmp + "' to '" + anchor + "'";
+                logMessage( "modifiedLink", msg );
+            }
+
+            if ( docName != null )
+            {
+                anchor = docName + "#" + anchor;
+            }
+
+            writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
+            writeStartTag( INLINE_TAG, "href.internal" );
+        }
+        else if ( name.startsWith( "../" ) )
+        {
+            // local link (ie anchor is not in the same source document)
+
+            if ( docName == null )
+            {
+                // can't resolve link without base, fop will issue a warning
+                writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( name ) );
+                writeStartTag( INLINE_TAG, "href.internal" );
+
+                return;
+            }
+
+            String anchor = resolveLinkRelativeToBase( chopExtension( name ) );
+
+            writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
+            writeStartTag( INLINE_TAG, "href.internal" );
+        }
+        else
+        {
+            // local link (ie anchor is not in the same source document)
+
+            String anchor = name;
+
+            if ( anchor.startsWith( "./" ) )
+            {
+                this.link( anchor.substring( 2 ) );
+                return;
+            }
+
+            anchor = chopExtension( anchor );
+
+            String base = docName.substring( 0, docName.lastIndexOf( "/" ) );
+            anchor = base + "/" + anchor;
+
+            writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
+            writeStartTag( INLINE_TAG, "href.internal" );
+        }
+    }
+
+    // only call this if docName != null !!!
+    private String resolveLinkRelativeToBase( String name )
+    {
+        String anchor = name;
+
+        String base = docName.substring( 0, docName.lastIndexOf( "/" ) );
+
+        if ( base.indexOf( "/" ) != -1 )
+        {
+            while ( anchor.startsWith( "../" ) )
+            {
+                base = base.substring( 0, base.lastIndexOf( "/" ) );
+
+                anchor = anchor.substring( 3 );
+
+                if ( base.lastIndexOf( "/" ) == -1 )
+                {
+                    while ( anchor.startsWith( "../" ) )
+                    {
+                        anchor = anchor.substring( 3 );
+                    }
+                    break;
+                }
+            }
+        }
+
+        return base + "/" + anchor;
+    }
+
+    private String chopExtension( String name )
+    {
+        String anchor = name;
+
+        int dot = anchor.lastIndexOf( "." );
+
+        if ( dot != -1 && dot != anchor.length() && anchor.charAt( dot + 1 ) != '/' )
+        {
+            int hash = anchor.indexOf( "#", dot );
+
+            if ( hash != -1 )
+            {
+                int dot2 = anchor.indexOf( ".", hash );
+
+                if ( dot2 != -1 )
+                {
+                    anchor =
+                        anchor.substring( 0, dot ) + "#" + HtmlTools.encodeId( anchor.substring( hash + 1, dot2 ) );
+                }
+                else
+                {
+                    anchor =
+                        anchor.substring( 0, dot ) + "#"
+                            + HtmlTools.encodeId( anchor.substring( hash + 1, anchor.length() ) );
+                }
+            }
+            else
+            {
+                anchor = anchor.substring( 0, dot );
+            }
+        }
+
+        return anchor;
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a start tag, prepending EOL.
+     */
+    protected void writeStartTag( Tag tag, String attributeId )
+    {
+        if ( !ignoreText )
+        {
+            super.writeStartTag( tag, attributeId );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a start tag, prepending EOL.
+     */
+    protected void writeStartTag( Tag tag, String id, String name )
+    {
+        if ( !ignoreText )
+        {
+            super.writeStartTag( tag, id, name );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes an end tag, appending EOL.
+     */
+    protected void writeEndTag( Tag t )
+    {
+        if ( !ignoreText )
+        {
+            super.writeEndTag( t );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a simple tag, appending EOL.
+     */
+    protected void writeEmptyTag( Tag tag, String attributeId )
+    {
+        if ( !ignoreText )
+        {
+            super.writeEmptyTag( tag, attributeId );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a text, swallowing any exceptions.
+     */
+    protected void write( String text )
+    {
+        if ( !ignoreText )
+        {
+            super.write( text );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a text, appending EOL.
+     */
+    protected void writeln( String text )
+    {
+        if ( !ignoreText )
+        {
+            super.writeln( text );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes content, escaping special characters.
+     */
+    protected void content( String text )
+    {
+        if ( !ignoreText )
+        {
+            super.content( text );
+        }
+    }
+
+    /**
+     * Writes EOL.
+     */
+    protected void newline()
+    {
+        if ( !ignoreText )
+        {
+            writeEOL();
+        }
+    }
+
+    /**
+     * Starts a page sequence, depending on the current chapter.
+     *
+     * @param headerText The text to write in the header, if null, nothing is written.
+     * @param footerText The text to write in the footer, if null, nothing is written.
+     */
+    protected void startPageSequence( String headerText, String footerText )
+    {
+        if ( chapter == 1 )
+        {
+            startPageSequence( "0", headerText, footerText );
+        }
+        else
+        {
+            startPageSequence( "auto", headerText, footerText );
+        }
+    }
+
+    /**
+     * Returns the text to write in the header of each page.
+     *
+     * @return String
+     */
+    protected String getHeaderText()
+    {
+        return Integer.toString( chapter ) + "   " + docTitle;
+    }
+
+    /**
+     * Returns the text to write in the footer of each page.
+     *
+     * @return String
+     */
+    protected String getFooterText()
+    {
+        int actualYear;
+        String add = " • " + getBundle( Locale.US ).getString( "footer.rights" );
+        String companyName = "";
+
+        if ( docModel != null && docModel.getMeta() != null && docModel.getMeta().isConfidential() )
+        {
+            add = add + " • " + getBundle( Locale.US ).getString( "footer.confidential" );
+        }
+
+        if ( docModel != null && docModel.getCover() != null && docModel.getCover().getCompanyName() != null )
+        {
+            companyName = docModel.getCover().getCompanyName();
+        }
+
+        if ( docModel != null && docModel.getMeta() != null && docModel.getMeta().getDate() != null )
+        {
+            Calendar date = Calendar.getInstance();
+            date.setTime( docModel.getMeta().getDate() );
+            actualYear = date.get( Calendar.YEAR );
+        }
+        else
+        {
+            actualYear = Calendar.getInstance().get( Calendar.YEAR );
+        }
+
+        return "©" + actualYear + ", " + companyName + add;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Returns the current chapter number as a string.
+     */
+    protected String getChapterString()
+    {
+        return Integer.toString( chapter ) + ".";
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a 'xsl-region-before' block.
+     */
+    protected void regionBefore( String headerText )
+    {
+        writeStartTag( STATIC_CONTENT_TAG, "flow-name", "xsl-region-before" );
+        writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "5.625in" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.625in" );
+        writeStartTag( TABLE_BODY_TAG, "" );
+        writeStartTag( TABLE_ROW_TAG, "" );
+        writeStartTag( TABLE_CELL_TAG, "" );
+        writeStartTag( BLOCK_TAG, "header.style" );
+
+        if ( headerText != null )
+        {
+            write( headerText );
+        }
+
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+        writeStartTag( TABLE_CELL_TAG, "" );
+        writeStartTag( BLOCK_TAG, "page.number" );
+        writeEmptyTag( PAGE_NUMBER_TAG, "" );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+        writeEndTag( TABLE_BODY_TAG );
+        writeEndTag( TABLE_TAG );
+        writeEndTag( STATIC_CONTENT_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a 'xsl-region-after' block.
+     */
+    protected void regionAfter( String footerText )
+    {
+        writeStartTag( STATIC_CONTENT_TAG, "flow-name", "xsl-region-after" );
+        writeStartTag( BLOCK_TAG, "footer.style" );
+
+        if ( footerText != null )
+        {
+            write( footerText );
+        }
+
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( STATIC_CONTENT_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a chapter heading.
+     */
+    protected void chapterHeading( String headerText, boolean chapterNumber )
+    {
+        writeStartTag( BLOCK_TAG, "" );
+        writeStartTag( LIST_BLOCK_TAG, "" );
+        writeStartTag( LIST_ITEM_TAG, "" );
+        writeln( "<fo:list-item-label end-indent=\"6.375in\" start-indent=\"-1in\">" );
+        writeStartTag( BLOCK_TAG, "outdented.number.style" );
+
+        if ( chapterNumber )
+        {
+            writeStartTag( BLOCK_TAG, "chapter.title" );
+            write( Integer.toString( chapter ) );
+            writeEndTag( BLOCK_TAG );
+        }
+
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( LIST_ITEM_LABEL_TAG );
+        writeln( "<fo:list-item-body end-indent=\"1in\" start-indent=\"0in\">" );
+        writeStartTag( BLOCK_TAG, "chapter.title" );
+
+        if ( headerText == null )
+        {
+            write( docTitle );
+        }
+        else
+        {
+            write( headerText );
+        }
+
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( LIST_ITEM_BODY_TAG );
+        writeEndTag( LIST_ITEM_TAG );
+        writeEndTag( LIST_BLOCK_TAG );
+        writeEndTag( BLOCK_TAG );
+        writeStartTag( BLOCK_TAG, "space-after.optimum", "0em" );
+        writeEmptyTag( LEADER_TAG, "chapter.rule" );
+        writeEndTag( BLOCK_TAG );
+    }
+
+    /**
+     * Writes a table of contents. The DocumentModel has to contain a DocumentTOC for this to work.
+     */
+    public void toc()
+    {
+        if ( docModel == null || docModel.getToc() == null || docModel.getToc().getItems() == null
+            || this.tocPosition == TOC_NONE )
+        {
+            return;
+        }
+
+        DocumentTOC toc = docModel.getToc();
+
+        writeln( "<fo:page-sequence master-reference=\"toc\" initial-page-number=\"1\" format=\"i\">" );
+        regionBefore( toc.getName() );
+        regionAfter( getFooterText() );
+        writeStartTag( FLOW_TAG, "flow-name", "xsl-region-body" );
+        writeStartTag( BLOCK_TAG, "id", "./toc" );
+        chapterHeading( toc.getName(), false );
+        writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.45in" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.4in" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "0.4in" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "5in" ); // TODO {$maxBodyWidth - 1.25}in
+        writeStartTag( TABLE_BODY_TAG );
+
+        writeTocItems( toc.getItems(), 1 );
+
+        writeEndTag( TABLE_BODY_TAG );
+        writeEndTag( TABLE_TAG );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( FLOW_TAG );
+        writeEndTag( PAGE_SEQUENCE_TAG );
+    }
+
+    private void writeTocItems( List tocItems, int level )
+    {
+        final int maxTocLevel = 4;
+
+        if ( level < 1 || level > maxTocLevel )
+        {
+            return;
+        }
+
+        tocStack.push( new NumberedListItem( NUMBERING_DECIMAL ) );
+
+        for ( Iterator k = tocItems.iterator(); k.hasNext(); )
+        {
+            DocumentTOCItem tocItem = (DocumentTOCItem) k.next();
+
+            String ref = getIdName( tocItem.getRef() );
+
+            writeStartTag( TABLE_ROW_TAG, "keep-with-next", "auto" );
+
+            if ( level > 2 )
+            {
+                for ( int i = 0; i < level - 2; i++ )
+                {
+                    writeStartTag( TABLE_CELL_TAG );
+                    writeSimpleTag( BLOCK_TAG );
+                    writeEndTag( TABLE_CELL_TAG );
+                }
+            }
+
+            writeStartTag( TABLE_CELL_TAG, "toc.cell" );
+            writeStartTag( BLOCK_TAG, "toc.number.style" );
+
+            NumberedListItem current = (NumberedListItem) tocStack.peek();
+            current.next();
+            write( currentTocNumber() );
+
+            writeEndTag( BLOCK_TAG );
+            writeEndTag( TABLE_CELL_TAG );
+
+            String span = "3";
+
+            if ( level > 2 )
+            {
+                span = Integer.toString( 5 - level );
+            }
+
+            writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", span, "toc.cell" );
+            MutableAttributeSet atts = getFoConfiguration().getAttributeSet( "toc.h" + level + ".style" );
+            atts.addAttribute( "text-align-last", "justify" );
+            writeStartTag( BLOCK_TAG, atts );
+            writeStartTag( BASIC_LINK_TAG, "internal-destination", ref );
+            write( tocItem.getName() );
+            writeEndTag( BASIC_LINK_TAG );
+            writeEmptyTag( LEADER_TAG, "toc.leader.style" );
+            writeStartTag( INLINE_TAG, "page.number" );
+            writeEmptyTag( PAGE_NUMBER_CITATION_TAG, "ref-id", ref );
+            writeEndTag( INLINE_TAG );
+            writeEndTag( BLOCK_TAG );
+            writeEndTag( TABLE_CELL_TAG );
+            writeEndTag( TABLE_ROW_TAG );
+
+            if ( tocItem.getItems() != null )
+            {
+                writeTocItems( tocItem.getItems(), level + 1 );
+            }
+        }
+
+        tocStack.pop();
+    }
+
+    private String currentTocNumber()
+    {
+        String ch = ( (NumberedListItem) tocStack.get( 0 ) ).getListItemSymbol();
+
+        for ( int i = 1; i < tocStack.size(); i++ )
+        {
+            ch = ch + "." + ( (NumberedListItem) tocStack.get( i ) ).getListItemSymbol();
+        }
+
+        return ch;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a fo:bookmark-tree. The DocumentModel has to contain a DocumentTOC for this to work.
+     */
+    protected void pdfBookmarks()
+    {
+        if ( docModel == null || docModel.getToc() == null )
+        {
+            return;
+        }
+
+        writeStartTag( BOOKMARK_TREE_TAG );
+
+        renderBookmarkItems( docModel.getToc().getItems() );
+
+        writeEndTag( BOOKMARK_TREE_TAG );
+    }
+
+    private void renderBookmarkItems( List items )
+    {
+        for ( Iterator k = items.iterator(); k.hasNext(); )
+        {
+            DocumentTOCItem tocItem = (DocumentTOCItem) k.next();
+
+            String ref = getIdName( tocItem.getRef() );
+
+            writeStartTag( BOOKMARK_TAG, "internal-destination", ref );
+            writeStartTag( BOOKMARK_TITLE_TAG );
+            write( tocItem.getName() );
+            writeEndTag( BOOKMARK_TITLE_TAG );
+
+            if ( tocItem.getItems() != null )
+            {
+                renderBookmarkItems( tocItem.getItems() );
+            }
+
+            writeEndTag( BOOKMARK_TAG );
+        }
+    }
+
+    /**
+     * Writes a cover page. The DocumentModel has to contain a DocumentMeta for this to work.
+     */
+    public void coverPage()
+    {
+        if ( this.docModel == null )
+        {
+            return;
+        }
+
+        DocumentCover cover = docModel.getCover();
+        DocumentMeta meta = docModel.getMeta();
+
+        if ( cover == null && meta == null )
+        {
+            return; // no information for cover page: ignore
+        }
+
+        // TODO: remove hard-coded settings
+
+        writeStartTag( PAGE_SEQUENCE_TAG, "master-reference", "cover-page" );
+        writeStartTag( FLOW_TAG, "flow-name", "xsl-region-body" );
+        writeStartTag( BLOCK_TAG, "text-align", "center" );
+        writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "3.125in" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "3.125in" );
+        writeStartTag( TABLE_BODY_TAG );
+
+        writeCoverHead( cover );
+        writeCoverBody( cover, meta );
+        writeCoverFooter( cover, meta );
+
+        writeEndTag( TABLE_BODY_TAG );
+        writeEndTag( TABLE_TAG );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( FLOW_TAG );
+        writeEndTag( PAGE_SEQUENCE_TAG );
+    }
+
+    private void writeCoverHead( DocumentCover cover )
+    {
+        if ( cover == null )
+        {
+            return;
+        }
+
+        String compLogo = cover.getCompanyLogo();
+        String projLogo = cover.getProjectLogo();
+
+        writeStartTag( TABLE_ROW_TAG, "height", COVER_HEADER_HEIGHT );
+        writeStartTag( TABLE_CELL_TAG );
+
+        if ( StringUtils.isNotEmpty( compLogo ) )
+        {
+            SinkEventAttributeSet atts = new SinkEventAttributeSet();
+            atts.addAttribute( "text-align", "left" );
+            atts.addAttribute( "vertical-align", "top" );
+            writeStartTag( BLOCK_TAG, atts );
+            figureGraphics( compLogo, getGraphicsAttributes( compLogo ) );
+            writeEndTag( BLOCK_TAG );
+        }
+
+        writeSimpleTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+        writeStartTag( TABLE_CELL_TAG );
+
+        if ( StringUtils.isNotEmpty( projLogo ) )
+        {
+            SinkEventAttributeSet atts = new SinkEventAttributeSet();
+            atts.addAttribute( "text-align", "right" );
+            atts.addAttribute( "vertical-align", "top" );
+            writeStartTag( BLOCK_TAG, atts );
+            figureGraphics( projLogo, getGraphicsAttributes( projLogo ) );
+            writeEndTag( BLOCK_TAG );
+        }
+
+        writeSimpleTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+    }
+
+    private void writeCoverBody( DocumentCover cover, DocumentMeta meta )
+    {
+        if ( cover == null && meta == null )
+        {
+            return;
+        }
+
+        String subtitle = null;
+        String title = null;
+        String type = null;
+        String version = null;
+        if ( cover == null )
+        {
+            // aleady checked that meta != null
+            getLog().debug( "The DocumentCover is not defined, using the DocumentMeta title as cover title." );
+            title = meta.getTitle();
+        }
+        else
+        {
+            subtitle = cover.getCoverSubTitle();
+            title = cover.getCoverTitle();
+            type = cover.getCoverType();
+            version = cover.getCoverVersion();
+        }
+
+        writeln( "<fo:table-row keep-with-previous=\"always\" height=\"0.014in\">" );
+        writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
+        writeStartTag( BLOCK_TAG, "line-height", "0.014in" );
+        writeEmptyTag( LEADER_TAG, "chapter.rule" );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+
+        writeStartTag( TABLE_ROW_TAG, "height", "7.447in" );
+        writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
+        writeln( "<fo:table table-layout=\"fixed\" width=\"100%\" >" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
+        writeEmptyTag( TABLE_COLUMN_TAG, "column-width", "2.083in" );
+
+        writeStartTag( TABLE_BODY_TAG );
+
+        writeStartTag( TABLE_ROW_TAG );
+        writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "3" );
+        writeSimpleTag( BLOCK_TAG );
+        writeEmptyTag( BLOCK_TAG, "space-before", "3.2235in" );
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+
+        writeStartTag( TABLE_ROW_TAG );
+        writeStartTag( TABLE_CELL_TAG );
+        writeEmptyTag( BLOCK_TAG, "space-after", "0.5in" );
+        writeEndTag( TABLE_CELL_TAG );
+
+        writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2", "cover.border.left" );
+        writeStartTag( BLOCK_TAG, "cover.title" );
+        write( title == null ? "" : title );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+
+        writeStartTag( TABLE_ROW_TAG );
+        writeStartTag( TABLE_CELL_TAG );
+        writeSimpleTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+
+        writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2", "cover.border.left.bottom" );
+        writeStartTag( BLOCK_TAG, "cover.subtitle" );
+        write( subtitle == null ? ( version == null ? "" : " v. " + version ) : subtitle );
+        writeEndTag( BLOCK_TAG );
+        writeStartTag( BLOCK_TAG, "cover.subtitle" );
+        write( type == null ? "" : type );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+
+        writeEndTag( TABLE_BODY_TAG );
+        writeEndTag( TABLE_TAG );
+
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+
+        writeStartTag( TABLE_ROW_TAG, "height", "0.014in" );
+        writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
+        writeln( "<fo:block space-after=\"0.2in\" line-height=\"0.014in\">" );
+        writeEmptyTag( LEADER_TAG, "chapter.rule" );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+
+        writeStartTag( TABLE_ROW_TAG );
+        writeStartTag( TABLE_CELL_TAG, "number-columns-spanned", "2" );
+        writeSimpleTag( BLOCK_TAG );
+        writeEmptyTag( BLOCK_TAG, "space-before", "0.2in" );
+        writeEndTag( TABLE_CELL_TAG );
+        writeEndTag( TABLE_ROW_TAG );
+    }
+
+    private void writeCoverFooter( DocumentCover cover, DocumentMeta meta )
+    {
+        if ( cover == null && meta == null )
+        {
+            return;
+        }
+
+        String date = null;
+        String compName = null;
+        if ( cover == null )
+        {
+            // aleady checked that meta != null
+            getLog().debug( "The DocumentCover is not defined, using the DocumentMeta author as company name." );
+            compName = meta.getAuthor();
+        }
+        else
+        {
+            compName = cover.getCompanyName();
+
+            if ( cover.getCoverdate() == null )
+            {
+                cover.setCoverDate( new Date() );
+                date = cover.getCoverdate();
+                cover.setCoverDate( null );
+            }
+            else
+            {
+                date = cover.getCoverdate();
+            }
+        }
+
+        writeStartTag( TABLE_ROW_TAG, "height", "0.3in" );
+
+        writeStartTag( TABLE_CELL_TAG );
+        MutableAttributeSet att = getFoConfiguration().getAttributeSet( "cover.subtitle" );
+        att.addAttribute( "height", "0.3in" );
+        att.addAttribute( "text-align", "left" );
+        writeStartTag( BLOCK_TAG, att );
+        write( compName == null ? ( cover.getAuthor() == null ? "" : cover.getAuthor() ) : compName );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+
+        writeStartTag( TABLE_CELL_TAG );
+        att = getFoConfiguration().getAttributeSet( "cover.subtitle" );
+        att.addAttribute( "height", "0.3in" );
+        att.addAttribute( "text-align", "right" );
+        writeStartTag( BLOCK_TAG, att );
+        write( date == null ? "" : date );
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( TABLE_CELL_TAG );
+
+        writeEndTag( TABLE_ROW_TAG );
+    }
+
+    private ResourceBundle getBundle( Locale locale )
+    {
+        return ResourceBundle.getBundle( "doxia-fo", locale, this.getClass().getClassLoader() );
+    }
+
+    private SinkEventAttributeSet getGraphicsAttributes( String logo )
+    {
+        MutableAttributeSet atts = null;
+
+        try
+        {
+            atts = DoxiaUtils.getImageAttributes( logo );
+        }
+        catch ( IOException e )
+        {
+            getLog().debug( e );
+        }
+
+        if ( atts == null )
+        {
+            return new SinkEventAttributeSet( new String[] { SinkEventAttributes.HEIGHT, COVER_HEADER_HEIGHT } );
+        }
+
+        // FOP dpi: 72
+        // Max width : 3.125 inch, table cell size, see #coverPage()
+        final int maxWidth = 225; // 3.125 * 72
+
+        if ( Integer.parseInt( atts.getAttribute( SinkEventAttributes.WIDTH ).toString() ) > maxWidth )
+        {
+            atts.addAttribute( "content-width", "3.125in" );
+        }
+
+        return new SinkEventAttributeSet( atts );
+    }
+}
diff --git a/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoConfiguration.java b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoConfiguration.java
new file mode 100644
index 0000000..48bacfe
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoConfiguration.java
@@ -0,0 +1,216 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.apache.maven.doxia.sink.SinkUtils;
+import org.codehaus.plexus.util.ReaderFactory;
+
+/**
+ * A utility class to construct FO configuration parameters.
+ *
+ * @author ltheussl
+ * @version $Id: FoConfiguration.java 775622 2009-05-17 10:44:33Z vsiveton $
+ * @since 1.1
+ */
+public class FoConfiguration
+{
+    /** Holds the single attributes. */
+    private MutableAttributeSet attributeSet;
+
+    /** The configuration instance. */
+    private final XMLConfiguration config;
+
+    /** The list of attribute sets. */
+    private List sets;
+
+    /**
+     * Constructor.
+     */
+    public FoConfiguration()
+    {
+        this.config = new XMLConfiguration();
+
+        // necessary because some attributes contain commas:
+        config.setDelimiterParsingDisabled( true );
+
+        loadDefaultConfig();
+    }
+
+    /**
+     * Load configuration parameters from a File.
+     *
+     * @param configFile the configuration file.
+     *
+     * @throws java.io.IOException if the File cannot be read
+     *  or some error occurs when initializing the configuration parameters.
+     *
+     * @since 1.1.1
+     */
+    public void load( File configFile )
+            throws IOException
+    {
+        config.clear();
+
+        try
+        {
+            config.load( configFile );
+        }
+        catch ( ConfigurationException cex )
+        {
+            IOException ioe = new IOException();
+            ioe.initCause( cex );
+            throw ioe;
+        }
+
+        loadDefaultConfig(); // this adds default values that are missing from above
+    }
+
+    /**
+     * Builds a list of attributes.
+     *
+     * @param attributeId A unique id to identify the set of attributes.
+     * This should correspond to the name of an attribute-set
+     * defined in the configuration file.
+     * @return A string that contains a list of attributes with
+     * the values configured for the current builder. Returns the
+     * empty string if attributeId is null or if attributeId
+     * is not a valid identifier.
+     */
+    public String getAttributeString( String attributeId )
+    {
+        if ( attributeId == null )
+        {
+            return "";
+        }
+
+        reset();
+        addAttributes( attributeId );
+
+        return SinkUtils.getAttributeString( attributeSet );
+    }
+
+    /**
+     * Builds a set of attributes.
+     *
+     * @param attributeId A unique id to identify the set of attributes.
+     * This should correspond to the name of an attribute-set
+     * defined in the configuration file.
+     * @return A MutableAttributeSet that contains the attributes with
+     * the values configured for the current builder. Returns null
+     * if attributeId is null or empty, or if attributeId is not a valid identifier.
+     */
+    public MutableAttributeSet getAttributeSet( String attributeId )
+    {
+        if ( attributeId == null || attributeId.length() == 0 )
+        {
+            return null;
+        }
+
+        reset();
+        addAttributes( attributeId );
+
+        if ( attributeSet.getAttributeCount() == 0 )
+        {
+            return null;
+        }
+
+        return attributeSet;
+    }
+
+    /**
+     * Adds an attribute to the current StringBuffer.
+     *
+     * @param attributeId A unique id to identify the set of attributes.
+     * This should correspond to the name of an attribute-set
+     * defined in the configuration file.
+     */
+    private void addAttributes( String attributeId )
+    {
+        int index = sets.indexOf( attributeId );
+        String keybase = "xsl:attribute-set(" + String.valueOf( index ) + ")";
+
+        Object prop = config.getProperty( keybase + ".xsl:attribute" );
+
+        if ( prop instanceof List )
+        {
+            List values = (List) prop;
+            List keys = config.getList( keybase + ".xsl:attribute[@name]" );
+
+            for ( int i = 0; i < values.size(); i++ )
+            {
+                attributeSet.addAttribute( keys.get( i ), values.get( i ) );
+            }
+        }
+        else if ( prop instanceof String )
+        {
+            String value = config.getString( keybase + ".xsl:attribute" );
+            String key = config.getString( keybase + ".xsl:attribute[@name]" );
+            attributeSet.addAttribute( key, value );
+        }
+
+        String extend = config.getString( keybase + "[@use-attribute-sets]" );
+
+        if ( extend != null )
+        {
+            addAttributes( extend );
+        }
+    }
+
+    /** Load the default fo configuration file. */
+    private void loadDefaultConfig()
+    {
+        try
+        {
+            config.load( ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/fo-styles.xslt" ) ) );
+        }
+        catch ( ConfigurationException cex )
+        {
+            // this should not happen
+            throw new RuntimeException( cex );
+        }
+        catch ( IOException e )
+        {
+            // this should not happen
+            throw new RuntimeException( e );
+        }
+
+        this.sets = config.getList( "xsl:attribute-set[@name]" );
+        reset();
+    }
+
+    /**
+     * (Re-)initialize the AttributeSet.
+     */
+    private void reset()
+    {
+        this.attributeSet = new SimpleAttributeSet();
+    }
+
+}
diff --git a/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoMarkup.java b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoMarkup.java
new file mode 100644
index 0000000..59ed0fe
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoMarkup.java
@@ -0,0 +1,333 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.markup.XmlMarkup;
+
+/**
+ * List of <code>FO</code> markups.
+ *
+ * @author ltheussl
+ * @version $Id: FoMarkup.java 746232 2009-02-20 13:32:44Z ltheussl $
+ * @since 1.1
+ */
+public interface FoMarkup
+    extends XmlMarkup
+{
+    /** FO namespace: "http://www.w3.org/1999/XSL/Format" */
+    String FO_NAMESPACE = "http://www.w3.org/1999/XSL/Format";
+
+    // ----------------------------------------------------------------------
+    // Specific FO tags
+    // ----------------------------------------------------------------------
+
+    /** FO tag for <code>root</code>. */
+    Tag ROOT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "root";
+        }
+    };
+
+    /** FO tag for <code>layout-master-set</code>. */
+    Tag LAYOUT_MASTER_SET_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "layout-master-set";
+        }
+    };
+
+    /** FO tag for <code>simple-page-master</code>. */
+    Tag SIMPLE_PAGE_MASTER_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "simple-page-master";
+        }
+    };
+
+    /** FO tag for <code>region-body</code>. */
+    Tag REGION_BODY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "region-body";
+        }
+    };
+
+    /** FO tag for <code>region-before</code>. */
+    Tag REGION_BEFORE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "region-before";
+        }
+    };
+
+    /** FO tag for <code>region-after</code>. */
+    Tag REGION_AFTER_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "region-after";
+        }
+    };
+
+    /** FO tag for <code>static-content</code>. */
+    Tag STATIC_CONTENT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "static-content";
+        }
+    };
+
+
+    /** FO tag for <code>page-sequence</code>. */
+    Tag PAGE_SEQUENCE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "page-sequence";
+        }
+    };
+
+    /** FO tag for <code>flow</code>. */
+    Tag FLOW_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "flow";
+        }
+    };
+
+    /** FO tag for <code>block</code>. */
+    Tag BLOCK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "block";
+        }
+    };
+
+    /** FO tag for <code>list-block</code>. */
+    Tag LIST_BLOCK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "list-block";
+        }
+    };
+
+    /** FO tag for <code>list-item</code>. */
+    Tag LIST_ITEM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "list-item";
+        }
+    };
+
+    /** FO tag for <code>list-item-body</code>. */
+    Tag LIST_ITEM_BODY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "list-item-body";
+        }
+    };
+
+    /** FO tag for <code>list-item-label</code>. */
+    Tag LIST_ITEM_LABEL_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "list-item-label";
+        }
+    };
+
+    /** FO tag for <code>table-and-caption</code>. */
+    Tag TABLE_AND_CAPTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "table-and-caption";
+        }
+    };
+
+    /** FO tag for <code>table</code>. */
+    Tag TABLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "table";
+        }
+    };
+
+    /** FO tag for <code>table-body</code>. */
+    Tag TABLE_BODY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "table-body";
+        }
+    };
+
+    /** FO tag for <code>table-column</code>. */
+    Tag TABLE_COLUMN_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "table-column";
+        }
+    };
+
+    /** FO tag for <code>table-row</code>. */
+    Tag TABLE_ROW_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "table-row";
+        }
+    };
+
+    /** FO tag for <code>table-cell</code>. */
+    Tag TABLE_CELL_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "table-cell";
+        }
+    };
+
+    /** FO tag for <code>table-caption</code>. */
+    Tag TABLE_CAPTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "table-caption";
+        }
+    };
+
+    /** FO tag for <code>inline</code>. */
+    Tag INLINE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "inline";
+        }
+    };
+
+    /** FO tag for <code>basic-link</code>. */
+    Tag BASIC_LINK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "basic-link";
+        }
+    };
+
+    /** FO tag for <code>leader</code>. */
+    Tag LEADER_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "leader";
+        }
+    };
+
+    /** FO tag for <code>page-number</code>. */
+    Tag PAGE_NUMBER_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "page-number";
+        }
+    };
+
+    /** FO tag for <code>page-number-citation</code>. */
+    Tag PAGE_NUMBER_CITATION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "page-number-citation";
+        }
+    };
+
+    /** FO tag for <code>bookmark-tree</code>. */
+    Tag BOOKMARK_TREE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bookmark-tree";
+        }
+    };
+
+    /** FO tag for <code>bookmark</code>. */
+    Tag BOOKMARK_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bookmark";
+        }
+    };
+
+    /** FO tag for <code>bookmark-title</code>. */
+    Tag BOOKMARK_TITLE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "bookmark-title";
+        }
+    };
+}
diff --git a/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoSink.java b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoSink.java
new file mode 100644
index 0000000..2faa7a6
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoSink.java
@@ -0,0 +1,1831 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeSet;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML.Attribute;
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.sink.AbstractXmlSink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.sink.SinkUtils;
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+
+/**
+ * A Doxia Sink that produces a FO model. The usage is similar to the following:
+ *
+ * <pre>
+ * FoSink sink = new FoSink( writer );
+ * sink.beginDocument();
+ * ...
+ * sink.endDocument();
+ * </pre>
+ *
+ * @author ltheussl
+ * @version $Id: FoSink.java 812141 2009-09-07 13:54:48Z ltheussl $
+ * @since 1.1
+ */
+public class FoSink
+    extends AbstractXmlSink
+    implements FoMarkup
+{
+    /** For writing the result. */
+    private final PrintWriter out;
+
+    /** Used to get the current position in numbered lists. */
+    private final Stack listStack;
+
+    /** Used to get attributes for a given FO element. */
+    private final FoConfiguration config;
+
+    /** Counts the current section level. */
+    private int section = 0;
+
+    /** Counts the current subsection level. */
+    private int subsection = 0;
+
+    /** Counts the current subsubsection level. */
+    private int subsubsection = 0;
+
+    /** Verbatim flag. */
+    private boolean verbatim;
+
+    /** figure flag. */
+    private boolean inFigure;
+
+    private final String encoding;
+
+    private final String languageId;
+
+    /** Stack of drawing borders on table cells. */
+    private final LinkedList tableGridStack;
+
+    /** Stack of alignment int[] of table cells. */
+    private final LinkedList cellJustifStack;
+
+    /** Stack of justification of table cells. */
+    private final LinkedList isCellJustifStack;
+
+    /** Stack of current table cell. */
+    private final LinkedList cellCountStack;
+
+    /** The stack of StringWriter to write the table result temporary, so we could play with the output and fix fo. */
+    private final LinkedList tableContentWriterStack;
+
+    private final LinkedList tableCaptionWriterStack;
+
+    private final LinkedList tableCaptionXMLWriterStack;
+
+    /** The stack of table caption */
+    private final LinkedList tableCaptionStack;
+
+    /** Map of warn messages with a String as key to describe the error type and a Set as value.
+     * Using to reduce warn messages. */
+    protected Map warnMessages;
+
+    /**
+     * Constructor, initialize the Writer.
+     *
+     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newXmlWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     */
+    protected FoSink( Writer writer )
+    {
+        this( writer, "UTF-8" );
+    }
+
+    /**
+     * Constructor, initialize the Writer and tells which encoding is used.
+     *
+     * @param writer not null writer to write the result.
+     * @param encoding the encoding used, that should be written to the generated HTML content
+     * if not <code>null</code>.
+     */
+    protected FoSink( Writer writer, String encoding )
+    {
+        this( writer, encoding, null );
+    }
+
+    /**
+     * Constructor, initialize the Writer and tells which encoding and languageId are used.
+     *
+     * @param writer not null writer to write the result.
+     * @param encoding the encoding used, that should be written to the generated HTML content
+     * if not <code>null</code>.
+     * @param languageId language identifier for the root element as defined by
+     * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
+     * in addition, the empty string may be specified.
+     */
+    protected FoSink( Writer writer, String encoding, String languageId )
+    {
+        if ( writer == null )
+        {
+            throw new NullPointerException( "Null writer in FO Sink!" );
+        }
+
+        this.out = new PrintWriter( writer );
+        this.encoding = encoding;
+        this.languageId = languageId;
+        this.config = new FoConfiguration();
+
+        this.listStack = new Stack();
+        this.tableGridStack = new LinkedList();
+        this.cellJustifStack = new LinkedList();
+        this.isCellJustifStack = new LinkedList();
+        this.cellCountStack = new LinkedList();
+        this.tableContentWriterStack = new LinkedList();
+        this.tableCaptionWriterStack = new LinkedList();
+        this.tableCaptionXMLWriterStack = new LinkedList();
+        this.tableCaptionStack = new LinkedList();
+
+        setNameSpace( "fo" );
+    }
+
+    // TODO add FOP compliance mode?
+
+    /**
+     * Load configuration parameters from a File.
+     *
+     * @param configFile the configuration file.
+     *
+     * @throws java.io.IOException if the File cannot be read
+     *  or some error occurs when initializing the configuration parameters.
+     *
+     * @since 1.1.1
+     */
+    public void load( File configFile )
+            throws IOException
+    {
+        config.load( configFile );
+    }
+
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        init();
+
+        startPageSequence( "0", null, null );
+    }
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        head( null );
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        writeStartTag( BLOCK_TAG, "doc.header.title" );
+    }
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        title( null );
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        writeStartTag( BLOCK_TAG, "doc.header.author" );
+    }
+
+    /** {@inheritDoc} */
+    public void author()
+    {
+        author( null );
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        writeStartTag( BLOCK_TAG, "doc.header.date" );
+    }
+
+    /** {@inheritDoc} */
+    public void date()
+    {
+        date( null );
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        // noop
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        body( null );
+    }
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        writeEOL();
+        writeEndTag( FLOW_TAG );
+        writeEOL();
+        writeEndTag( PAGE_SEQUENCE_TAG );
+        writeEOL();
+        endDocument();
+    }
+
+    // -----------------------------------------------------------------------
+    //
+    // -----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void sectionTitle()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section( int level, SinkEventAttributes attributes )
+    {
+        if ( level == SECTION_LEVEL_1 )
+        {
+            section++;
+            subsection = 0;
+            subsubsection = 0;
+        }
+        else if ( level == SECTION_LEVEL_2 )
+        {
+            subsection++;
+            subsubsection = 0;
+        }
+        else if ( level == SECTION_LEVEL_3 )
+        {
+            subsubsection++;
+        }
+
+        onSection();
+    }
+
+    /** {@inheritDoc} */
+    public void section_( int level )
+    {
+        onSection_();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        onSectionTitle( level );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+        onSectionTitle_();
+    }
+
+    /** {@inheritDoc} */
+    public void section1()
+    {
+        section( SECTION_LEVEL_1, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        sectionTitle( SECTION_LEVEL_1, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        sectionTitle_( SECTION_LEVEL_1 );
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        section_( SECTION_LEVEL_1 );
+    }
+
+    /** {@inheritDoc} */
+    public void section2()
+    {
+        section( SECTION_LEVEL_2, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        sectionTitle( SECTION_LEVEL_2, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        sectionTitle_( SECTION_LEVEL_2 );
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        section_( SECTION_LEVEL_2 );
+    }
+
+    /** {@inheritDoc} */
+    public void section3()
+    {
+        section( SECTION_LEVEL_3, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        sectionTitle( SECTION_LEVEL_3, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        sectionTitle_( SECTION_LEVEL_3 );
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        section_( SECTION_LEVEL_3 );
+    }
+
+    /** {@inheritDoc} */
+    public void section4()
+    {
+        section( SECTION_LEVEL_4, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        sectionTitle( SECTION_LEVEL_4, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        sectionTitle_( SECTION_LEVEL_4 );
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        section_( SECTION_LEVEL_4 );
+    }
+
+    /** {@inheritDoc} */
+    public void section5()
+    {
+        section( SECTION_LEVEL_5, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        sectionTitle( SECTION_LEVEL_5, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        sectionTitle_( SECTION_LEVEL_5 );
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        section_( SECTION_LEVEL_5 );
+    }
+
+    /** Starts a section/subsection. */
+    private void onSection()
+    {
+        writeEOL();
+        writeStartTag( BLOCK_TAG, "body.text" );
+    }
+
+    /**
+     * Starts a section title.
+     *
+     * @param depth The section level.
+     */
+    private void onSectionTitle( int depth )
+    {
+        StringBuffer title = new StringBuffer( 16 );
+
+        title.append( getChapterString() );
+
+        writeEOL();
+        if ( depth == SECTION_LEVEL_1 )
+        {
+            writeStartTag( BLOCK_TAG, "body.h1" );
+            title.append( section ).append( "   " );
+        }
+        else if ( depth == SECTION_LEVEL_2 )
+        {
+            writeStartTag( BLOCK_TAG, "body.h2" );
+            title.append( section ).append( "." );
+            title.append( subsection ).append( "   " );
+        }
+        else if ( depth == SECTION_LEVEL_3 )
+        {
+            writeStartTag( BLOCK_TAG, "body.h3" );
+            title.append( section ).append( "." );
+            title.append( subsection ).append( "." );
+            title.append( subsubsection ).append( "   " );
+        }
+        else if ( depth == SECTION_LEVEL_4 )
+        {
+            writeStartTag( BLOCK_TAG, "body.h4" );
+        }
+        else
+        {
+            writeStartTag( BLOCK_TAG, "body.h5" );
+        }
+
+        write( title.toString() );
+    }
+
+    /** Ends a section title. */
+    private void onSectionTitle_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** Ends a section/subsection. */
+    private void onSection_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /**
+     * Resets the section counter to 0.
+     * Only useful for overriding classes, like AggregateSink, the FoSink puts everything into one chapter.
+     */
+    protected void resetSectionCounter()
+    {
+        this.section = 0;
+    }
+
+    /**
+     * Returns the current chapter number as a string.
+     * By default does nothing, gets overridden by AggregateSink.
+     *
+     * @return an empty String.
+     */
+    protected String getChapterString()
+    {
+        return "";
+    }
+
+    // -----------------------------------------------------------------------
+    //
+    // -----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void list( SinkEventAttributes attributes )
+    {
+        writeEOL();
+        writeStartTag( LIST_BLOCK_TAG, "list" );
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        list( null );
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        writeEndTag( LIST_BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        writeStartTag( LIST_ITEM_TAG, "list.item" );
+        writeStartTag( LIST_ITEM_LABEL_TAG );
+        writeStartTag( BLOCK_TAG );
+        write( "•" ); // TODO customize?
+        writeEndTag( BLOCK_TAG );
+        writeEndTag( LIST_ITEM_LABEL_TAG );
+        writeEOL();
+        writeStartTag( LIST_ITEM_BODY_TAG, "list.item" );
+        writeEOL();
+        writeStartTag( BLOCK_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        listItem( null );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+        writeEndTag( LIST_ITEM_BODY_TAG );
+        writeEOL();
+        writeEndTag( LIST_ITEM_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        this.listStack.push( new NumberedListItem( numbering ) );
+        writeEOL();
+        writeStartTag( LIST_BLOCK_TAG, "list" );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        numberedList( numbering, null );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        this.listStack.pop();
+        writeEndTag( LIST_BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        NumberedListItem current = (NumberedListItem) this.listStack.peek();
+        current.next();
+
+        writeStartTag( LIST_ITEM_TAG, "list.item" );
+
+        writeEOL();
+        writeStartTag( LIST_ITEM_LABEL_TAG );
+        writeEOL();
+        writeStartTag( BLOCK_TAG );
+        write( current.getListItemSymbol() );
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+        writeEndTag( LIST_ITEM_LABEL_TAG );
+        writeEOL();
+
+        writeStartTag( LIST_ITEM_BODY_TAG, "list.item" );
+        writeEOL();
+        writeStartTag( BLOCK_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        numberedListItem( null );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+        writeEndTag( LIST_ITEM_BODY_TAG );
+        writeEOL();
+        writeEndTag( LIST_ITEM_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        writeEOL();
+        writeStartTag( BLOCK_TAG, "dl" );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList()
+    {
+        definitionList( null );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem( SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        definitionListItem( null );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        writeStartTag( BLOCK_TAG, "dt" );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        definedTerm( null );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void definition( SinkEventAttributes attributes )
+    {
+        writeEOL();
+        writeStartTag( BLOCK_TAG, "dd" );
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        definition( null );
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void figure( SinkEventAttributes attributes )
+    {
+        this.inFigure = true;
+        writeEOL();
+        writeStartTag( BLOCK_TAG, "figure.display" );
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        figure( null );
+    }
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        this.inFigure = false;
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        figureGraphics( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = config.getAttributeSet( "figure.graphics" );
+        atts.addAttribute( Attribute.SRC.toString(), src );
+
+        // http://xmlgraphics.apache.org/fop/graphics.html#resolution
+
+        final String[] valids = new String[] {"content-height", "content-width", "height", "width"};
+        final MutableAttributeSet filtered = SinkUtils.filterAttributes( attributes, valids );
+
+        if ( filtered != null )
+        {
+            atts.addAttributes( filtered );
+        }
+
+        writeln( "<fo:external-graphic" + SinkUtils.getAttributeString( atts ) + "/>" );
+    }
+
+    /**
+     * Flags if we are inside a figure.
+     *
+     * @return True if we are between {@link #figure()} and {@link #figure_()} calls.
+     */
+    protected boolean isFigure()
+    {
+        return this.inFigure;
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        writeStartTag( BLOCK_TAG, "figure.caption" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption()
+    {
+        figureCaption( null );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph()
+    {
+        paragraph( null );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = config.getAttributeSet( "normal.paragraph" );
+
+        if ( attributes != null && attributes.isDefined( SinkEventAttributes.ALIGN ) )
+        {
+            atts.addAttribute( "text-align", attributes.getAttribute( SinkEventAttributes.ALIGN ) );
+        }
+
+        writeEOL();
+        writeStartTag( BLOCK_TAG, atts );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        this.verbatim = true;
+
+        boolean boxed = false;
+
+        if ( attributes != null && attributes.isDefined( SinkEventAttributes.DECORATION ) )
+        {
+            boxed =
+                "boxed".equals( attributes.getAttribute( SinkEventAttributes.DECORATION ).toString() );
+        }
+
+        if ( boxed )
+        {
+            writeStartTag( BLOCK_TAG, "body.source" );
+        }
+        else
+        {
+            writeStartTag( BLOCK_TAG, "body.pre" );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        verbatim( boxed ? SinkEventAttributeSet.BOXED : null );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        this.verbatim = false;
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        writeEOL();
+        writeEOL();
+        writeStartTag( BLOCK_TAG );
+        writeEmptyTag( LEADER_TAG, "body.rule" );
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        horizontalRule( null );
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        writeEmptyTag( BLOCK_TAG, "break-before", "page" );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        writeEOL();
+        writeStartTag( BLOCK_TAG, "table.padding" );
+
+        // <fo:table-and-caption> is XSL-FO 1.0 standard but still not implemented in FOP 0.95
+        //writeStartTag( TABLE_AND_CAPTION_TAG );
+
+        this.tableContentWriterStack.addLast( new StringWriter() );
+        writeStartTag( TABLE_TAG, "table.layout" );
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        table( null );
+    }
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        String content = this.tableContentWriterStack.removeLast().toString();
+
+        StringBuffer sb = new StringBuffer();
+        int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() );
+        for ( int i = 0; i < cellCount; i++ )
+        {
+            sb.append( "<fo:table-column column-width=\"proportional-column-width(1)\"/>" );
+            sb.append( EOL );
+        }
+
+        int index = content.indexOf( ">" ) + 1;
+        writeln( content.substring( 0, index ) );
+        write( sb.toString() );
+        write( content.substring( index ) );
+
+        writeEndTag( TABLE_TAG );
+        writeEOL();
+
+        // <fo:table-and-caption> is XSL-FO 1.0 standard but still not implemented in FOP 0.95
+        //writeEndTag( TABLE_AND_CAPTION_TAG );
+
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+
+        if ( !this.tableCaptionStack.isEmpty() && this.tableCaptionStack.getLast() != null )
+        {
+            paragraph( SinkEventAttributeSet.CENTER );
+            write( this.tableCaptionStack.removeLast().toString() );
+            paragraph_();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        this.tableGridStack.addLast( Boolean.valueOf( grid ) );
+        this.cellJustifStack.addLast( justification );
+        this.isCellJustifStack.addLast( Boolean.valueOf( true ) );
+        this.cellCountStack.addLast( new Integer( 0 ) );
+        writeEOL();
+        writeStartTag( TABLE_BODY_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        this.tableGridStack.removeLast();
+        this.cellJustifStack.removeLast();
+        this.isCellJustifStack.removeLast();
+        writeEndTag( TABLE_BODY_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        // TODO spacer rows
+        writeStartTag( TABLE_ROW_TAG, "table.body.row" );
+        this.cellCountStack.removeLast();
+        this.cellCountStack.addLast( new Integer( 0 ) );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        tableRow( null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        writeEndTag( TABLE_ROW_TAG );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        tableCell( false, attributes );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        tableCell( (SinkEventAttributes) null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        // TODO: fop can't handle cell width
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        tableCell( true, attributes );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        tableHeaderCell( (SinkEventAttributes) null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        // TODO: fop can't handle cell width
+        tableHeaderCell();
+    }
+
+    /**
+     * Writes a table cell.
+     *
+     * @param headerRow true if this is a header cell.
+     * @param attributes the cell attributes, could be null.
+     */
+    private void tableCell( boolean headerRow, SinkEventAttributes attributes )
+    {
+        MutableAttributeSet cellAtts = headerRow
+                 ? config.getAttributeSet( "table.heading.cell" )
+                 : config.getAttributeSet( "table.body.cell" );
+
+        // the column-number is needed for the hack to center the table, see tableRows.
+        int cellCount = Integer.parseInt( this.cellCountStack.getLast().toString() );
+        cellAtts.addAttribute( "column-number", String.valueOf( cellCount + 1 ) );
+
+        if ( this.tableGridStack.getLast().equals( Boolean.TRUE ) )
+        {
+            cellAtts.addAttributes( config.getAttributeSet( "table.body.cell.grid" ) );
+        }
+
+        MutableAttributeSet blockAtts = headerRow
+                 ? config.getAttributeSet( "table.heading.block" )
+                 : config.getAttributeSet( "table.body.block" );
+
+        String justif = null;
+        if ( attributes == null )
+        {
+            attributes = new SinkEventAttributeSet( 0 );
+        }
+
+        if ( attributes.isDefined( Attribute.ALIGN.toString() ) )
+        {
+            justif = attributes.getAttribute( Attribute.ALIGN.toString() ).toString();
+        }
+
+        int[] cellJustif = (int[]) this.cellJustifStack.getLast();
+        if ( justif == null && cellJustif != null && cellJustif.length > 0
+            && this.isCellJustifStack.getLast().equals( Boolean.TRUE ) )
+        {
+            switch ( cellJustif[Math.min( cellCount, cellJustif.length - 1 )] )
+            {
+                case JUSTIFY_LEFT:
+                    justif = "left";
+                    break;
+                case JUSTIFY_RIGHT:
+                    justif = "right";
+                    break;
+                case JUSTIFY_CENTER:
+                default:
+                    justif = "center";
+            }
+        }
+
+        if ( justif != null )
+        {
+            blockAtts.addAttribute( "text-align", justif );
+        }
+
+        writeStartTag( TABLE_CELL_TAG, cellAtts );
+        writeEOL();
+        writeStartTag( BLOCK_TAG, blockAtts );
+        writeEOL();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        writeEndTag( BLOCK_TAG );
+        writeEOL();
+        writeEndTag( TABLE_CELL_TAG );
+        writeEOL();
+
+        if ( this.isCellJustifStack.getLast().equals( Boolean.TRUE ) )
+        {
+            int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() );
+            this.cellCountStack.addLast( new Integer( ++cellCount ) );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        tableCell_();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        StringWriter sw = new StringWriter();
+        this.tableCaptionWriterStack.addLast( sw );
+        this.tableCaptionXMLWriterStack.addLast( new PrettyPrintXMLWriter( sw ) );
+
+        // <fo:table-caption> is XSL-FO 1.0 standard but not implemented in FOP 0.95
+        //writeStartTag( TABLE_CAPTION_TAG );
+
+        // TODO: how to implement this otherwise?
+        // table-footer doesn't work because it has to be declared before table-body.
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption()
+    {
+        tableCaption( null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption_()
+    {
+        if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
+        {
+            this.tableCaptionStack.addLast( this.tableCaptionWriterStack.removeLast().toString() );
+            this.tableCaptionXMLWriterStack.removeLast();
+        }
+        // <fo:table-caption> is XSL-FO 1.0 standard but not implemented in FOP 0.95
+        //writeEndTag( TABLE_CAPTION_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Anchor name cannot be null!" );
+        }
+
+        String anchor = name;
+
+        if ( !DoxiaUtils.isValidId( anchor ) )
+        {
+            anchor = DoxiaUtils.encodeId( name, true );
+
+            String msg = "Modified invalid anchor name: '" + name + "' to '" + anchor + "'";
+            logMessage( "modifiedLink", msg );
+        }
+
+        anchor = "#" + name;
+
+        writeStartTag( INLINE_TAG, "id", anchor );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        anchor( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor_()
+    {
+        writeEndTag( INLINE_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Link name cannot be null!" );
+        }
+
+        if ( DoxiaUtils.isExternalLink( name ) )
+        {
+            writeStartTag( BASIC_LINK_TAG, "external-destination", HtmlTools.escapeHTML( name ) );
+            writeStartTag( INLINE_TAG, "href.external" );
+        }
+        else if ( DoxiaUtils.isInternalLink( name ) )
+        {
+            String anchor = name.substring( 1 );
+
+            if ( !DoxiaUtils.isValidId( anchor ) )
+            {
+                anchor = DoxiaUtils.encodeId( anchor, true );
+
+                String msg = "Modified invalid anchor name: '" + name + "' to '" + anchor + "'";
+                logMessage( "modifiedLink", msg );
+            }
+
+            anchor = "#" + anchor;
+
+            writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
+            writeStartTag( INLINE_TAG, "href.internal" );
+        }
+        else
+        {
+            // treat everything else as is
+            String anchor = name;
+
+            writeStartTag( BASIC_LINK_TAG, "internal-destination", HtmlTools.escapeHTML( anchor ) );
+            writeStartTag( INLINE_TAG, "href.internal" );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        link( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        writeEndTag( INLINE_TAG );
+        writeEndTag( BASIC_LINK_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        writeStartTag( INLINE_TAG, "italic" );
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        writeEndTag( INLINE_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        writeStartTag( INLINE_TAG, "bold" );
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        writeEndTag( INLINE_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        writeStartTag( INLINE_TAG, "monospace" );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        writeEndTag( INLINE_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        writeEOL();
+        writeEOL();
+        writeSimpleTag( BLOCK_TAG );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        lineBreak( null );
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        write( " " );
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        content( text );
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        text( text, null );
+    }
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        write( text );
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        out.flush();
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        out.close();
+
+        if ( getLog().isWarnEnabled() && this.warnMessages != null )
+        {
+            for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                Set set = (Set) entry.getValue();
+
+                for ( Iterator it2 = set.iterator(); it2.hasNext(); )
+                {
+                    String msg = (String) it2.next();
+
+                    getLog().warn( msg );
+                }
+            }
+
+            this.warnMessages = null;
+        }
+
+        init();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Unkown events just log a warning message but are ignored otherwise.
+     * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        String msg = "Unknown Sink event: '" + name + "', ignoring!";
+        logMessage( "unknownEvent", msg );
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+
+        if ( StringUtils.isNotEmpty( comment ) && comment.indexOf( "--" ) != -1 )
+        {
+            String originalComment = comment;
+            // http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments
+            while ( comment.indexOf( "--" ) != -1 )
+            {
+                comment = StringUtils.replace( comment, "--", "- -" );
+            }
+
+            String msg = "Modified invalid comment: '" + originalComment + "' to '" + comment + "'";
+            logMessage( "modifyComment", msg );
+        }
+
+        StringBuffer buf = new StringBuffer( comment.length() + 9 );
+
+        buf.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS ).append( SPACE );
+        buf.append( comment );
+        buf.append( SPACE ).append( MINUS ).append( MINUS ).append( GREATER_THAN );
+
+        write( buf.toString() );
+    }
+
+    /**
+     * Writes the beginning of a FO document.
+     */
+    public void beginDocument()
+    {
+        write( "<?xml version=\"1.0\"" );
+        if ( encoding != null )
+        {
+            write( " encoding=\"" + encoding + "\"" );
+        }
+        write( "?>" );
+        writeEOL();
+
+        MutableAttributeSet atts = new SinkEventAttributeSet();
+        atts.addAttribute( "xmlns:" + getNameSpace(), FO_NAMESPACE );
+
+        if ( languageId != null )
+        {
+            atts.addAttribute( "language", languageId );
+        }
+
+        writeStartTag( ROOT_TAG, atts );
+
+        writeStartTag( LAYOUT_MASTER_SET_TAG );
+
+        writeStartTag( SIMPLE_PAGE_MASTER_TAG, "layout.master.set.cover-page" );
+        writeEmptyTag( REGION_BODY_TAG, "layout.master.set.cover-page.region-body" );
+        writeEndTag( SIMPLE_PAGE_MASTER_TAG );
+        writeEOL();
+
+        writeStartTag( SIMPLE_PAGE_MASTER_TAG, "layout.master.set.toc" );
+        writeEmptyTag( REGION_BODY_TAG, "layout.master.set.toc.region-body" );
+        writeEmptyTag( REGION_BEFORE_TAG, "layout.master.set.toc.region-before" );
+        writeEmptyTag( REGION_AFTER_TAG, "layout.master.set.toc.region-after" );
+        writeEndTag( SIMPLE_PAGE_MASTER_TAG );
+        writeEOL();
+
+        writeStartTag( SIMPLE_PAGE_MASTER_TAG, "layout.master.set.body" );
+        writeEmptyTag( REGION_BODY_TAG, "layout.master.set.body.region-body" );
+        writeEmptyTag( REGION_BEFORE_TAG, "layout.master.set.body.region-before" );
+        writeEmptyTag( REGION_AFTER_TAG, "layout.master.set.body.region-after" );
+        writeEndTag( SIMPLE_PAGE_MASTER_TAG );
+        writeEOL();
+
+        writeEndTag( LAYOUT_MASTER_SET_TAG );
+        writeEOL();
+
+        pdfBookmarks();
+    }
+
+    /**
+     * Writes the end of a FO document, flushes and closes the stream.
+     */
+    public void endDocument()
+    {
+        writeEndTag( ROOT_TAG );
+        writeEOL();
+
+        flush();
+        close();
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * Returns the configuration object of this sink.
+     *
+     * @return The configuration object of this sink.
+     */
+    protected FoConfiguration getFoConfiguration()
+    {
+        return config;
+    }
+
+    /**
+     * Writes a start tag, prepending EOL.
+     *
+     * @param tag The tag.
+     * @param attributeId An id identifying the attribute set.
+     */
+    protected void writeStartTag( Tag tag, String attributeId )
+    {
+        writeEOL();
+        writeStartTag( tag, config.getAttributeSet( attributeId ) );
+    }
+
+    /**
+     * Writes a start tag, prepending EOL.
+     *
+     * @param tag The tag.
+     * @param id An id to add.
+     * @param name The name (value) of the id.
+     */
+    protected void writeStartTag( Tag tag, String id, String name )
+    {
+        writeEOL();
+        MutableAttributeSet att = new SinkEventAttributeSet( new String[] {id, name} );
+
+        writeStartTag( tag, att );
+    }
+
+    /**
+     * Writes a start tag, prepending EOL.
+     *
+     * @param tag The tag.
+     * @param id An id to add.
+     * @param name The name (value) of the id.
+     * @param attributeId An id identifying the attribute set.
+     */
+    protected void writeStartTag( Tag tag, String id, String name, String attributeId )
+    {
+        MutableAttributeSet att = config.getAttributeSet( attributeId );
+
+        // make sure we don't add it twice
+        if ( att.isDefined( id ) )
+        {
+            att.removeAttribute( id );
+        }
+
+        att.addAttribute( id, name );
+
+        writeEOL();
+        writeStartTag( tag, att );
+    }
+
+    /**
+     * Writes an empty tag, prepending EOL.
+     *
+     * @param tag The tag.
+     * @param id An id to add.
+     * @param name The name (value) of the id.
+     */
+    protected void writeEmptyTag( Tag tag, String id, String name )
+    {
+        MutableAttributeSet att = new SinkEventAttributeSet( new String[] {id, name} );
+
+        writeEOL();
+        writeSimpleTag( tag, att );
+    }
+
+    /**
+     * Writes a simple tag, appending EOL.
+     *
+     * @param tag The tag name.
+     * @param attributeId An id identifying the attribute set.
+     */
+    protected void writeEmptyTag( Tag tag, String attributeId )
+    {
+        writeEOL();
+        writeSimpleTag( tag, config.getAttributeSet( attributeId ) );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Writes a text, swallowing any exceptions.
+     */
+    protected void write( String text )
+    {
+        if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
+        {
+            ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() ).writeText( unifyEOLs( text ) );
+        }
+        else if ( !this.tableContentWriterStack.isEmpty() && this.tableContentWriterStack.getLast() != null )
+        {
+            ( (StringWriter) this.tableContentWriterStack.getLast() ).write( unifyEOLs( text ) );
+        }
+        else
+        {
+            out.write( unifyEOLs( text ) );
+        }
+    }
+
+    /**
+     * Writes a text, appending EOL.
+     *
+     * @param text The text to write.
+     */
+    protected void writeln( String text )
+    {
+        write( text );
+        writeEOL();
+    }
+
+    /**
+     * Writes content, escaping special characters.
+     *
+     * @param text The text to write.
+     */
+    protected void content( String text )
+    {
+        write( escaped( text, verbatim ) );
+    }
+
+    /**
+     * Escapes special characters so that the text can be included in a fo file.
+     *
+     * @param text The text to process.
+     * @param verb In verbatim mode, white space and newlines are escaped.
+     * @return The text with special characters escaped.
+     */
+    public static String escaped( String text, boolean verb )
+    {
+        int length = text.length();
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = text.charAt( i );
+            switch ( c )
+            {
+                case ' ':
+                    if ( verb )
+                    {
+                        buffer.append( " " );
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+                    break;
+                case '<':
+                    buffer.append( "<" );
+                    break;
+                case '>':
+                    buffer.append( ">" );
+                    break;
+                case '&':
+                    buffer.append( "&" );
+                    break;
+                case '\n':
+                    buffer.append( EOL );
+                    if ( verb )
+                    {
+                        buffer.append( "<fo:block/>" + EOL );
+                    }
+                    break;
+                default:
+                    if ( needsSymbolFont( c ) )
+                    {
+                        // TODO: make font configurable?
+                        buffer.append( "<fo:inline font-family=\"Symbol\">" ).append( c ).append( "</fo:inline>" );
+                    }
+                    else
+                    {
+                        buffer.append( c );
+                    }
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    /** {@inheritDoc} */
+    protected void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
+    {
+        if ( this.tableCaptionXMLWriterStack.isEmpty() )
+        {
+            super.writeStartTag ( t, att, isSimpleTag );
+        }
+        else
+        {
+            String tag = ( getNameSpace() != null ? getNameSpace() + ":" : "" ) + t.toString();
+            ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() ).startElement( tag );
+
+            if ( att != null )
+            {
+                Enumeration names = att.getAttributeNames();
+                while ( names.hasMoreElements() )
+                {
+                    Object key = names.nextElement();
+                    Object value = att.getAttribute( key );
+
+                    ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() )
+                            .addAttribute( key.toString(), value.toString() );
+                }
+            }
+
+            if ( isSimpleTag )
+            {
+                ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() ).endElement();
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void writeEndTag( Tag t )
+    {
+        if ( this.tableCaptionXMLWriterStack.isEmpty() )
+        {
+            super.writeEndTag( t );
+        }
+        else
+        {
+            ( (PrettyPrintXMLWriter) this.tableCaptionXMLWriterStack.getLast() ).endElement();
+        }
+    }
+
+    private static final char UPPER_ALPHA = 0x391;
+    private static final char PIV = 0x3d6;
+    private static final char OLINE = 0x203e;
+    private static final char DIAMS = 0x2666;
+    private static final char EURO = 0x20ac;
+    private static final char TRADE = 0x2122;
+    private static final char PRIME = 0x2032;
+    private static final char PPRIME = 0x2033;
+
+    private static boolean needsSymbolFont( char c )
+    {
+        // greek characters and mathematical symbols, except the euro and trade symbols
+        // symbols I couldn't get to display in any font:
+        // zwnj (0x200C), zwj (0x200D), lrm (0x200E), rlm (0x200F), oline (0x203E),
+        // lceil (0x2038), rceil (0x2039), lfloor (0x203A), rfloor (0x203B)
+        return ( c >= UPPER_ALPHA && c <= PIV )
+                || ( c == PRIME || c == PPRIME )
+                || ( c >= OLINE && c <= DIAMS && c != EURO && c != TRADE );
+    }
+
+    /**
+     * Starts a page sequence.
+     *
+     * @param initPageNumber The initial page number. Should be either "0" (for the first page) or "auto".
+     * @param headerText The text to write in the header, if null, nothing is written.
+     * @param footerText The text to write in the footer, if null, nothing is written.
+     */
+    protected void startPageSequence( String initPageNumber, String headerText, String footerText )
+    {
+        writeln( "<fo:page-sequence initial-page-number=\"" + initPageNumber + "\" master-reference=\"body\">" );
+        regionBefore( headerText );
+        regionAfter( footerText );
+        writeln( "<fo:flow flow-name=\"xsl-region-body\">" );
+        chapterHeading( null, true );
+    }
+
+    /**
+     * Writes a 'xsl-region-before' block.
+     *
+     * @param headerText The text to write in the header, if null, nothing is written.
+     */
+    protected void regionBefore( String headerText )
+    {
+        // do nothing, overridden by AggregateSink
+    }
+
+    /**
+     * Writes a 'xsl-region-after' block. By default does nothing, gets overridden by AggregateSink.
+     *
+     * @param footerText The text to write in the footer, if null, nothing is written.
+     */
+    protected void regionAfter( String footerText )
+    {
+        // do nothing, overridden by AggregateSink
+    }
+
+    /**
+     * Writes a chapter heading. By default does nothing, gets overridden by AggregateSink.
+     *
+     * @param headerText The text to write in the header, if null, the current document title is written.
+     * @param chapterNumber True if the chapter number should be written in front of the text.
+     */
+    protected void chapterHeading( String headerText, boolean chapterNumber )
+    {
+        // do nothing, overridden by AggregateSink
+    }
+
+    /**
+     * Writes a fo:bookmark-tree. By default does nothing, gets overridden by AggregateSink.
+     */
+    protected void pdfBookmarks()
+    {
+        // do nothing, overridden by AggregateSink
+    }
+
+    /**
+     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
+     *
+     * @param key not null
+     * @param msg not null
+     * @see #close()
+     * @since 1.1.1
+     */
+    protected void logMessage( String key, String msg )
+    {
+        msg = "[FO Sink] " + msg;
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( msg );
+
+            return;
+        }
+
+        if ( warnMessages == null )
+        {
+            warnMessages = new HashMap();
+        }
+
+        Set set = (Set) warnMessages.get( key );
+        if ( set == null )
+        {
+            set = new TreeSet();
+        }
+        set.add( msg );
+        warnMessages.put( key, set );
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.listStack.clear();
+        this.tableGridStack.clear();
+        this.cellJustifStack.clear();
+        this.isCellJustifStack.clear();
+        this.cellCountStack.clear();
+        this.tableContentWriterStack.clear();
+        this.tableCaptionWriterStack.clear();
+        this.tableCaptionXMLWriterStack.clear();
+        this.tableCaptionStack.clear();
+
+        this.section = 0;
+        this.subsection = 0;
+        this.subsubsection = 0;
+        this.verbatim = false;
+        this.inFigure = false;
+        this.warnMessages = null;
+    }
+}
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoSinkFactory.java b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoSinkFactory.java
new file mode 100644
index 0000000..b62a46c
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoSinkFactory.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractXmlSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * FO implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: FoSinkFactory.java 739565 2009-01-31 14:39:03Z vsiveton $
+ * @since 1.1
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="fo"
+ */
+public class FoSinkFactory
+    extends AbstractXmlSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        return new FoSink( writer, encoding );
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding, String languageId )
+    {
+        return new FoSink( writer, encoding, languageId );
+    }
+}
diff --git a/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoUtils.java b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoUtils.java
new file mode 100644
index 0000000..0e7efc1
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/FoUtils.java
@@ -0,0 +1,230 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Date;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.maven.doxia.document.DocumentModel;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * <code>FO Sink</code> utilities.
+ *
+ * @author ltheussl
+ * @version $Id: FoUtils.java 785531 2009-06-17 09:47:59Z ltheussl $
+ * @since 1.1
+ */
+public class FoUtils
+{
+    /** To reuse the FopFactory **/
+    private static final FopFactory FOP_FACTORY = FopFactory.newInstance();
+
+    /** To reuse the TransformerFactory **/
+    private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
+
+    /**
+     * Converts an FO file to a PDF file using FOP.
+     *
+     * @param fo the FO file, not null.
+     * @param pdf the target PDF file, not null.
+     * @param resourceDir The base directory for relative path resolution, could be null.
+     * If null, defaults to the parent directory of fo.
+     * @param documentModel the document model to add PDF metadatas like author, title and keywords, could be null.
+     * @throws javax.xml.transform.TransformerException In case of a conversion problem.
+     * @since 1.1.1
+     */
+    public static void convertFO2PDF( File fo, File pdf, String resourceDir, DocumentModel documentModel )
+        throws TransformerException
+    {
+        FOUserAgent foUserAgent = getDefaultUserAgent( fo, resourceDir );
+
+        if ( documentModel != null && documentModel.getMeta() != null )
+        {
+            // http://xmlgraphics.apache.org/fop/embedding.html#user-agent
+            String authors = documentModel.getMeta().getAllAuthorNames();
+            if ( StringUtils.isNotEmpty( authors ) )
+            {
+                foUserAgent.setAuthor( authors );
+            }
+            if ( StringUtils.isNotEmpty( documentModel.getMeta().getTitle() ) )
+            {
+                foUserAgent.setTitle( documentModel.getMeta().getTitle() );
+            }
+            String keywords = documentModel.getMeta().getAllKeyWords();
+            if ( StringUtils.isNotEmpty( keywords ) )
+            {
+                foUserAgent.setKeywords( keywords );
+            }
+            if ( StringUtils.isNotEmpty( documentModel.getMeta().getCreator() ) )
+            {
+                foUserAgent.setCreator( documentModel.getMeta().getCreator() );
+            }
+            if ( StringUtils.isNotEmpty( documentModel.getMeta().getGenerator() ) )
+            {
+                foUserAgent.setProducer( documentModel.getMeta().getGenerator() );
+            }
+            if ( documentModel.getMeta().getCreationDate() != null )
+            {
+                foUserAgent.setCreationDate( documentModel.getMeta().getCreationDate() );
+            }
+        }
+
+        if ( foUserAgent.getCreator() == null )
+        {
+            foUserAgent.setCreator( System.getProperty( "user.name" ) );
+        }
+        if ( foUserAgent.getCreationDate() == null )
+        {
+            foUserAgent.setCreationDate( new Date() );
+        }
+
+        convertFO2PDF( fo, pdf, resourceDir, foUserAgent );
+    }
+
+    /**
+     * Converts an FO file to a PDF file using FOP.
+     *
+     * @param fo the FO file, not null.
+     * @param pdf the target PDF file, not null.
+     * @param resourceDir The base directory for relative path resolution, could be null.
+     * If null, defaults to the parent directory of fo.
+     * @param foUserAgent the FOUserAgent to use.
+     *      May be null, in which case a default user agent will be used.
+     * @throws javax.xml.transform.TransformerException In case of a conversion problem.
+     * @since 1.1.1
+     */
+    public static void convertFO2PDF( File fo, File pdf, String resourceDir, FOUserAgent foUserAgent )
+        throws TransformerException
+    {
+        FOUserAgent userAgent = ( foUserAgent == null ? getDefaultUserAgent( fo, resourceDir ) : foUserAgent );
+
+        OutputStream out = null;
+        try
+        {
+            try
+            {
+                out = new BufferedOutputStream( new FileOutputStream( pdf ) );
+            }
+            catch ( IOException e )
+            {
+                throw new TransformerException( e );
+            }
+
+            Result res = null;
+            try
+            {
+                Fop fop = FOP_FACTORY.newFop( MimeConstants.MIME_PDF, userAgent, out );
+                res = new SAXResult( fop.getDefaultHandler() );
+            }
+            catch ( FOPException e )
+            {
+                throw new TransformerException( e );
+            }
+
+            Transformer transformer = null;
+            try
+            {
+                // identity transformer
+                transformer = TRANSFORMER_FACTORY.newTransformer();
+            }
+            catch ( TransformerConfigurationException e )
+            {
+                throw new TransformerException( e );
+            }
+
+            transformer.transform( new StreamSource( fo ), res );
+        }
+        finally
+        {
+            IOUtil.close( out );
+        }
+    }
+
+    /**
+     * Converts an FO file to a PDF file using FOP.
+     *
+     * @param fo the FO file, not null.
+     * @param pdf the target PDF file, not null.
+     * @param resourceDir The base directory for relative path resolution, could be null.
+     * If null, defaults to the parent directory of fo.
+     * @throws javax.xml.transform.TransformerException In case of a conversion problem.
+     * @see #convertFO2PDF(File, File, String, DocumentModel)
+     */
+    public static void convertFO2PDF( File fo, File pdf, String resourceDir )
+        throws TransformerException
+    {
+        convertFO2PDF( fo, pdf, resourceDir, (DocumentModel) null );
+    }
+
+    /**
+     * Returns a base URL to be used by the FOUserAgent.
+     *
+     * @param fo the FO file.
+     * @param resourceDir the resource directory.
+     * @return String.
+     */
+    private static String getBaseURL( File fo, String resourceDir )
+    {
+        String url = null;
+
+        if ( resourceDir == null )
+        {
+            url = "file:///" + fo.getParent() + "/";
+        }
+        else
+        {
+            url = "file:///" + resourceDir + "/";
+        }
+
+        return url;
+    }
+
+    private static FOUserAgent getDefaultUserAgent( File fo, String resourceDir )
+    {
+        FOUserAgent foUserAgent = FOP_FACTORY.newFOUserAgent();
+        foUserAgent.setBaseURL( getBaseURL( fo, resourceDir ) );
+
+        return foUserAgent;
+    }
+
+    private FoUtils()
+    {
+        // Utility class
+    }
+}
diff --git a/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/NumberedListItem.java b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/NumberedListItem.java
new file mode 100644
index 0000000..3a13f48
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/java/org/apache/maven/doxia/module/fo/NumberedListItem.java
@@ -0,0 +1,183 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Used to count the position in a numbered list.
+ *
+ * @author ltheussl
+ * @version $Id: NumberedListItem.java 946933 2010-05-21 08:39:07Z ltheussl $
+ * @since 1.1
+ */
+public class NumberedListItem
+{
+
+    /** Arabic decimals from 1 - 26. */
+    private static final String[] DECIMALS =
+    {
+        "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
+        "11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
+        "21", "22", "23", "24", "25", "26"
+    };
+
+    /** Lower-case alphanumerics from a - z. */
+    private static final String[] LOWER_ALPHAS =
+    {
+        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
+        "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
+        "u", "v", "w", "x", "y", "z"
+    };
+
+    /** Upper-case alphanumerics from A - Z. */
+    private static final String[] UPPER_ALPHAS =
+    {
+        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
+        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
+        "U", "V", "W", "X", "Y", "Z"
+    };
+
+    /** Lower-case roman numbers from i - xxvi. */
+    private static final String[] LOWER_ROMANS =
+    {
+        "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x",
+        "xi", "xii", "xiii", "xiv", "xv", "xvi", "xvii", "xviii", "xix", "xx",
+        "xxi", "xxii", "xxiii", "xxiv", "xxv", "xxvi"
+    };
+
+    /** Upper-case roman numbers from I - XXVI. */
+    private static final String[] UPPER_ROMANS =
+    {
+        "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
+        "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX",
+        "XXI", "XXII", "XXIII", "XXIV", "XXV", "XXVI"
+    };
+
+    /** The position in the list. */
+    private int count;
+
+    /** The numbering format. */
+    private final int format;
+
+    /**
+     * Constructor. Initializes count and format.
+     *
+     * @param itemFormat The numbering format of this List.
+     * Should be one of the formats defined in {@link org.apache.maven.doxia.sink.Sink}.
+     */
+    public NumberedListItem( int itemFormat )
+    {
+        if ( !isValidItemFormat( itemFormat ) )
+        {
+            throw new IllegalArgumentException( "Unknown item format!" );
+        }
+
+        this.format = itemFormat;
+        this.count = 0;
+    }
+
+    /**
+     * Returns the current count, ie the position in the list.
+     *
+     * @return The current count.
+     */
+    public int count()
+    {
+        return count;
+    }
+
+    /**
+     * Returns the numbering format.
+     *
+     * @return The numbering format.
+     */
+    public int format()
+    {
+        return format;
+    }
+
+    /**
+     * Increase the current count by 1.
+     */
+    public void next()
+    {
+        count++;
+    }
+
+    /**
+     * Returns the symbol for the current list item.
+     *
+     * @return The symbol for the current list item.
+     */
+    public String getListItemSymbol()
+    {
+        int j = count() - 1;
+
+        if ( j < 0 )
+        {
+            j = 0;
+        }
+        else if ( j > DECIMALS.length - 1 )
+        {
+            j = DECIMALS.length - 1;
+        }
+
+        String symbol;
+
+        switch ( format() )
+        {
+            case Sink.NUMBERING_UPPER_ALPHA:
+                symbol = UPPER_ALPHAS[j];
+                break;
+            case Sink.NUMBERING_LOWER_ALPHA:
+                symbol = LOWER_ALPHAS[j];
+                break;
+            case Sink.NUMBERING_UPPER_ROMAN:
+                symbol = UPPER_ROMANS[j];
+                break;
+            case Sink.NUMBERING_LOWER_ROMAN:
+                symbol = LOWER_ROMANS[j];
+                break;
+            case Sink.NUMBERING_DECIMAL:
+            default:
+                symbol = DECIMALS[j];
+        }
+
+        return symbol + ".";
+    }
+
+    /**
+     * Determines if the given format is one of the formats defined in
+     * {@link org.apache.maven.doxia.sink.Sink}.
+     *
+     * @param itemFormat the format to check.
+     * @return True if the format is a valid item format according to the Sink API.
+     */
+    private boolean isValidItemFormat( int itemFormat )
+    {
+        return ( ( itemFormat == Sink.NUMBERING_UPPER_ALPHA )
+            || ( itemFormat == Sink.NUMBERING_LOWER_ALPHA )
+            || ( itemFormat == Sink.NUMBERING_UPPER_ROMAN )
+            || ( itemFormat == Sink.NUMBERING_LOWER_ROMAN )
+            || ( itemFormat == Sink.NUMBERING_DECIMAL ) );
+    }
+
+}
diff --git a/doxia-modules/doxia-module-fo/src/main/resources/doxia-fo.properties b/doxia-modules/doxia-module-fo/src/main/resources/doxia-fo.properties
new file mode 100644
index 0000000..e69349d
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/resources/doxia-fo.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+footer.rights=ALL RIGHTS RESERVED.
+footer.confidential=PROPRIETARY AND CONFIDENTIAL
diff --git a/doxia-modules/doxia-module-fo/src/main/resources/doxia-fo_en.properties b/doxia-modules/doxia-module-fo/src/main/resources/doxia-fo_en.properties
new file mode 100644
index 0000000..96cf407
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/resources/doxia-fo_en.properties
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# NOTE:
+# This bundle is intentionally empty because English strings are provided by the base bundle via the parent chain. It
+# must be provided nevertheless such that a request for locale "en" will not errorneously pick up the bundle for the
+# JVM's default locale (which need not be "en"). See the method javadoc about
+#   ResourceBundle.getBundle(String, Locale, ClassLoader)
+# for a full description of the lookup strategy.
diff --git a/doxia-modules/doxia-module-fo/src/main/resources/fo-styles.xslt b/doxia-modules/doxia-module-fo/src/main/resources/fo-styles.xslt
new file mode 100644
index 0000000..fd77c16
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/resources/fo-styles.xslt
@@ -0,0 +1,509 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<!-- These values are optimized for an A4 paper size. -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
+
+    <!-- Layout master sets -->
+    <xsl:attribute-set name="layout.master.set.base">
+      <xsl:attribute name="page-width">8.27in</xsl:attribute>
+      <xsl:attribute name="page-height">11.70in</xsl:attribute>
+        <xsl:attribute name="margin-top">0.625in</xsl:attribute>
+        <xsl:attribute name="margin-bottom">0.6in</xsl:attribute>
+        <xsl:attribute name="margin-left">1in</xsl:attribute>
+        <xsl:attribute name="margin-right">1in</xsl:attribute>
+    </xsl:attribute-set>
+
+    <xsl:attribute-set name="layout.master.set.cover-page" use-attribute-sets="layout.master.set.base">
+        <xsl:attribute name="master-name">cover-page</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="layout.master.set.cover-page.region-body">
+        <xsl:attribute name="margin-top">0.7in</xsl:attribute>
+    </xsl:attribute-set>
+
+    <xsl:attribute-set name="layout.master.set.cover-inside" use-attribute-sets="layout.master.set.base">
+        <xsl:attribute name="master-name">cover-inside</xsl:attribute>
+        <xsl:attribute name="margin-top">0in</xsl:attribute>
+        <xsl:attribute name="margin-bottom">0in</xsl:attribute>
+        <xsl:attribute name="margin-left">0in</xsl:attribute>
+        <xsl:attribute name="margin-right">0in</xsl:attribute>
+    </xsl:attribute-set>
+
+    <xsl:attribute-set name="layout.master.set.toc" use-attribute-sets="layout.master.set.base">
+        <xsl:attribute name="master-name">toc</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="layout.master.set.toc.region-body">
+        <xsl:attribute name="margin-top">0.7in</xsl:attribute>
+        <xsl:attribute name="margin-bottom">0.8in</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="layout.master.set.toc.region-before">
+        <xsl:attribute name="extent">0.35in</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="layout.master.set.toc.region-after">
+        <xsl:attribute name="extent">0.125in</xsl:attribute>
+    </xsl:attribute-set>
+
+    <xsl:attribute-set name="layout.master.set.body" use-attribute-sets="layout.master.set.base">
+        <xsl:attribute name="master-name">body</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="layout.master.set.body.region-body">
+        <xsl:attribute name="margin-top">0.7in</xsl:attribute>
+        <xsl:attribute name="margin-bottom">0.8in</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="layout.master.set.body.region-before">
+        <xsl:attribute name="extent">0.35in</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="layout.master.set.body.region-after">
+        <xsl:attribute name="extent">0.125in</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Style 'primitives' from which all others are descended -->
+    <xsl:attribute-set name="base.body.style">
+        <xsl:attribute name="font-family">Garamond,serif</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="base.heading.style">
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="color">#000000</xsl:attribute>
+        <xsl:attribute name="keep-with-next">always</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="base.pre.style">
+        <xsl:attribute name="font-family">monospace</xsl:attribute>
+    </xsl:attribute-set>
+
+    <xsl:attribute-set name="italic">
+        <xsl:attribute name="font-style">italic</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="bold">
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="monospace">
+        <xsl:attribute name="font-family">monospace</xsl:attribute>
+        <xsl:attribute name="font-size">10pt</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Standard body and heading styles -->
+    <xsl:attribute-set name="body.text" use-attribute-sets="base.body.style">
+        <xsl:attribute name="font-size">11pt</xsl:attribute>
+        <xsl:attribute name="line-height">12pt</xsl:attribute>
+        <xsl:attribute name="white-space-collapse">true</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.pre" use-attribute-sets="base.pre.style">
+        <xsl:attribute name="font-size">10pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="code.indent" use-attribute-sets="body.pre">
+        <xsl:attribute name="start-indent">inherited-property-value(start-indent) + 1em</xsl:attribute>
+        <xsl:attribute name="end-indent">inherited-property-value(end-indent) + 1em</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.source" use-attribute-sets="body.pre">
+        <xsl:attribute name="wrap-option">no-wrap</xsl:attribute>
+        <xsl:attribute name="keep-together">always</xsl:attribute>
+        <xsl:attribute name="white-space-collapse">false</xsl:attribute>
+        <xsl:attribute name="color">black</xsl:attribute>
+        <xsl:attribute name="border-style">solid</xsl:attribute>
+        <xsl:attribute name="border-width">0.5pt</xsl:attribute>
+        <xsl:attribute name="border-color">#454545</xsl:attribute>
+        <xsl:attribute name="padding-before">0.25em</xsl:attribute>
+        <xsl:attribute name="padding-after">0.25em</xsl:attribute>
+        <xsl:attribute name="padding-start">0.25em</xsl:attribute>
+        <xsl:attribute name="padding-end">0.25em</xsl:attribute>
+        <xsl:attribute name="start-indent">inherited-property-value(start-indent) + 2.5em</xsl:attribute>
+        <xsl:attribute name="end-indent">inherited-property-value(end-indent) + 3em</xsl:attribute>
+        <xsl:attribute name="space-before">0.75em</xsl:attribute>
+        <xsl:attribute name="space-after">1em</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.rule">
+        <xsl:attribute name="leader-length.optimum">100%</xsl:attribute>
+        <xsl:attribute name="leader-pattern">rule</xsl:attribute>
+        <xsl:attribute name="rule-thickness">0.5pt</xsl:attribute>
+        <xsl:attribute name="color">black</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.strong">
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="font-size">9.0pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.title" use-attribute-sets="base.heading.style">
+        <xsl:attribute name="font-size">16pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.h1" use-attribute-sets="base.heading.style">
+        <xsl:attribute name="font-size">12pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+        <xsl:attribute name="space-before">18pt</xsl:attribute>
+        <xsl:attribute name="space-after">6pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.h2" use-attribute-sets="base.heading.style">
+        <xsl:attribute name="font-size">9.5pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+        <xsl:attribute name="space-before">18pt</xsl:attribute>
+        <xsl:attribute name="space-after">5pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.h3" use-attribute-sets="base.heading.style">
+        <xsl:attribute name="font-size">9.5pt</xsl:attribute>
+        <xsl:attribute name="space-before">15pt</xsl:attribute>
+        <xsl:attribute name="space-after">3pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.h4" use-attribute-sets="base.heading.style">
+        <xsl:attribute name="font-size">9.5pt</xsl:attribute>
+        <xsl:attribute name="space-before">9pt</xsl:attribute>
+        <xsl:attribute name="space-after">3pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="body.h5" use-attribute-sets="base.heading.style">
+        <xsl:attribute name="font-size">9.5pt</xsl:attribute>
+        <xsl:attribute name="font-style">italic</xsl:attribute>
+        <xsl:attribute name="space-after">3pt</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Chapter heading styles -->
+    <xsl:attribute-set name="chapter.title" use-attribute-sets="body.title">
+        <xsl:attribute name="line-height">10pt</xsl:attribute>
+        <xsl:attribute name="space-after">6pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="chapter.rule">
+        <xsl:attribute name="leader-length.optimum">100%</xsl:attribute>
+        <xsl:attribute name="leader-pattern">dots</xsl:attribute>
+        <xsl:attribute name="rule-thickness">1pt</xsl:attribute>
+        <xsl:attribute name="color">#454545</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Outdented numbers -->
+    <xsl:attribute-set name="outdented.number.style" use-attribute-sets="base.heading.style">
+        <xsl:attribute name="font-size">9.5pt</xsl:attribute>
+        <!--<xsl:attribute name="color">#454545</xsl:attribute>-->
+        <xsl:attribute name="line-height">10pt</xsl:attribute>
+        <xsl:attribute name="text-align">right</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Page header/footer styles -->
+    <xsl:attribute-set name="footer.style">
+        <xsl:attribute name="letter-spacing">2pt</xsl:attribute>
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="font-size">6pt</xsl:attribute>
+        <xsl:attribute name="color">#454545</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="header.style">
+        <xsl:attribute name="letter-spacing">2pt</xsl:attribute>
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="font-size">6pt</xsl:attribute>
+        <xsl:attribute name="color">#454545</xsl:attribute>
+        <xsl:attribute name="text-align">left</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="page.number">
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="font-size">9.5pt</xsl:attribute>
+        <xsl:attribute name="text-align">right</xsl:attribute>
+        <xsl:attribute name="color">black</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Style for hyperlinks -->
+    <xsl:attribute-set name="href.internal">
+        <xsl:attribute name="color">blue</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="href.external">
+        <xsl:attribute name="color">green</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- 'Normal' line-spacing styles for paragraph and pre elements -->
+    <xsl:attribute-set name="normal.paragraph" use-attribute-sets="body.text">
+        <xsl:attribute name="space-before">3pt</xsl:attribute>
+        <xsl:attribute name="space-after">6pt</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Styles for lists, list items, definition lists, etc.  -->
+    <xsl:attribute-set name="list">
+        <xsl:attribute name="start-indent">inherited-property-value(start-indent)</xsl:attribute>
+        <xsl:attribute name="space-before.optimum">10pt</xsl:attribute>
+        <xsl:attribute name="provisional-distance-between-starts">1em</xsl:attribute>
+        <xsl:attribute name="provisional-label-separation">1em</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="list.item">
+        <xsl:attribute name="start-indent">inherited-property-value(start-indent) + 1em</xsl:attribute>
+        <xsl:attribute name="space-before">0.15em</xsl:attribute>
+        <xsl:attribute name="space-after">0.25em</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="dl" use-attribute-sets="body.text">
+        <xsl:attribute name="start-indent">1em</xsl:attribute>
+        <xsl:attribute name="end-indent">1em</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="dt" use-attribute-sets="base.body.style">
+        <xsl:attribute name="start-indent">1em</xsl:attribute>
+        <xsl:attribute name="end-indent">1em</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="dd" use-attribute-sets="base.body.style">
+        <xsl:attribute name="start-indent">inherited-property-value(start-indent) + 1em</xsl:attribute>
+        <xsl:attribute name="end-indent">inherited-property-value(end-indent) + 1em</xsl:attribute>
+        <xsl:attribute name="space-before">0.6em</xsl:attribute>
+        <xsl:attribute name="space-after">0.6em</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Error style -->
+    <xsl:attribute-set name="error.block" use-attribute-sets="base.block">
+        <xsl:attribute name="font-size">8pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+        <xsl:attribute name="color">red</xsl:attribute>
+        <xsl:attribute name="border-style">solid</xsl:attribute>
+        <xsl:attribute name="border-width">0.5pt</xsl:attribute>
+        <xsl:attribute name="border-color">red</xsl:attribute>
+        <xsl:attribute name="padding">0.75em</xsl:attribute>
+        <xsl:attribute name="start-indent">inherited-property-value(start-indent) + 2.5em</xsl:attribute>
+        <xsl:attribute name="end-indent">inherited-property-value(end-indent) + 3em</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- cover styles -->
+    <xsl:attribute-set name="cover.title">
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="color">#000000</xsl:attribute>
+        <xsl:attribute name="keep-with-next">always</xsl:attribute>
+        <xsl:attribute name="font-size">16pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+        <xsl:attribute name="text-align">left</xsl:attribute>
+        <xsl:attribute name="display-align">center</xsl:attribute>
+        <xsl:attribute name="space-after">0.5in</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="cover.subtitle">
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="color">#000000</xsl:attribute>
+        <xsl:attribute name="keep-with-next">always</xsl:attribute>
+        <xsl:attribute name="font-size">12pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+        <xsl:attribute name="text-align">left</xsl:attribute>
+        <xsl:attribute name="display-align">center</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="cover.border.left" use-attribute-sets="base.pre.style">
+        <xsl:attribute name="padding-start">0.2in</xsl:attribute>
+        <xsl:attribute name="border-left-style">dotted</xsl:attribute>
+        <xsl:attribute name="border-left-width">0.1pt</xsl:attribute>
+        <xsl:attribute name="border-left-color">#000000</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="cover.border.left.bottom" use-attribute-sets="cover.border.left">
+        <xsl:attribute name="padding-after">0.2in</xsl:attribute>
+        <xsl:attribute name="border-bottom-style">dotted</xsl:attribute>
+        <xsl:attribute name="border-bottom-width">0.1pt</xsl:attribute>
+        <xsl:attribute name="border-bottom-color">#000000</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Header styles (ie title, author, date in single document mode) -->
+    <xsl:attribute-set name="doc.header.title" use-attribute-sets="base.body.style">
+        <xsl:attribute name="text-align">center</xsl:attribute>
+        <xsl:attribute name="font-size">16pt</xsl:attribute>
+        <xsl:attribute name="space-before.optimum">30pt</xsl:attribute>
+        <xsl:attribute name="space-after.optimum">14pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="doc.header.author" use-attribute-sets="base.body.style">
+        <xsl:attribute name="text-align">center</xsl:attribute>
+        <xsl:attribute name="font-size">12pt</xsl:attribute>
+        <xsl:attribute name="space-before.optimum">20pt</xsl:attribute>
+        <xsl:attribute name="space-after.optimum">14pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="doc.header.date" use-attribute-sets="base.body.style">
+        <xsl:attribute name="text-align">center</xsl:attribute>
+        <xsl:attribute name="font-size">12pt</xsl:attribute>
+        <xsl:attribute name="space-before.optimum">20pt</xsl:attribute>
+        <xsl:attribute name="space-after.optimum">30pt</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Figure styles -->
+    <xsl:attribute-set name="figure.display">
+        <xsl:attribute name="display-align">center</xsl:attribute>
+        <xsl:attribute name="text-align">center</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="figure.graphics">
+        <xsl:attribute name="height">auto</xsl:attribute>
+        <xsl:attribute name="width">auto</xsl:attribute>
+        <xsl:attribute name="content-height">auto</xsl:attribute>
+        <xsl:attribute name="content-width">auto</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="figure.caption" use-attribute-sets="base.body.style">
+        <xsl:attribute name="keep-with-previous">always</xsl:attribute>
+        <xsl:attribute name="text-align">center</xsl:attribute>
+        <xsl:attribute name="font-size">10pt</xsl:attribute>
+        <xsl:attribute name="font-style">italic</xsl:attribute>
+        <xsl:attribute name="space-before.optimum">20pt</xsl:attribute>
+        <xsl:attribute name="space-after.optimum">30pt</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Tables styles -->
+    <xsl:attribute-set name="table.layout">
+        <xsl:attribute name="table-omit-footer-at-break">false</xsl:attribute>
+        <!-- note that table-layout="auto" is not supported by FOP 0.93 -->
+        <xsl:attribute name="table-layout">fixed</xsl:attribute>
+        <xsl:attribute name="width">100%</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.heading.rule">
+        <xsl:attribute name="leader-length.optimum">100%</xsl:attribute>
+        <xsl:attribute name="leader-pattern">rule</xsl:attribute>
+        <xsl:attribute name="rule-thickness">0.5pt</xsl:attribute>
+        <xsl:attribute name="color">black</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="base.cell">
+        <xsl:attribute name="padding-start">2.5pt</xsl:attribute>
+        <xsl:attribute name="padding-end">5pt</xsl:attribute>
+        <!-- http://xmlgraphics.apache.org/fop/faq.html#keep-together -->
+        <xsl:attribute name="keep-together.within-column">always</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="base.block">
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="line-height">1.2em</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.padding">
+        <xsl:attribute name="padding-before">9pt</xsl:attribute>
+        <xsl:attribute name="padding-after">12pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.title.row">
+        <xsl:attribute name="keep-together">always</xsl:attribute>
+        <xsl:attribute name="keep-with-next">always</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.title.cell" use-attribute-sets="base.cell">
+        <xsl:attribute name="border-after-style">solid</xsl:attribute>
+        <xsl:attribute name="border-after-width">0.5pt</xsl:attribute>
+        <xsl:attribute name="border-after-color">black</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.title.block" use-attribute-sets="base.block">
+        <xsl:attribute name="font-size">11pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.footer.cell" use-attribute-sets="base.cell">
+        <xsl:attribute name="padding-before">5pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.footer.block" use-attribute-sets="base.block">
+        <xsl:attribute name="font-size">9pt</xsl:attribute>
+        <xsl:attribute name="font-style">italic</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.heading.cell" use-attribute-sets="base.cell">
+        <xsl:attribute name="padding-before">7pt</xsl:attribute>
+        <xsl:attribute name="display-align">after</xsl:attribute>
+        <xsl:attribute name="background-color">#bbbbbb</xsl:attribute>
+        <xsl:attribute name="color">white</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.heading.block" use-attribute-sets="base.block">
+        <xsl:attribute name="font-size">10pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.heading.rule">
+        <xsl:attribute name="leader-length.optimum">100%</xsl:attribute>
+        <xsl:attribute name="leader-pattern">rule</xsl:attribute>
+        <xsl:attribute name="rule-thickness">0.5pt</xsl:attribute>
+        <xsl:attribute name="color">black</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.number.cell" use-attribute-sets="base.cell">
+        <xsl:attribute name="padding-before">6pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.number.block" use-attribute-sets="base.block">
+        <xsl:attribute name="font-size">9pt</xsl:attribute>
+        <xsl:attribute name="font-style">italic</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.subheading.row">
+        <xsl:attribute name="keep-together">always</xsl:attribute>
+        <xsl:attribute name="keep-with-next">always</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.subheading.cell" use-attribute-sets="base.cell">
+        <xsl:attribute name="padding-after">1pt</xsl:attribute>
+        <xsl:attribute name="background-color">#D3D3D3</xsl:attribute>
+        <xsl:attribute name="border-before-style">solid</xsl:attribute>
+        <xsl:attribute name="border-before-width">2.5pt</xsl:attribute>
+        <xsl:attribute name="border-before-color">#D3D3D3</xsl:attribute>
+        <xsl:attribute name="display-align">after</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.subheading.block" use-attribute-sets="base.block">
+        <xsl:attribute name="font-size">9pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+        <xsl:attribute name="vertical-align">bottom</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.body.row">
+        <xsl:attribute name="keep-together">auto</xsl:attribute>
+        <xsl:attribute name="keep-with-next">auto</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.body.norule">
+        <xsl:attribute name="leader-length.optimum">100%</xsl:attribute>
+        <xsl:attribute name="leader-pattern">rule</xsl:attribute>
+        <xsl:attribute name="rule-thickness">1pt</xsl:attribute>
+        <xsl:attribute name="color">white</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.body.rule">
+        <xsl:attribute name="leader-length.optimum">100%</xsl:attribute>
+        <xsl:attribute name="leader-pattern">dots</xsl:attribute>
+        <xsl:attribute name="rule-thickness">0.5pt</xsl:attribute>
+        <xsl:attribute name="color">#A9A9A9</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.body.lastrule">
+        <xsl:attribute name="leader-length.optimum">100%</xsl:attribute>
+        <xsl:attribute name="leader-pattern">rule</xsl:attribute>
+        <xsl:attribute name="rule-thickness">0.5pt</xsl:attribute>
+        <xsl:attribute name="color">black</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.body.cell.grid">
+        <xsl:attribute name="border-style">solid</xsl:attribute>
+        <xsl:attribute name="border-width">0.2mm</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.body.cell" use-attribute-sets="base.cell">
+        <xsl:attribute name="padding-before">4pt</xsl:attribute>
+        <xsl:attribute name="padding-after">1.5pt</xsl:attribute>
+        <xsl:attribute name="background-color">#eeeeee</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.body.block" use-attribute-sets="base.block">
+        <xsl:attribute name="font-size">9pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="table.pre" use-attribute-sets="base.pre.style">
+        <xsl:attribute name="font-size">9pt</xsl:attribute>
+    </xsl:attribute-set>
+
+    <!-- Table of content styles -->
+    <xsl:attribute-set name="toc.cell">
+        <xsl:attribute name="display-align">after</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="toc.base.style">
+        <xsl:attribute name="font-family">Helvetica,sans-serif</xsl:attribute>
+        <xsl:attribute name="line-height">16pt</xsl:attribute>
+        <!-- <xsl:attribute name="text-align-last">start</xsl:attribute>-->
+        <xsl:attribute name="text-align-last">justify</xsl:attribute>
+        <xsl:attribute name="wrap-option">no-wrap</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="toc.leader.style">
+        <xsl:attribute name="leader-pattern">dots</xsl:attribute>
+        <xsl:attribute name="leader-pattern-width">5pt</xsl:attribute>
+        <xsl:attribute name="color">#454545</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="toc.number.style">
+        <xsl:attribute name="font-size">9.5pt</xsl:attribute>
+        <xsl:attribute name="text-align">end</xsl:attribute>
+        <xsl:attribute name="color">#A9A9A9</xsl:attribute>
+        <xsl:attribute name="line-height">16pt</xsl:attribute>
+        <xsl:attribute name="end-indent">6pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="toc.h1.style" use-attribute-sets="toc.base.style">
+        <xsl:attribute name="font-size">12pt</xsl:attribute>
+        <xsl:attribute name="font-weight">bold</xsl:attribute>
+        <xsl:attribute name="space-before">18pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="toc.h2.style" use-attribute-sets="toc.base.style">
+        <xsl:attribute name="font-size">11pt</xsl:attribute>
+        <xsl:attribute name="space-before">15pt</xsl:attribute>
+        <xsl:attribute name="space-before">3pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="toc.h3.style" use-attribute-sets="toc.base.style">
+        <xsl:attribute name="font-size">10pt</xsl:attribute>
+        <xsl:attribute name="space-before">4pt</xsl:attribute>
+    </xsl:attribute-set>
+    <xsl:attribute-set name="toc.h4.style" use-attribute-sets="toc.base.style">
+        <xsl:attribute name="font-size">9.5pt</xsl:attribute>
+        <xsl:attribute name="space-before">4pt</xsl:attribute>
+    </xsl:attribute-set>
+</xsl:stylesheet>
diff --git a/doxia-modules/doxia-module-fo/src/main/resources/log4j.properties b/doxia-modules/doxia-module-fo/src/main/resources/log4j.properties
new file mode 100644
index 0000000..96a29e0
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/main/resources/log4j.properties
@@ -0,0 +1,24 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+log4j.rootCategory=INFO, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%5p [%c:%L] %d{ISO8601} - %m%n
+
+log4j.logger.org.apache.fop=ERROR
diff --git a/doxia-modules/doxia-module-fo/src/site/site.xml b/doxia-modules/doxia-module-fo/src/site/site.xml
new file mode 100644
index 0000000..bb7f21b
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/site/site.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project name="Doxia :: FO Module">
+  <bannerLeft>
+    <name>Doxia :: FO Module</name>
+    <src>http://maven.apache.org/images/apache-maven-project-2.png</src>
+    <href>http://maven.apache.org/doxia/</href>
+  </bannerLeft>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu name="Doxia :: FO Module">
+      <item name="Overview" href="index.html"/>
+      <item name="Usage" href="usage.html"/>
+      <item name="Links" href="links.html"/>
+    </menu>
+
+    <menu ref="reports"/>
+
+  </body>
+</project>
diff --git a/doxia-modules/doxia-module-fo/src/site/xdoc/index.xml b/doxia-modules/doxia-module-fo/src/site/xdoc/index.xml
new file mode 100644
index 0000000..cf894ee
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/site/xdoc/index.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document>
+  <properties>
+    <title>Doxia FO Module - Index</title>
+    <author email="ltheussl at apache.org">Lukas Theussl</author>
+  </properties>
+
+  <body>
+    <section name="Doxia FO Module">
+
+      <p>
+        This is a <a href="http://maven.apache.org/doxia/">Doxia</a> module
+        for <a href="http://www.w3.org/TR/xsl/">XSL Formatting Objects</a>.
+      </p>
+      <p>
+        Currently only a sink is available, ie you can generate FO files
+        either programmatically or by converting from any other
+        <a href="http://maven.apache.org/doxia/references/index.html">input format</a>
+        supported by Doxia, but you cannot parse them.
+      </p>
+      <p>
+        The generated FO files can then be converted into other formats,
+        eg pdf, using <a href="http://xmlgraphics.apache.org/fop/">Apache Fop</a>.
+        Check out the <a href="usage.html">usage</a> examples.
+      </p>
+
+      <subsection name="Current limitations">
+        <ul>
+          <li>Table captions not implemented</li>
+          <li>Table width is fixed and column widths are uniformly distributed</li>
+        </ul>
+      </subsection>
+
+    </section>
+
+  </body>
+</document>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-fo/src/site/xdoc/links.xml b/doxia-modules/doxia-module-fo/src/site/xdoc/links.xml
new file mode 100644
index 0000000..980d4fd
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/site/xdoc/links.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document>
+  <properties>
+    <title>Doxia FO Module - Links</title>
+    <author email="ltheussl at apache.org">Lukas Theussl</author>
+  </properties>
+
+  <body>
+    <section name="Useful links">
+
+      <ul>
+        <li><a href="http://xmlgraphics.apache.org/fop/">Apache FOP</a></li>
+        <li><a href="http://www.w3.org/TR/xsl/">W3C XSL Recommendation</a></li>
+        <li><a href="http://www.w3.org/TR/xslt">W3C XSLT Recommendation</a></li>
+        <li><a href="http://www.adobe.com/devnet/pdf/pdf_reference.html">PDF Reference</a></li>
+        <li><a href="http://www.w3schools.com/xslfo/default.asp">XSL-FO Tutorial</a></li>
+        <li><a href="http://www.xmlmind.com/foconverter/">XMLmind FO Converter</a></li>
+      </ul>
+
+    </section>
+
+  </body>
+</document>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-fo/src/site/xdoc/usage.xml b/doxia-modules/doxia-module-fo/src/site/xdoc/usage.xml
new file mode 100644
index 0000000..0ad3fef
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/site/xdoc/usage.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document>
+  <properties>
+    <title>Doxia FO Module - Usage</title>
+    <author email="ltheussl at apache.org">Lukas Theussl</author>
+  </properties>
+
+  <body>
+    <section name="Usage">
+
+    <macro name="toc">
+      <param name="section" value="1"/>
+      <param name="fromDepth" value="1"/>
+      <param name="toDepth" value="1"/>
+    </macro>
+
+    <subsection name="Converting single documents">
+
+      <p>
+        Here's an example that converts a single apt input file into a fo document.
+        To produce a pdf from the result using Apache FOP, see eg
+        <a href="http://xmlgraphics.apache.org/fop/0.93/embedding.html#ExampleFO2PDF">ExampleFO2PDF</a>.
+      </p>
+
+      <source>
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.maven.doxia.module.apt.AptParser;
+import org.apache.maven.doxia.module.fo.FoSink;
+import org.apache.maven.doxia.parser.ParseException;
+
+
+public class Apt2FO
+{
+    public static void main(String[] args)
+    {
+        try
+        {
+            // Open input apt document:
+            FileReader source = new FileReader(
+                new File( "resources", "test.apt" ) );
+
+            // Create FO sink:
+            FoSink fosink = new FoSink( new FileWriter(
+                new File( "output", "test.fo" ) ) );
+
+            AptParser parser = new AptParser();
+            parser.parse( source, fosink );
+
+            // close streams
+            fosink.close();
+            source.close();
+        }
+        catch ( ParseException e )
+        {
+            e.printStackTrace();
+        }
+        catch ( FileNotFoundException e )
+        {
+            e.printStackTrace();
+        }
+        catch ( IOException e )
+        {
+            e.printStackTrace();
+        }
+    }
+}</source>
+
+    </subsection>
+
+    <subsection name="Converting multiple documents">
+
+      <p>
+        If you want to parse several source documents into a single fo file,
+        so you can generate a single pdf from multiple source files, you should
+        use the FoAggregateSink. A simple example is outlined below,
+        refer to the <a href="apidocs/index.html">API docs</a> for more information.
+      </p>
+
+      <source>
+    AptParser parser = new AptParser();
+
+    FoAggregateSink fosink = new FoAggregateSink(
+        new FileWriter( new File( "out", "aggregate.fo" ) ) );
+
+    // getDocumentModel() should return a DocumentModel object
+    fosink.setDocumentModel( getDocumentModel()  );
+
+    fosink.beginDocument();
+
+    fosink.coverPage();
+
+    fosink.toc();
+
+    // first document
+    FileReader source1 =
+        new FileReader( new File( "resources", "test1.apt" ) );
+    fosink.setDocumentName( "doc1" );
+    fosink.setDocumentTitle( "Document 1" );
+    parser.parse( source1, fosink );
+
+    // second document
+    FileReader source2 =
+        new FileReader( new File( "resources", "test2.apt" ) );
+    fosink.setDocumentName( "doc2" );
+    fosink.setDocumentTitle( "Document 2" );
+    parser.parse( source2, fosink );
+
+    fosink.endDocument();</source>
+
+    </subsection>
+
+    <subsection name="Embedded use">
+
+      <p>
+        To compile and run the following example,
+        you need the following jars on your classpath:
+        <code>doxia-core-1.1.1</code>, <code>doxia-module-fo-1.1.1</code>,
+        <code>doxia-sink-api-1.1.1</code>, <code>doxia-logging-api-1.1.1</code>,
+        <code>commons-configuration-1.4</code>, <code>commons-lang-2.4</code>,
+        <code>plexus-utils-1.5.8</code>, <code>commons-collections-3.2</code>,
+        <code>commons-logging-1.1.1</code>.
+      </p>
+
+      <source>import java.io.File;
+import java.io.IOException;
+
+import org.apache.maven.doxia.module.fo.FoSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+public class TestPDF
+{
+    public static void main( String[] args )
+    {
+        Sink sink = null;
+
+        try
+        {
+            sink = new FoSinkFactory().createSink( new File( "." ), "test.fo" );
+            populateSink( sink );
+        }
+        catch ( IOException ex )
+        {
+            ex.printStackTrace();
+        }
+        finally
+        {
+            sink.close();
+        }
+    }
+
+    private static void populateSink( Sink sink )
+    {
+        sink.head();
+        sink.title();
+        sink.text( "Title" );
+        sink.title_();
+        sink.author();
+        sink.text( "Author" );
+        sink.author_();
+        sink.date();
+        sink.text( "Date" );
+        sink.date_();
+        sink.head_();
+        sink.body();
+        sink.paragraph();
+        sink.text( "Hello world!" );
+        sink.paragraph_();
+        sink.body_();
+    }
+}</source>
+    </subsection>
+
+
+    </section>
+
+  </body>
+</document>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoAggregateSinkTest.java b/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoAggregateSinkTest.java
new file mode 100644
index 0000000..bc9d33c
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoAggregateSinkTest.java
@@ -0,0 +1,204 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringWriter;
+import java.io.Writer;
+
+import junit.framework.TestCase;
+
+/**
+ * Test FoAggregateSink.
+ *
+ * @author ltheussl
+ * @version $Id: FoAggregateSinkTest.java 811802 2009-09-06 10:49:37Z vsiveton $
+ */
+public class FoAggregateSinkTest
+    extends TestCase
+{
+    private FoAggregateSink sink;
+
+    private Writer writer;
+
+    /**
+     * Set up the writer.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+        writer = new StringWriter();
+    }
+
+    /**
+     * Test of body method, of class FoAggregateSink.
+     */
+    public void testBody()
+    {
+        try
+        {
+            sink = new FoAggregateSink( writer );
+
+            sink.setDocumentName( "folder/documentName.apt" );
+            sink.setDocumentTitle( "documentTitle" );
+            sink.body();
+            sink.body_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertTrue( writer.toString().indexOf( "<fo:block id=\"./folder/documentName\">" ) != -1 );
+    }
+
+    /**
+     * Test of setDocumentName method, of class FoAggregateSink.
+     */
+    public void testSetDocumentName()
+    {
+        try
+        {
+            sink = new FoAggregateSink( writer );
+
+            sink.setDocumentName( "folder\\documentName.boo" );
+            sink.body();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertTrue( writer.toString().indexOf( "<fo:block id=\"./folder/documentName\">" ) != -1 );
+    }
+
+    /**
+     * Test of figureGraphics method, of class FoAggregateSink.
+     */
+    public void testFigureGraphics()
+    {
+        try
+        {
+            sink = new FoAggregateSink( writer );
+            sink.setDocumentName( "./folder\\docName.xml" );
+            sink.figureGraphics( "./../images/fig.png", null );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertTrue( writer.toString().indexOf( "<fo:external-graphic src=\"./images/fig.png\"" ) != -1 );
+    }
+
+    /**
+     * Test of anchor method, of class FoAggregateSink.
+     */
+    public void testAnchor()
+    {
+        try
+        {
+            sink = new FoAggregateSink( writer );
+            sink.anchor( "invalid Anchor" );
+            sink.setDocumentName( "./folder\\docName.xml" );
+            sink.anchor( "validAnchor" );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertTrue( writer.toString().indexOf( "<fo:inline id=\"#invalid_Anchor\">" ) != -1 );
+        assertTrue( writer.toString().indexOf( "<fo:inline id=\"./folder/docName#validAnchor\">" ) != -1 );
+    }
+
+    /**
+     * Test of link method, of class FoAggregateSink.
+     */
+    public void testLink()
+    {
+        try
+        {
+            sink = new FoAggregateSink( writer );
+            sink.link( "http://www.example.com/" );
+            sink.text( "http://www.example.com/" );
+            sink.link_();
+            sink.setDocumentName( "./folder\\docName.xml" );
+            sink.link( "#anchor" );
+            sink.text( "#anchor" );
+            sink.link_();
+            sink.link( "./././index.html" );
+            sink.text( "./././index.html" );
+            sink.link_();
+            sink.link( "./../download.html" );
+            sink.text( "./../download.html" );
+            sink.link_();
+            sink.link( ".///test.html" );
+            sink.text( "./test.html" );
+            sink.link_();
+            sink.link( "./whatsnew-1.1.html" );
+            sink.text( "./whatsnew-1.1.html" );
+            sink.link_();
+            sink.setDocumentName( ".///whatsnew-1.1.html" );
+            sink.body();
+            sink.body_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        String result = writer.toString();
+
+        assertTrue( result.indexOf( "<fo:basic-link external-destination=\"http://www.example.com/\">" ) != -1 );
+        assertTrue( result.indexOf( "<fo:basic-link internal-destination=\"./folder/docName#anchor\">" ) != -1 );
+        assertTrue( result.indexOf( "<fo:basic-link internal-destination=\"./folder/index\">" ) != -1 );
+        assertTrue( result.indexOf( "<fo:basic-link internal-destination=\"./download\">" ) != -1 );
+        assertTrue( result.indexOf( "<fo:basic-link internal-destination=\"./folder/test\">" ) != -1 );
+        assertTrue( result.indexOf( "<fo:basic-link internal-destination=\"./folder/whatsnew-1.1\">" ) != -1 );
+        assertTrue( result.indexOf( "<fo:block id=\"./whatsnew-1.1\">" ) != -1 );
+
+        writer = new StringWriter();
+        try
+        {
+            sink = new FoAggregateSink( writer );
+            sink.setDocumentName( "./subdir/dir/index.html" );
+            sink.link( "../../root.html" );
+            sink.text( "../../root.html" );
+            sink.link_();
+            sink.link( "../../../outside.html" );
+            sink.text( "../../../outside.html" );
+            sink.link_();
+            sink.body();
+            sink.body_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        result = writer.toString();
+
+        assertTrue( result.indexOf( "<fo:basic-link internal-destination=\"./root\">" ) != -1 );
+        assertTrue( result.indexOf( "<fo:basic-link internal-destination=\"./outside\">" ) != -1 );
+    }
+}
diff --git a/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoConfigurationTest.java b/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoConfigurationTest.java
new file mode 100644
index 0000000..2681742
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoConfigurationTest.java
@@ -0,0 +1,70 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+import junit.framework.TestCase;
+
+/**
+ * FoConfiguration tests.
+ *
+ * @version $Id: FoConfigurationTest.java 806502 2009-08-21 11:33:25Z vsiveton $
+ */
+public class FoConfigurationTest
+    extends TestCase
+{
+
+    /** Tests the getAttributeString( String ) method. */
+    public void testGetAttributeString()
+    {
+        FoConfiguration config = new FoConfiguration();
+
+        assertEquals( "Null attribute ID should return empty string!", "", config.getAttributeString( null ) );
+
+        assertEquals( "Non existent attribute ID should return empty string!", "",
+                      config.getAttributeString( "a.dummy.attribute" ) );
+
+        assertEquals( "Wrong attributes returned for body.pre!", " font-family=\"monospace\" font-size=\"10pt\"",
+                      config.getAttributeString( "body.pre" ) );
+    }
+
+    /** Tests the getAttributeSet( String ) method. */
+    public void testGetAttributeSet()
+    {
+        FoConfiguration config = new FoConfiguration();
+
+        assertNull( "Null attribute ID should return null AttributeSet!", config.getAttributeSet( null ) );
+
+        assertNull( "Empty attribute ID should return null AttributeSet!", config.getAttributeSet( "" ) );
+
+        assertNull( "Non existent attribute ID should return null AttributeSet!",
+                    config.getAttributeSet( "a.dummy.attribute" ) );
+
+        MutableAttributeSet expected = new SimpleAttributeSet();
+        expected.addAttribute( "font-size", "10pt" );
+        expected.addAttribute( "font-family", "monospace" );
+        MutableAttributeSet actual = config.getAttributeSet( "body.pre" );
+
+        assertTrue( "Wrong AttributeSet returned for body.pre!", expected.isEqual( actual ) );
+    }
+
+}
diff --git a/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoSinkTest.java b/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoSinkTest.java
new file mode 100644
index 0000000..83c5fff
--- /dev/null
+++ b/doxia-modules/doxia-module-fo/src/test/java/org/apache/maven/doxia/module/fo/FoSinkTest.java
@@ -0,0 +1,528 @@
+package org.apache.maven.doxia.module.fo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.StringReader;
+import java.io.Writer;
+
+import org.apache.maven.doxia.document.DocumentMeta;
+import org.apache.maven.doxia.document.DocumentModel;
+import org.apache.maven.doxia.document.DocumentTOC;
+import org.apache.maven.doxia.document.DocumentTOCItem;
+
+import org.apache.maven.doxia.parser.XhtmlBaseParser;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+import org.apache.maven.doxia.sink.SinkTestDocument;
+
+/**
+ * <code>FO Sink</code> Test case.
+ *
+ * @version $Id: FoSinkTest.java 946933 2010-05-21 08:39:07Z ltheussl $
+ */
+public class FoSinkTest
+    extends AbstractSinkTest
+{
+    private FoConfiguration config;
+
+    // ----------------------------------------------------------------------
+    // Specific test methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Uses fop to generate a pdf from a test document.
+     * @throws Exception If the conversion fails.
+     */
+    public void testConvertFO2PDF()
+        throws Exception
+    {
+        String fileName = "test";
+        // first create fo
+        FoSink fosink = new FoSink( getTestWriter( fileName ) );
+        fosink.beginDocument();
+        SinkTestDocument.generate( fosink );
+        fosink.endDocument();
+        fosink.close();
+
+        // then generate PDF
+        fo2pdf( fileName );
+    }
+
+    /**
+     * Uses fop to generate an aggregated pdf from two test documents.
+     * @throws Exception If the conversion fails.
+     */
+    public void testAggregateMode()
+        throws Exception
+    {
+        FoAggregateSink fosink = new FoAggregateSink( getTestWriter( "aggregate" ) );
+
+        fosink.setDocumentModel( getModel() );
+
+        fosink.beginDocument();
+
+        fosink.coverPage();
+
+        fosink.toc();
+
+        fosink.setDocumentName( "doc1" );
+        fosink.setDocumentTitle( "Document 1" );
+        SinkTestDocument.generate( fosink );
+
+        // re-use the same source
+        fosink.setDocumentName( "doc2" );
+        fosink.setDocumentTitle( "Document 2" );
+        SinkTestDocument.generate( fosink );
+
+        fosink.endDocument();
+
+        // then generate PDF
+        fo2pdf( "aggregate" );
+    }
+
+    private DocumentModel getModel()
+    {
+        DocumentModel model = new DocumentModel();
+        model.setToc( getToc() );
+        model.setMeta( getMeta() );
+        return model;
+    }
+
+    private DocumentMeta getMeta()
+    {
+        DocumentMeta meta = new DocumentMeta();
+        meta.setAuthor( "The Apache Maven Project" );
+        meta.setTitle( "Doxia FO Sink" );
+        return meta;
+    }
+
+    private DocumentTOC getToc()
+    {
+        DocumentTOCItem item1 = new DocumentTOCItem();
+        item1.setName( "First document" );
+        item1.setRef( "doc1.apt" );
+
+        DocumentTOCItem item2 = new DocumentTOCItem();
+        item2.setName( "Second document" );
+        item2.setRef( "doc2.xml" );
+
+        DocumentTOC toc = new DocumentTOC();
+        toc.setName( "What's in here" );
+        toc.addItem( item1 );
+        toc.addItem( item2 );
+
+        return toc;
+    }
+
+    // ----------------------------------------------------------------------
+    // Abstract methods the individual SinkTests must provide
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "fo";
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new FoSink( writer );
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        String attribs = getConfig().getAttributeString( "doc.header.title" );
+        return EOL + "<fo:block" + attribs + ">" + title + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        String attribs = getConfig().getAttributeString( "doc.header.author" );
+        return EOL + "<fo:block" + attribs + ">" + author + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        String attribs = getConfig().getAttributeString( "doc.header.date" );
+        return EOL + "<fo:block" + attribs + ">" + date + "</fo:block>" + EOL;
+    }
+
+    // TODO
+    protected String getHeadBlock()
+    {
+        return "";
+    }
+
+    // TODO: remove
+    public void testHead()
+    {
+        String expected = "";
+        assertEquals( "Wrong head!", expected, getHeadBlock() );
+    }
+
+    /** {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return EOL + "</fo:flow>" + EOL + "</fo:page-sequence>" + EOL + "</fo:root>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        String attribs = getConfig().getAttributeString( "body.text" );
+        String attrib2 = getConfig().getAttributeString( "body.h1" );
+        return EOL + EOL + "<fo:block" + attribs + ">" + EOL + EOL + "<fo:block" + attrib2 + ">1   " + title
+            + "</fo:block>" + EOL + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        String attribs = getConfig().getAttributeString( "body.text" );
+        String attrib2 = getConfig().getAttributeString( "body.h2" );
+        return EOL + EOL + "<fo:block" + attribs + ">" + EOL + EOL + "<fo:block" + attrib2 + ">0.1   " + title
+            + "</fo:block>" + EOL + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        String attribs = getConfig().getAttributeString( "body.text" );
+        String attrib2 = getConfig().getAttributeString( "body.h3" );
+        return EOL + EOL + "<fo:block" + attribs + ">" + EOL + EOL + "<fo:block" + attrib2 + ">0.0.1   " + title
+            + "</fo:block>" + EOL + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        String attribs = getConfig().getAttributeString( "body.text" );
+        String attrib2 = getConfig().getAttributeString( "body.h4" );
+        return EOL + EOL + "<fo:block" + attribs + ">" + EOL + EOL + "<fo:block" + attrib2 + ">" + title
+            + "</fo:block>" + EOL + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        String attribs = getConfig().getAttributeString( "body.text" );
+        String attrib2 = getConfig().getAttributeString( "body.h5" );
+        return EOL + EOL + "<fo:block" + attribs + ">" + EOL + EOL + "<fo:block" + attrib2 + ">" + title
+            + "</fo:block>" + EOL + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        String attribs = getConfig().getAttributeString( "list" );
+        String itemAttribs = getConfig().getAttributeString( "list.item" );
+        return EOL + EOL + "<fo:list-block" + attribs + ">" + EOL + "<fo:list-item" + itemAttribs
+            + "><fo:list-item-label><fo:block>•</fo:block></fo:list-item-label>" + EOL + EOL
+            + "<fo:list-item-body" + itemAttribs + ">" + EOL + "<fo:block>" + item + "</fo:block>" + EOL
+            + "</fo:list-item-body>" + EOL + "</fo:list-item>" + EOL + "</fo:list-block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        String attribs = getConfig().getAttributeString( "list" );
+        String itemAttribs = getConfig().getAttributeString( "list.item" );
+        return EOL + EOL + "<fo:list-block" + attribs + ">" + EOL + "<fo:list-item" + itemAttribs + ">" + EOL
+            + "<fo:list-item-label>" + EOL + "<fo:block>i.</fo:block>" + EOL + "</fo:list-item-label>" + EOL + EOL
+            + "<fo:list-item-body" + itemAttribs + ">" + EOL + "<fo:block>" + item + "</fo:block>" + EOL
+            + "</fo:list-item-body>" + EOL + "</fo:list-item>" + EOL + "</fo:list-block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        String dlAtts = getConfig().getAttributeString( "dl" );
+        String dtAtts = getConfig().getAttributeString( "dt" );
+        String ddAtts = getConfig().getAttributeString( "dd" );
+        return EOL + EOL + "<fo:block" + dlAtts + ">" + EOL + "<fo:block" + dtAtts + ">" + definum + "</fo:block>"
+            + EOL + EOL + EOL + "<fo:block" + ddAtts + ">" + definition + "</fo:block>" + EOL + "</fo:block>"
+            + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        String dlAtts = getConfig().getAttributeString( "figure.display" );
+        String dtAtts = getConfig().getAttributeString( "figure.graphics" );
+        String ddAtts = getConfig().getAttributeString( "figure.caption" );
+        return EOL + EOL + "<fo:block" + dlAtts + "><fo:external-graphic" + " src=\"" + source + "\"" + dtAtts
+            + "/>" + EOL + EOL + "<fo:block" + ddAtts + ">" + caption + "</fo:block>" + EOL + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        String dlAtts = getConfig().getAttributeString( "table.padding" );
+        String dtAtts = getConfig().getAttributeString( "table.layout" );
+        String ddAtts = getConfig().getAttributeString( "table.body.row" );
+        // String deAtts = getConfig().getAttributeString( "table.body.cell" );
+
+        return EOL + EOL + "<fo:block" + dlAtts + ">" + EOL + "<fo:table" + dtAtts + ">" + EOL
+            + "<fo:table-column column-width=\"proportional-column-width(1)\"/>" + EOL + EOL + "<fo:table-body>"
+            + EOL + "<fo:table-row" + ddAtts
+            + "><fo:table-cell column-number=\"1\" padding-after=\"1.5pt\" padding-end=\"5pt\" "
+            + "keep-together.within-column=\"always\" padding-start=\"2.5pt\" "
+            + "background-color=\"#eeeeee\" padding-before=\"4pt\">" + EOL + "<fo:block line-height=\"1.2em\" "
+            + "text-align=\"center\" font-family=\"Helvetica,sans-serif\" font-size=\"9pt\">" + EOL + cell
+            + "</fo:block>" + EOL + "</fo:table-cell>" + EOL + "</fo:table-row>" + EOL + "</fo:table-body>" + EOL
+            + "</fo:table>" + EOL + "</fo:block>" + EOL + EOL
+            + "<fo:block white-space-collapse=\"true\" space-after=\"6pt\" space-before=\"3pt\" "
+            + "font-family=\"Garamond,serif\" line-height=\"12pt\" text-align=\"center\" font-size=\"11pt\">"
+            + "Table_caption</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        String attribs = getConfig().getAttributeString( "normal.paragraph" );
+        return EOL + "<fo:block" + attribs + ">" + text + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        String attribs = getConfig().getAttributeString( "body.source" );
+        return EOL + "<fo:block" + attribs + ">" + text + "</fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        String attribs = getConfig().getAttributeString( "body.rule" );
+        return EOL + EOL + "<fo:block>" + EOL + "<fo:leader" + attribs + " /></fo:block>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return EOL + "<fo:block break-before=\"page\" />" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        // all anchors get '#' pre-pended
+        return EOL + "<fo:inline id=\"#" + anchor + "\">" + anchor + "</fo:inline>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        String attribs = getConfig().getAttributeString( "href.internal" );
+        return EOL + "<fo:basic-link internal-destination=\"" + link + "\">" + EOL + "<fo:inline" + attribs + ">"
+            + text + "</fo:inline></fo:basic-link>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        String attribs = getConfig().getAttributeString( "italic" );
+        return EOL + "<fo:inline" + attribs + ">" + text + "</fo:inline>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        String attribs = getConfig().getAttributeString( "bold" );
+        return EOL + "<fo:inline" + attribs + ">" + text + "</fo:inline>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        String attribs = getConfig().getAttributeString( "monospace" );
+        return EOL + "<fo:inline" + attribs + ">" + text + "</fo:inline>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return EOL + EOL + "<fo:block />";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return " ";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        return FoSink.escaped( text, false );
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        return text;
+    }
+
+    // ----------------------------------------------------------------------
+    // Auxiliary methods
+    // ----------------------------------------------------------------------
+
+    private void fo2pdf( String baseName )
+        throws Exception
+    {
+        // File outputDirectory = new File( getBasedirFile(), getOutputDir() );
+        File outputDirectory = new File( getBasedir(), outputBaseDir() + getOutputDir() );
+        File resourceDirectory = new File( getBasedirFile(), "target/test-classes" );
+        File foFile = new File( outputDirectory, baseName + "." + outputExtension() );
+        File pdfFile = new File( outputDirectory, baseName + ".pdf" );
+        FoUtils.convertFO2PDF( foFile, pdfFile, resourceDirectory.getCanonicalPath() );
+    }
+
+    private FoConfiguration getConfig()
+    {
+        if ( config == null )
+        {
+            config = ( (FoSink) getSink() ).getFoConfiguration();
+        }
+
+        return config;
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "<!-- Simple comment with - - - - -->";
+    }
+
+    /**
+     * DOXIA-357
+     *
+     * @throws Exception if any
+     */
+    public void testTableCaption()
+        throws Exception
+    {
+        StringBuffer html = new StringBuffer();
+        html.append( "<table>" ).append( EOL );
+        html.append( "<caption>caption table</caption>" ).append( EOL );
+        html.append( "<tr>" ).append( EOL );
+        html.append( "<td>foo</td>" ).append( EOL );
+        html.append( "</tr>" ).append( EOL );
+        html.append( "<tr>" ).append( EOL );
+        html.append( "<td>bar</td>" ).append( EOL );
+        html.append( "</tr>" ).append( EOL );
+        html.append( "</table>" ).append( EOL );
+
+        String fileName = "testTableCaption";
+
+        // first create fo
+        FoSink fosink = new FoSink( getTestWriter( fileName ) );
+        fosink.beginDocument();
+        SinkTestDocument.generateHead( fosink );
+
+        fosink.body();
+        XhtmlBaseParser parser = new XhtmlBaseParser();
+        parser.parse( new StringReader( html.toString() ), fosink );
+        fosink.body_();
+
+        fosink.endDocument();
+        fosink.close();
+
+        // then generate PDF
+        fo2pdf( fileName );
+    }
+
+    /**
+     * @throws Exception if any
+     */
+    public void testNestedTables()
+        throws Exception
+    {
+        StringBuffer html = new StringBuffer();
+        html.append( "<table>" ).append( EOL );
+        html.append( "<caption>first caption</caption>" ).append( EOL );
+        html.append( "<tr>" ).append( EOL );
+        html.append( "<td>foo</td>" ).append( EOL );
+        html.append( "</tr>" ).append( EOL );
+        html.append( "<tr>" ).append( EOL );
+        html.append( "<td>" ).append( EOL );
+
+        html.append( "<table>" ).append( EOL );
+        html.append( "<caption>second caption</caption>" ).append( EOL );
+        html.append( "<tr>" ).append( EOL );
+        html.append( "<td>foo</td>" ).append( EOL );
+        html.append( "<td>bar</td>" ).append( EOL );
+        html.append( "</tr>" ).append( EOL );
+        html.append( "<tr>" ).append( EOL );
+        html.append( "<td>foo1</td>" ).append( EOL );
+        html.append( "<td>" ).append( EOL );
+
+        html.append( "<table>" ).append( EOL );
+        html.append( "<caption>third caption</caption>" ).append( EOL );
+        html.append( "<tr>" ).append( EOL );
+        html.append( "<td>foo1</td>" ).append( EOL );
+        html.append( "<td>bar1</td>" ).append( EOL );
+        html.append( "</tr>" ).append( EOL );
+        html.append( "</table>" ).append( EOL );
+        html.append( "</td>" ).append( EOL );
+
+        html.append( "</tr>" ).append( EOL );
+        html.append( "</table>" ).append( EOL );
+
+        html.append( "</td>" ).append( EOL );
+        html.append( "</tr>" ).append( EOL );
+        html.append( "</table>" ).append( EOL );
+
+        String fileName = "testNestedTables";
+
+        // first create fo
+        FoSink fosink = new FoSink( getTestWriter( fileName ) );
+        fosink.beginDocument();
+        SinkTestDocument.generateHead( fosink );
+
+        fosink.body();
+        XhtmlBaseParser parser = new XhtmlBaseParser();
+        parser.parse( new StringReader( html.toString() ), fosink );
+        fosink.body_();
+
+        fosink.endDocument();
+        fosink.close();
+
+        // then generate PDF
+        fo2pdf( fileName );
+    }
+}
diff --git a/doxia-modules/doxia-module-fo/src/test/resources/figure.png b/doxia-modules/doxia-module-fo/src/test/resources/figure.png
new file mode 100644
index 0000000..a49903a
Binary files /dev/null and b/doxia-modules/doxia-module-fo/src/test/resources/figure.png differ
diff --git a/doxia-modules/doxia-module-itext/pom.xml b/doxia-modules/doxia-module-itext/pom.xml
new file mode 100644
index 0000000..d61131e
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-itext</artifactId>
+
+  <name>Doxia :: iText Module</name>
+  <description>A Doxia module for iText source documents.</description>
+
+  <developers>
+    <developer>
+      <id>vsiveton</id>
+      <name>Vincent Siveton</name>
+      <email>vsiveton at apache.org</email>
+      <organization>Apache Software Foundation</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+      <timezone>-5</timezone>
+    </developer>
+  </developers>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.lowagie</groupId>
+      <artifactId>itext</artifactId>
+      <version>1.4</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <!-- To be sure that JVM will implement AWT in software -->
+          <systemProperties>
+            <property>
+              <name>java.awt.headless</name>
+              <value>true</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextFont.java b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextFont.java
new file mode 100644
index 0000000..329b407
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextFont.java
@@ -0,0 +1,389 @@
+package org.apache.maven.doxia.module.itext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.awt.Color;
+
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Font;
+import com.lowagie.text.FontFactory;
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.pdf.BaseFont;
+
+/**
+ * <code>iText</code> wrapper object for font.
+ *
+ * @see com.lowagie.text.Font
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ITextFont.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+public class ITextFont
+{
+    /** A normal font style */
+    public static final String NORMAL = MarkupTags.CSS_VALUE_NORMAL;
+
+    /** A bold font style */
+    public static final String BOLD = MarkupTags.CSS_VALUE_BOLD;
+
+    /** A italic font style */
+    public static final String ITALIC = MarkupTags.CSS_VALUE_ITALIC;
+
+    /** An underline font style */
+    public static final String UNDERLINE = MarkupTags.CSS_VALUE_UNDERLINE;
+
+    /** A default font name */
+    public static final String DEFAULT_FONT_NAME = FontFactory.HELVETICA;
+
+    /** A default font size */
+    public static final float DEFAULT_FONT_SIZE = 12;
+
+    /** A default font style */
+    public static final String DEFAULT_FONT_STYLE = NORMAL;
+
+    /** A default Black color definition */
+    public static final int DEFAULT_FONT_COLOR_RED = Color.BLACK.getRed();
+
+    /** A default Black color definition */
+    public static final int DEFAULT_FONT_COLOR_GREEN = Color.BLACK.getGreen();
+
+    /** A default Black color definition */
+    public static final int DEFAULT_FONT_COLOR_BLUE = Color.BLACK.getBlue();
+
+    private static final int SECTION_FONT_SIZE_0 = 24;
+    private static final int SECTION_FONT_SIZE_1 = 22;
+    private static final int SECTION_FONT_SIZE_2 = 20;
+    private static final int SECTION_FONT_SIZE_3 = 18;
+    private static final int SECTION_FONT_SIZE_4 = 16;
+    private static final int SECTION_FONT_SIZE_DEFAULT = 14;
+
+    private boolean monoSpaced = false;
+
+    private float currentSize = 12;
+
+    private int currentStyle = Font.NORMAL;
+
+    private Color currentColor = Color.BLACK;
+
+    /**
+     * Default constructor
+     */
+    public ITextFont()
+    {
+        // nop
+    }
+
+    /**
+     * Add bold style to the current style
+     */
+    public void addBold()
+    {
+        this.currentStyle += Font.BOLD;
+    }
+
+    /**
+     * Remove bold style to the current style
+     */
+    public void removeBold()
+    {
+        this.currentStyle -= Font.BOLD;
+        if ( this.currentStyle < 0 )
+        {
+            this.currentStyle = Font.NORMAL;
+        }
+    }
+
+    /**
+     * Add italic style to the current style
+     */
+    public void addItalic()
+    {
+        this.currentStyle += Font.ITALIC;
+    }
+
+    /**
+     * Remove italic style to the current style
+     */
+    public void removeItalic()
+    {
+        this.currentStyle -= Font.ITALIC;
+        if ( this.currentStyle < 0 )
+        {
+            this.currentStyle = Font.NORMAL;
+        }
+    }
+
+    /**
+     * Add italic style to the current style
+     */
+    public void addUnderlined()
+    {
+        this.currentStyle += Font.UNDERLINE;
+    }
+
+    /**
+     * Remove italic style to the current style
+     */
+    public void removeUnderlined()
+    {
+        this.currentStyle -= Font.UNDERLINE;
+        if ( this.currentStyle < 0 )
+        {
+            this.currentStyle = Font.NORMAL;
+        }
+    }
+
+    /**
+     * Add monospaced style to the current style
+     *
+     * @param monoSpaced true for monospaced style
+     */
+    public void setMonoSpaced( boolean monoSpaced )
+    {
+        this.monoSpaced = monoSpaced;
+    }
+
+    /**
+     * Set a new font color
+     *
+     * @param color a new color
+     */
+    public void setColor( Color color )
+    {
+        this.currentColor = color;
+    }
+
+    /**
+     * Set a new font color
+     *
+     * @param size a new size
+     */
+    public void setSize( float size )
+    {
+        this.currentSize = size;
+    }
+
+    /**
+     * Return the font name
+     *
+     * @return the font name
+     */
+    public String getFontName()
+    {
+        Font font = getCurrentFont();
+
+        return font.getFamilyname();
+    }
+
+    /**
+     * Return the font style
+     *
+     * @return the font style
+     */
+    public String getFontStyle()
+    {
+        Font font = getCurrentFont();
+        StringBuffer sb = new StringBuffer();
+
+        if ( font.isBold() )
+        {
+            sb.append( BOLD );
+        }
+
+        if ( font.isItalic() )
+        {
+            if ( sb.length() == 0 )
+            {
+                sb.append( ITALIC );
+            }
+            else
+            {
+                sb.append( "," );
+                sb.append( ITALIC );
+            }
+        }
+
+        if ( font.isUnderlined() )
+        {
+            if ( sb.length() == 0 )
+            {
+                sb.append( UNDERLINE );
+            }
+            else
+            {
+                sb.append( "," );
+                sb.append( UNDERLINE );
+            }
+        }
+
+        if ( sb.length() == 0 )
+        {
+            return NORMAL;
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Return the font name
+     *
+     * @return the font name
+     */
+    public String getFontSize()
+    {
+        Font font = getCurrentFont();
+
+        return String.valueOf( font.getCalculatedSize() );
+    }
+
+    /**
+     * Return the font color blue
+     *
+     * @return the font color blue
+     */
+    public String getFontColorBlue()
+    {
+        Font font = getCurrentFont();
+
+        return String.valueOf( font.color().getBlue() );
+    }
+
+    /**
+     * Return the font color green
+     *
+     * @return the font color green
+     */
+    public String getFontColorGreen()
+    {
+        Font font = getCurrentFont();
+
+        return String.valueOf( font.color().getGreen() );
+    }
+
+    /**
+     * Return the font color red
+     *
+     * @return the font color red
+     */
+    public String getFontColorRed()
+    {
+        Font font = getCurrentFont();
+
+        return String.valueOf( font.color().getRed() );
+    }
+
+    /**
+     * Get a section font size depending the section number.
+     * <dl>
+     * <dt>0</dt>
+     * <dd>Chapter: font size = 24</dd>
+     * <dt>1</dt>
+     * <dd>Section 1: font size = 22</dd>
+     * <dt>2</dt>
+     * <dd>Section 2: font size = 20</dd>
+     * <dt>3</dt>
+     * <dd>Section 3: font size = 18</dd>
+     * <dt>4</dt>
+     * <dd>Section 4: font size = 16</dd>
+     * <dt>5 ot otherwise</dt>
+     * <dd>Section 5: font size = 14</dd>
+     * </dl>
+     *
+     * @param sectionNumber a section number
+     * @return a font size.
+     */
+    public static int getSectionFontSize( int sectionNumber )
+    {
+        switch ( sectionNumber )
+        {
+            case 0:
+                return SECTION_FONT_SIZE_0;
+
+            case 1:
+                return SECTION_FONT_SIZE_1;
+
+            case 2:
+                return SECTION_FONT_SIZE_2;
+
+            case 3:
+                return SECTION_FONT_SIZE_3;
+
+            case 4:
+                return SECTION_FONT_SIZE_4;
+
+            case 5:
+            default:
+                return SECTION_FONT_SIZE_DEFAULT;
+        }
+    }
+
+    /**
+     * Convenience method to get a defined MonoSpaced font depending the wanted style and size.
+     *
+     * @param style the font style.
+     * @param size the font size.
+     * @param color the font color.
+     * @return a font the font.
+     */
+    public static Font getMonoSpacedFont( int style, float size, Color color )
+    {
+        try
+        {
+            return new Font( BaseFont.createFont( BaseFont.COURIER, BaseFont.CP1252, false ), size, style, color );
+        }
+        catch ( Exception e )
+        {
+            throw new ExceptionConverter( e );
+        }
+    }
+
+    /**
+     * Convenience method to get a defined font depending the wanted style and size.
+     *
+     * @param style the font style.
+     * @param size the font size.
+     * @param color the font color.
+     * @return a font the font.
+     */
+    public static Font getFont( int style, float size, Color color )
+    {
+        Font font = new Font();
+        font.setFamily( DEFAULT_FONT_NAME );
+        font.setStyle( style );
+        font.setSize( size );
+        font.setColor( color );
+        return font;
+    }
+
+    /**
+     * Convenience method to return the current font
+     *
+     * @return the current font
+     */
+    private Font getCurrentFont()
+    {
+        if ( this.monoSpaced )
+        {
+            return getMonoSpacedFont( this.currentStyle, this.currentSize, this.currentColor );
+        }
+
+        return getFont( this.currentStyle, this.currentSize, this.currentColor );
+    }
+}
diff --git a/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextHeader.java b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextHeader.java
new file mode 100644
index 0000000..71ac483
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextHeader.java
@@ -0,0 +1,139 @@
+package org.apache.maven.doxia.module.itext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+import java.util.Date;
+
+/**
+ * Header object containing meta-informations.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ITextHeader.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+public class ITextHeader
+{
+    private String title;
+
+    private StringBuffer authors;
+
+    private Date date;
+
+    /**
+     * Default constructor
+     */
+    public ITextHeader()
+    {
+        // nop
+    }
+
+    /**
+     * Add a title to the Document
+     *
+     * @param title1 the title.
+     */
+    public final void setTitle( String title1 )
+    {
+        this.title = title1;
+    }
+
+    /**
+     * Get the title
+     *
+     * @return title as String
+     */
+    public String getTitle()
+    {
+        if ( this.title == null )
+        {
+            return "";
+        }
+
+        return this.title;
+    }
+
+    /**
+     * Add a new author
+     *
+     * @param author the author.
+     */
+    public void addAuthor( String author )
+    {
+        if ( this.authors == null )
+        {
+            this.authors = new StringBuffer();
+        }
+        else
+        {
+            this.authors.append( ", " );
+        }
+
+        this.authors.append( author );
+    }
+
+    /**
+     * Get the authors
+     *
+     * @return the authors as String
+     */
+    public String getAuthors()
+    {
+        if ( ( this.authors == null ) || ( this.authors.length() == 0 ) )
+        {
+            return System.getProperty( "user.name" );
+        }
+
+        return this.authors.toString();
+    }
+
+    /**
+     * Add a date to the document
+     *
+     * @param date1 a date as String
+     */
+    public void setDate( String date1 )
+    {
+        try
+        {
+            this.date = new SimpleDateFormat().parse( date1 );
+        }
+        catch ( ParseException e )
+        {
+            this.date = new Date();
+        }
+    }
+
+    /**
+     * Get the date of the document
+     *
+     * @return the date as String
+     */
+    public String getDate()
+    {
+        if ( this.date == null )
+        {
+            return new Date( System.currentTimeMillis() ).toString();
+        }
+
+        return this.date.toString();
+    }
+}
diff --git a/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextSink.java b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextSink.java
new file mode 100644
index 0000000..1824dbb
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextSink.java
@@ -0,0 +1,1829 @@
+package org.apache.maven.doxia.module.itext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import com.lowagie.text.BadElementException;
+import com.lowagie.text.ElementTags;
+import com.lowagie.text.Image;
+
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.maven.doxia.sink.AbstractXmlSink;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+import org.codehaus.plexus.util.xml.XMLWriter;
+
+
+/**
+ * <p>A doxia Sink which produces an XML Front End document for <code>iText</code> framework.</p>
+ * Known limitations:
+ * <ul>
+ * <li>Roman lists are not supported.</li>
+ * <li>Horizontal rule is not supported with 1.3.
+ * See <a href="http://www.mail-archive.com/itext-questions@lists.sourceforge.net/msg10323.html">
+ * http://www.mail-archive.com/itext-questions@lists.sourceforge.net/msg10323.html</a></li>
+ * <li>iText has some problems with <code>ElementTags.TABLE</code> and <code>ElementTags.TABLEFITSPAGE</code>.
+ * See http://sourceforge.net/tracker/index.php?func=detail&aid=786427&group_id=15255&atid=115255.</li>
+ * <li>Images could be on another page and next text on the last one.</li>
+ * </ul>
+ *
+ * @see <a href="http://www.lowagie.com/iText/tutorial/ch07.html">http://www.lowagie.com/iText/tutorial/ch07.html</a>
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ITextSink.java 1003454 2010-10-01 09:46:51Z ltheussl $
+ */
+public class ITextSink
+    extends AbstractXmlSink
+{
+    /** This is the place where the iText DTD is located. IMPORTANT: this DTD is not uptodate! */
+    public static final String DTD = "http://itext.sourceforge.net/itext.dtd";
+
+    /** This is the reference to the DTD. */
+    public static final String DOCTYPE = "ITEXT SYSTEM \"" + DTD + "\"";
+
+    /** This is the default leading for chapter title */
+    public static final String DEFAULT_CHAPTER_TITLE_LEADING = "36.0";
+
+    /** This is the default leading for section title */
+    public static final String DEFAULT_SECTION_TITLE_LEADING = "24.0";
+
+    /** The ClassLoader used */
+    private ClassLoader currentClassLoader;
+
+    /** The action context */
+    private SinkActionContext actionContext;
+
+    /** The Writer used */
+    private Writer writer;
+
+    /** The XML Writer used */
+    private final XMLWriter xmlWriter;
+
+    private boolean writeStart;
+
+    /** The Header object */
+    private ITextHeader header;
+
+    /** The font object */
+    private ITextFont font;
+
+    private int numberDepth = 1;
+
+    private int depth = 0;
+
+    private StringWriter tableCaptionWriter = null;
+
+    private XMLWriter tableCaptionXMLWriter = null;
+
+    /** Flag to know if an anchor is defined or not. Used as workaround for iText which needs a defined local
+     * destination. */
+    private boolean anchorDefined = false;
+
+    /** Flag to know if an figure event is called. */
+    private boolean figureDefined = false;
+
+    /** Map of warn messages with a String as key to describe the error type and a Set as value.
+     * Using to reduce warn messages. */
+    private Map warnMessages;
+
+    /**
+     * <p>Constructor for ITextSink.</p>
+     *
+     * @param writer the writer.
+     */
+    protected ITextSink( Writer writer )
+    {
+        this( writer, "UTF-8" );
+    }
+
+    /**
+     * <p>Constructor for ITextSink.</p>
+     *
+     * @param writer the writer.
+     * @param encoding the encoding.
+     * @since 1.1
+     */
+    protected ITextSink( Writer writer, String encoding )
+    {
+        // No doctype since itext doctype is not up to date!
+        this( new PrettyPrintXMLWriter( writer, encoding, null ) );
+
+        this.writer = writer;
+        this.writeStart = true;
+    }
+
+    /**
+     * <p>Constructor for ITextSink.</p>
+     *
+     * @param xmlWriter a pretty-printing xml writer.
+     */
+    protected ITextSink( PrettyPrintXMLWriter xmlWriter )
+    {
+        this.xmlWriter = xmlWriter;
+
+        this.writeStart = false;
+
+        init();
+    }
+
+    /**
+     * Get the current classLoader
+     *
+     * @return the current class loader
+     */
+    public ClassLoader getClassLoader()
+    {
+        return currentClassLoader;
+    }
+
+    /**
+     * Set a new class loader
+     *
+     * @param cl the class loader.
+     */
+    public void setClassLoader( ClassLoader cl )
+    {
+        currentClassLoader = cl;
+    }
+
+    // ----------------------------------------------------------------------
+    // Document
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        IOUtil.close( writer );
+
+        init();
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        if ( getLog().isWarnEnabled() && this.warnMessages != null )
+        {
+            for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                Set set = (Set) entry.getValue();
+
+                for ( Iterator it2 = set.iterator(); it2.hasNext(); )
+                {
+                    String msg = (String) it2.next();
+
+                    getLog().warn( msg );
+                }
+            }
+        }
+
+        this.warnMessages = null;
+    }
+
+    // ----------------------------------------------------------------------
+    // Header
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        //init(); // why? this causes DOXIA-413
+
+        actionContext.setAction( SinkActionContext.HEAD );
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void author()
+    {
+        actionContext.setAction( SinkActionContext.AUTHOR );
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void date()
+    {
+        actionContext.setAction( SinkActionContext.DATE );
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        actionContext.setAction( SinkActionContext.TITLE );
+    }
+
+    // ----------------------------------------------------------------------
+    // Body
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        if ( writeStart )
+        {
+            writeEndElement(); // ElementTags.CHAPTER
+
+            writeEndElement(); // ElementTags.ITEXT
+        }
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        if ( writeStart )
+        {
+            writeStartElement( ElementTags.ITEXT );
+            writeAddAttribute( ElementTags.TITLE, header.getTitle() );
+            writeAddAttribute( ElementTags.AUTHOR, header.getAuthors() );
+            writeAddAttribute( ElementTags.CREATIONDATE, header.getDate() );
+            writeAddAttribute( ElementTags.SUBJECT, header.getTitle() );
+            writeAddAttribute( ElementTags.KEYWORDS, "" );
+            writeAddAttribute( ElementTags.PRODUCER, "Generated with Doxia by " + System.getProperty( "user.name" ) );
+            writeAddAttribute( ElementTags.PAGE_SIZE, ITextUtil.getPageSize( ITextUtil.getDefaultPageSize() ) );
+
+            writeStartElement( ElementTags.CHAPTER );
+            writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
+            writeAddAttribute( ElementTags.DEPTH, depth );
+            writeAddAttribute( ElementTags.INDENT, "0.0" );
+
+            writeStartElement( ElementTags.TITLE );
+            writeAddAttribute( ElementTags.LEADING, DEFAULT_CHAPTER_TITLE_LEADING );
+            writeAddAttribute( ElementTags.FONT, ITextFont.DEFAULT_FONT_NAME );
+            writeAddAttribute( ElementTags.SIZE, ITextFont.getSectionFontSize( 0 ) );
+            writeAddAttribute( ElementTags.STYLE, ITextFont.BOLD );
+            writeAddAttribute( ElementTags.BLUE, ITextFont.DEFAULT_FONT_COLOR_BLUE );
+            writeAddAttribute( ElementTags.GREEN, ITextFont.DEFAULT_FONT_COLOR_GREEN );
+            writeAddAttribute( ElementTags.RED, ITextFont.DEFAULT_FONT_COLOR_RED );
+            writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER );
+
+//            startChunk( ITextFont.DEFAULT_FONT_NAME, ITextFont.getSectionFontSize( 0 ),
+//                    ITextFont.BOLD, ITextFont.DEFAULT_FONT_COLOR_BLUE, ITextFont.DEFAULT_FONT_COLOR_GREEN,
+//                    ITextFont.DEFAULT_FONT_COLOR_RED, "top" );
+
+            writeStartElement( ElementTags.CHUNK );
+            writeAddAttribute( ElementTags.FONT, ITextFont.DEFAULT_FONT_NAME );
+            writeAddAttribute( ElementTags.SIZE, ITextFont.getSectionFontSize( 0 ) );
+            writeAddAttribute( ElementTags.STYLE, ITextFont.BOLD );
+            writeAddAttribute( ElementTags.BLUE, ITextFont.DEFAULT_FONT_COLOR_BLUE );
+            writeAddAttribute( ElementTags.GREEN, ITextFont.DEFAULT_FONT_COLOR_GREEN );
+            writeAddAttribute( ElementTags.RED, ITextFont.DEFAULT_FONT_COLOR_RED );
+//            writeAddAttribute( ElementTags.LOCALDESTINATION, "top" );
+
+            write( header.getTitle() );
+
+            writeEndElement(); // ElementTags.CHUNK
+
+            writeEndElement(); // ElementTags.TITLE
+        }
+
+        actionContext.setAction( SinkActionContext.BODY );
+    }
+
+    // ----------------------------------------------------------------------
+    // Sections
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void sectionTitle()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_()
+    {
+        actionContext.setAction( SinkActionContext.SECTION_TITLE );
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        writeEndElement(); // ElementTags.SECTION
+
+        numberDepth--;
+        depth = 0;
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void section1()
+    {
+        numberDepth++;
+        depth = 1;
+
+        writeStartElement( ElementTags.SECTION );
+        writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
+        writeAddAttribute( ElementTags.DEPTH, depth );
+        writeAddAttribute( ElementTags.INDENT, "0.0" );
+
+        lineBreak();
+
+        actionContext.setAction( SinkActionContext.SECTION_1 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        writeEndElement(); // ElementTags.TITLE
+
+        font.setSize( ITextFont.DEFAULT_FONT_SIZE );
+        bold_();
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        font.setSize( ITextFont.getSectionFontSize( 1 ) );
+        font.setColor( Color.BLACK );
+        bold();
+
+        writeStartElement( ElementTags.TITLE );
+        writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+//        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
+
+        actionContext.setAction( SinkActionContext.SECTION_TITLE_1 );
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        writeEndElement(); // ElementTags.SECTION
+
+        numberDepth--;
+        depth = 0;
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void section2()
+    {
+        numberDepth++;
+        depth = 1;
+
+        writeStartElement( ElementTags.SECTION );
+        writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
+        writeAddAttribute( ElementTags.DEPTH, depth );
+        writeAddAttribute( ElementTags.INDENT, "0.0" );
+
+        lineBreak();
+
+        actionContext.setAction( SinkActionContext.SECTION_2 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        writeEndElement(); // ElementTags.TITLE
+
+        font.setSize( ITextFont.DEFAULT_FONT_SIZE );
+        bold_();
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        font.setSize( ITextFont.getSectionFontSize( 2 ) );
+        font.setColor( Color.BLACK );
+        bold();
+
+        writeStartElement( ElementTags.TITLE );
+        writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+//        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
+
+        actionContext.setAction( SinkActionContext.SECTION_TITLE_2 );
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        writeEndElement(); // ElementTags.SECTION
+
+        numberDepth--;
+        depth = 1;
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void section3()
+    {
+        numberDepth++;
+        depth = 1;
+
+        writeStartElement( ElementTags.SECTION );
+        writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
+        writeAddAttribute( ElementTags.DEPTH, depth );
+        writeAddAttribute( ElementTags.INDENT, "0.0" );
+
+        lineBreak();
+
+        actionContext.setAction( SinkActionContext.SECTION_3 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        writeEndElement(); // ElementTags.TITLE
+
+        font.setSize( ITextFont.DEFAULT_FONT_SIZE );
+        bold_();
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        font.setSize( ITextFont.getSectionFontSize( 3 ) );
+        font.setColor( Color.BLACK );
+        bold();
+
+        writeStartElement( ElementTags.TITLE );
+        writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+//        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
+
+        actionContext.setAction( SinkActionContext.SECTION_TITLE_3 );
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        writeEndElement(); // ElementTags.SECTION
+
+        numberDepth--;
+        depth = 1;
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void section4()
+    {
+        numberDepth++;
+        depth = 1;
+
+        writeStartElement( ElementTags.SECTION );
+        writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
+        writeAddAttribute( ElementTags.DEPTH, depth );
+        writeAddAttribute( ElementTags.INDENT, "0.0" );
+
+        lineBreak();
+
+        actionContext.setAction( SinkActionContext.SECTION_4 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        writeEndElement(); // ElementTags.TITLE
+
+        font.setSize( ITextFont.DEFAULT_FONT_SIZE );
+        bold_();
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        font.setSize( ITextFont.getSectionFontSize( 4 ) );
+        font.setColor( Color.BLACK );
+        bold();
+
+        writeStartElement( ElementTags.TITLE );
+        writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+//        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
+
+        actionContext.setAction( SinkActionContext.SECTION_TITLE_4 );
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        writeEndElement(); // ElementTags.SECTION
+
+        numberDepth--;
+        depth = 1;
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void section5()
+    {
+        numberDepth++;
+        depth = 1;
+
+        writeStartElement( ElementTags.SECTION );
+        writeAddAttribute( ElementTags.NUMBERDEPTH, numberDepth );
+        writeAddAttribute( ElementTags.DEPTH, depth );
+        writeAddAttribute( ElementTags.INDENT, "0.0" );
+
+        lineBreak();
+
+        actionContext.setAction( SinkActionContext.SECTION_5 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        writeEndElement(); // ElementTags.TITLE
+
+        font.setSize( ITextFont.DEFAULT_FONT_SIZE );
+        bold_();
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        font.setSize( ITextFont.getSectionFontSize( 5 ) );
+        font.setColor( Color.BLACK );
+        bold();
+
+        writeStartElement( ElementTags.TITLE );
+        writeAddAttribute( ElementTags.LEADING, DEFAULT_SECTION_TITLE_LEADING );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+//        writeAddAttribute( ElementTags.LOCALDESTINATION, "top" ); // trygve
+
+        actionContext.setAction( SinkActionContext.SECTION_TITLE_5 );
+    }
+
+    // ----------------------------------------------------------------------
+    // Paragraph
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        // Special case
+        if ( ( actionContext.getCurrentAction() == SinkActionContext.LIST_ITEM )
+            || ( actionContext.getCurrentAction() == SinkActionContext.NUMBERED_LIST_ITEM )
+            || ( actionContext.getCurrentAction() == SinkActionContext.DEFINITION ) )
+        {
+            return;
+        }
+
+        writeEndElement(); // ElementTags.PARAGRAPH
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph()
+    {
+        // Special case
+        if ( ( actionContext.getCurrentAction() == SinkActionContext.LIST_ITEM )
+            || ( actionContext.getCurrentAction() == SinkActionContext.NUMBERED_LIST_ITEM )
+            || ( actionContext.getCurrentAction() == SinkActionContext.DEFINITION ) )
+        {
+            return;
+        }
+
+        writeStartElement( ElementTags.PARAGRAPH );
+        writeStartElement( ElementTags.NEWLINE );
+        writeEndElement();
+
+        actionContext.setAction( SinkActionContext.PARAGRAPH );
+    }
+
+    // ----------------------------------------------------------------------
+    // Lists
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        writeEndElement(); // ElementTags.LIST
+
+        writeEndElement(); // ElementTags.CHUNK
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        writeStartElement( ElementTags.LIST );
+        writeAddAttribute( ElementTags.NUMBERED, Boolean.FALSE.toString() );
+        writeAddAttribute( ElementTags.SYMBOLINDENT, "15" );
+
+        actionContext.setAction( SinkActionContext.LIST );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        writeEndElement(); // ElementTags.LISTITEM
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        writeStartElement( ElementTags.LISTITEM );
+        writeAddAttribute( ElementTags.INDENTATIONLEFT, "20.0" );
+
+        actionContext.setAction( SinkActionContext.LIST_ITEM );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        writeEndElement(); // ElementTags.LIST
+
+        writeEndElement(); // ElementTags.CHUNK
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        writeStartElement( ElementTags.LIST );
+        writeAddAttribute( ElementTags.NUMBERED, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.SYMBOLINDENT, "20" );
+
+        switch ( numbering )
+        {
+            case Sink.NUMBERING_UPPER_ALPHA:
+                writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() );
+                writeAddAttribute( ElementTags.FIRST, 'A' );
+                break;
+
+            case Sink.NUMBERING_LOWER_ALPHA:
+                writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() );
+                writeAddAttribute( ElementTags.FIRST, 'a' );
+                break;
+
+            // TODO Doesn't work
+            case Sink.NUMBERING_UPPER_ROMAN:
+                writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() );
+                writeAddAttribute( ElementTags.FIRST, 'I' );
+                break;
+
+            case Sink.NUMBERING_LOWER_ROMAN:
+                writeAddAttribute( ElementTags.LETTERED, Boolean.TRUE.toString() );
+                writeAddAttribute( ElementTags.FIRST, 'i' );
+                break;
+
+            case Sink.NUMBERING_DECIMAL:
+            default:
+                writeAddAttribute( ElementTags.LETTERED, Boolean.FALSE.toString() );
+        }
+
+        actionContext.setAction( SinkActionContext.NUMBERED_LIST );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        writeEndElement(); // ElementTags.LISTITEM
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        writeStartElement( ElementTags.LISTITEM );
+        writeAddAttribute( ElementTags.INDENTATIONLEFT, "20" );
+
+        actionContext.setAction( SinkActionContext.NUMBERED_LIST_ITEM );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList_()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList()
+    {
+        lineBreak();
+
+        actionContext.setAction( SinkActionContext.DEFINITION_LIST );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm_()
+    {
+        font.setSize( ITextFont.DEFAULT_FONT_SIZE );
+        bold_();
+
+        writeEndElement(); // ElementTags.CHUNK
+
+        actionContext.release();
+
+        lineBreak();
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        font.setSize( ITextFont.DEFAULT_FONT_SIZE + 2 );
+        bold();
+
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        actionContext.setAction( SinkActionContext.DEFINED_TERM );
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        writeEndElement(); // ElementTags.CHUNK
+
+        actionContext.release();
+
+        lineBreak();
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        // We need to add a non break space first to display empty string
+        write( "\u00A0" + StringUtils.repeat( " ", 16 ), false, false );
+
+        writeEndElement(); // ElementTags.CHUNK
+
+        actionContext.setAction( SinkActionContext.DEFINITION );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem_()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        actionContext.setAction( SinkActionContext.DEFINITION_LIST_ITEM );
+    }
+
+    // ----------------------------------------------------------------------
+    //  Tables
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        if ( tableCaptionXMLWriter != null )
+        {
+            tableCaptionXMLWriter = null;
+
+            writeEndElement(); // ElementTags.TABLE
+
+            writeEndElement(); // ElementTags.CHUNK
+
+            writeStartElement( ElementTags.PARAGRAPH );
+            writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER );
+
+            write( tableCaptionWriter.toString(), true );
+
+            writeEndElement(); // ElementTags.PARAGRAPH
+
+            tableCaptionWriter = null;
+        }
+        else
+        {
+            writeEndElement(); // ElementTags.TABLE
+
+            writeEndElement(); // ElementTags.CHUNK
+        }
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        writeStartElement( ElementTags.TABLE );
+        writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER );
+        writeAddAttribute( ElementTags.WIDTH, "100.0%" );
+        writeAddAttribute( ElementTags.TABLEFITSPAGE, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.CELLSFITPAGE, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.CELLPADDING, "10" );
+        //writeAddAttribute( ElementTags.COLUMNS, "2" );
+
+        actionContext.setAction( SinkActionContext.TABLE );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption_()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption()
+    {
+        tableCaptionWriter = new StringWriter();
+        tableCaptionXMLWriter = new PrettyPrintXMLWriter( tableCaptionWriter );
+        actionContext.setAction( SinkActionContext.TABLE_CAPTION );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        writeEndElement(); // ElementTags.CELL
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        writeStartElement( ElementTags.CELL );
+        writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.HORIZONTALALIGN, ElementTags.ALIGN_LEFT );
+
+        actionContext.setAction( SinkActionContext.TABLE_CELL );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        actionContext.setAction( SinkActionContext.TABLE_CELL );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        writeEndElement(); // ElementTags.CELL
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        writeStartElement( ElementTags.CELL );
+        writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.HEADER, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.BGRED, Color.GRAY.getRed() );
+        writeAddAttribute( ElementTags.BGBLUE, Color.GRAY.getBlue() );
+        writeAddAttribute( ElementTags.BGGREEN, Color.GRAY.getGreen() );
+        writeAddAttribute( ElementTags.HORIZONTALALIGN, ElementTags.ALIGN_CENTER );
+
+        actionContext.setAction( SinkActionContext.TABLE_HEADER_CELL );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        actionContext.setAction( SinkActionContext.TABLE_HEADER_CELL );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        writeEndElement(); // ElementTags.ROW
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        writeStartElement( ElementTags.ROW );
+
+        actionContext.setAction( SinkActionContext.TABLE_ROW );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        //writeEndElement(); // ElementTags.TABLE
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        // ElementTags.TABLE
+        writeAddAttribute( ElementTags.COLUMNS, justification.length );
+
+        actionContext.setAction( SinkActionContext.TABLE_ROWS );
+    }
+
+    // ----------------------------------------------------------------------
+    // Verbatim
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        writeEndElement(); // ElementTags.CELL
+
+        writeEndElement(); // ElementTags.ROW
+
+        writeEndElement(); // ElementTags.TABLE
+
+        writeEndElement(); // ElementTags.CHUNK
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        // Always boxed
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        writeStartElement( ElementTags.TABLE );
+        writeAddAttribute( ElementTags.COLUMNS, "1" );
+        writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_CENTER );
+        writeAddAttribute( ElementTags.TABLEFITSPAGE, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.CELLSFITPAGE, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.CELLPADDING, "10" );
+        writeAddAttribute( ElementTags.WIDTH, "100.0%" );
+
+        writeStartElement( ElementTags.ROW );
+
+        writeStartElement( ElementTags.CELL );
+        writeAddAttribute( ElementTags.LEFT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.RIGHT, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.TOP, Boolean.TRUE.toString() );
+        writeAddAttribute( ElementTags.BOTTOM, Boolean.TRUE.toString() );
+
+        actionContext.setAction( SinkActionContext.VERBATIM );
+    }
+
+    // ----------------------------------------------------------------------
+    // Figures
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        writeEndElement(); // ElementTags.IMAGE
+
+        writeEndElement(); // ElementTags.CHUNK
+
+        actionContext.release();
+
+        figureDefined = false;
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        figureDefined = true;
+
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        writeStartElement( ElementTags.IMAGE );
+
+        actionContext.setAction( SinkActionContext.FIGURE );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption()
+    {
+        actionContext.setAction( SinkActionContext.FIGURE_CAPTION );
+    }
+
+    /**
+     * If the <code>name</code> is a relative link, the internal link will used a System property <code>itext.basedir</code>,
+     * or the class loader.
+     * {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        String urlName = null;
+        File nameFile = null;
+        if ( ( name.toLowerCase( Locale.ENGLISH ).startsWith( "http://" ) )
+            || ( name.toLowerCase( Locale.ENGLISH ).startsWith( "https://" ) ) )
+        {
+            urlName = name;
+        }
+        else
+        {
+            if ( System.getProperty( "itext.basedir" ) != null )
+            {
+                try
+                {
+                    nameFile = new File( System.getProperty( "itext.basedir" ), name );
+                    urlName = nameFile.toURI().toURL().toString();
+                }
+                catch ( MalformedURLException e )
+                {
+                    getLog().error( "MalformedURLException: " + e.getMessage(), e );
+                }
+            }
+            else
+            {
+                if ( getClassLoader() != null )
+                {
+                    if ( getClassLoader().getResource( name ) != null )
+                    {
+                        urlName = getClassLoader().getResource( name ).toString();
+                    }
+                }
+                else
+                {
+                    if ( ITextSink.class.getClassLoader().getResource( name ) != null )
+                    {
+                        urlName = ITextSink.class.getClassLoader().getResource( name ).toString();
+                    }
+                }
+            }
+        }
+
+        if ( urlName == null )
+        {
+            String msg =
+                "No image '" + name
+                    + "' found in the class loader. Try to call setClassLoader(ClassLoader) before.";
+            logMessage( "imageNotFound", msg );
+
+            return;
+        }
+
+        if ( nameFile != null && !nameFile.exists() )
+        {
+            String msg = "No image '" + nameFile + "' found in your system, check the path.";
+            logMessage( "imageNotFound", msg );
+
+            return;
+        }
+
+        boolean figureCalled = figureDefined;
+        if ( !figureCalled )
+        {
+            figure();
+        }
+
+        float width = 0;
+        float height = 0;
+        try
+        {
+            Image image = Image.getInstance( new URL( urlName ) );
+            image.scaleToFit( ITextUtil.getDefaultPageSize().width() / 2, ITextUtil.getDefaultPageSize().height() / 2 );
+            width = image.plainWidth();
+            height = image.plainHeight();
+        }
+        catch ( BadElementException e )
+        {
+            getLog().error( "BadElementException: " + e.getMessage(), e );
+        }
+        catch ( MalformedURLException e )
+        {
+            getLog().error( "MalformedURLException: " + e.getMessage(), e );
+        }
+        catch ( IOException e )
+        {
+            getLog().error( "IOException: " + e.getMessage(), e );
+        }
+
+        writeAddAttribute( ElementTags.URL, urlName );
+        writeAddAttribute( ElementTags.ALIGN, ElementTags.ALIGN_MIDDLE );
+        writeAddAttribute( ElementTags.PLAINWIDTH, String.valueOf( width ) );
+        writeAddAttribute( ElementTags.PLAINHEIGHT, String.valueOf( height ) );
+
+        actionContext.setAction( SinkActionContext.FIGURE_GRAPHICS );
+
+        if ( !figureCalled )
+        {
+            figure_();
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Fonts
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        font.removeBold();
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        font.addBold();
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        font.removeItalic();
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        font.addItalic();
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        font.setMonoSpaced( false );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        font.setMonoSpaced( true );
+    }
+
+    // ----------------------------------------------------------------------
+    // Links
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        writeEndElement(); // ElementTags.ANCHOR
+
+        font.setColor( Color.BLACK );
+        font.removeUnderlined();
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Link name cannot be null!" );
+        }
+
+        font.setColor( Color.BLUE );
+        font.addUnderlined();
+
+        writeStartElement( ElementTags.ANCHOR );
+        if ( StringUtils.isNotEmpty( name )&& name.startsWith( "#" ) && StringUtils.isNotEmpty( header.getTitle() ))
+        {
+            name = "#" + DoxiaUtils.encodeId( header.getTitle(), true ) + "_" + name.substring( 1 );
+        }
+        writeAddAttribute( ElementTags.REFERENCE, HtmlTools.escapeHTML( name ) );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        actionContext.setAction( SinkActionContext.LINK );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor_()
+    {
+        if ( !anchorDefined )
+        {
+            // itext needs a defined local destination, we put an invisible text
+            writeAddAttribute( ElementTags.BLUE, "255" );
+            writeAddAttribute( ElementTags.GREEN, "255" );
+            writeAddAttribute( ElementTags.RED, "255" );
+
+            write( "_" );
+        }
+
+        anchorDefined = false;
+
+        writeEndElement(); // ElementTags.ANCHOR
+
+        actionContext.release();
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "Anchor name cannot be null!" );
+        }
+
+        if ( StringUtils.isNotEmpty( header.getTitle() ) )
+        {
+            name = header.getTitle() + "_" + name;
+        }
+        String id = name;
+
+        if ( !DoxiaUtils.isValidId( id ) )
+        {
+            id = DoxiaUtils.encodeId( name, true );
+
+            String msg = "Modified invalid link: '" + name + "' to '" + id + "'";
+            logMessage( "modifiedLink", msg );
+        }
+
+        writeStartElement( ElementTags.ANCHOR );
+        writeAddAttribute( ElementTags.NAME, id );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+
+        actionContext.setAction( SinkActionContext.ANCHOR );
+    }
+
+    // ----------------------------------------------------------------------
+    // Misc
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        // Special case for the header
+        if ( ( actionContext.getCurrentAction() == SinkActionContext.AUTHOR )
+            || ( actionContext.getCurrentAction() == SinkActionContext.DATE )
+            || ( actionContext.getCurrentAction() == SinkActionContext.TITLE ) )
+        {
+            return;
+        }
+
+        writeStartElement( ElementTags.NEWLINE );
+        writeEndElement();
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        write( " " );
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        writeStartElement( ElementTags.NEWPAGE );
+        writeEndElement();
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        writeStartElement( ElementTags.PARAGRAPH );
+        writeAddAttribute( ElementTags.BLUE, "255" );
+        writeAddAttribute( ElementTags.GREEN, "255" );
+        writeAddAttribute( ElementTags.RED, "255" );
+        write( "_" );
+        writeEndElement();
+
+        writeStartElement( ElementTags.PARAGRAPH );
+        writeStartElement( ElementTags.HORIZONTALRULE );
+        writeEndElement();
+        writeEndElement();
+    }
+
+    // ----------------------------------------------------------------------
+    // Text
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+        write( text, false );
+
+        writeEndElement(); // ElementTags.CHUNK
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        if ( StringUtils.isEmpty( text ) )
+        {
+            return;
+        }
+
+        switch ( actionContext.getCurrentAction() )
+        {
+            case SinkActionContext.AUTHOR:
+                header.addAuthor( text );
+                break;
+
+            case SinkActionContext.DATE:
+                header.setDate( text );
+                break;
+
+            case SinkActionContext.TITLE:
+                header.setTitle( text );
+                break;
+
+            case SinkActionContext.TABLE_CAPTION:
+                this.tableCaptionXMLWriter.writeText( text );
+                break;
+
+            case SinkActionContext.VERBATIM:
+                // Used to preserve indentation and formating
+                LineNumberReader lnr = new LineNumberReader( new StringReader( text ) );
+                String line;
+                try
+                {
+                    while ( ( line = lnr.readLine() ) != null )
+                    {
+                        writeStartElement( ElementTags.CHUNK );
+                        writeAddAttribute( ElementTags.FONT, font.getFontName() );
+                        writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+                        writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+                        writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+                        writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+                        writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+                        write( "<![CDATA[", true );
+                        // Special case
+                        line = StringUtils.replace( line, "<![CDATA[", "< ![CDATA[" );
+                        line = StringUtils.replace( line, "]]>", "]] >" );
+                        write( line, true, false );
+                        write( "]]>", true );
+
+                        writeEndElement();
+                        lineBreak();
+                    }
+                }
+                catch ( IOException e )
+                {
+                    throw new RuntimeException( "IOException: ", e );
+                }
+                break;
+
+            case SinkActionContext.FIGURE_CAPTION:
+                writeAddAttribute( ElementTags.ALT, text );
+                break;
+
+            case SinkActionContext.SECTION_TITLE:
+            case SinkActionContext.SECTION_1:
+            case SinkActionContext.SECTION_2:
+            case SinkActionContext.SECTION_3:
+            case SinkActionContext.SECTION_4:
+            case SinkActionContext.SECTION_5:
+            case SinkActionContext.FIGURE:
+            case SinkActionContext.FIGURE_GRAPHICS:
+            case SinkActionContext.TABLE_ROW:
+            case SinkActionContext.TABLE:
+            case SinkActionContext.HEAD:
+            case SinkActionContext.UNDEFINED:
+                break;
+
+            case SinkActionContext.ANCHOR:
+                anchorDefined = true;
+            case SinkActionContext.PARAGRAPH:
+            case SinkActionContext.LINK:
+            case SinkActionContext.TABLE_CELL:
+            case SinkActionContext.TABLE_HEADER_CELL:
+            case SinkActionContext.DEFINITION:
+            case SinkActionContext.DEFINED_TERM:
+            case SinkActionContext.NUMBERED_LIST_ITEM:
+            case SinkActionContext.LIST_ITEM:
+            case SinkActionContext.SECTION_TITLE_5:
+            case SinkActionContext.SECTION_TITLE_4:
+            case SinkActionContext.SECTION_TITLE_3:
+            case SinkActionContext.SECTION_TITLE_2:
+            case SinkActionContext.SECTION_TITLE_1:
+            default:
+                writeStartElement( ElementTags.CHUNK );
+                writeAddAttribute( ElementTags.FONT, font.getFontName() );
+                writeAddAttribute( ElementTags.SIZE, font.getFontSize() );
+                writeAddAttribute( ElementTags.STYLE, font.getFontStyle() );
+                writeAddAttribute( ElementTags.BLUE, font.getFontColorBlue() );
+                writeAddAttribute( ElementTags.GREEN, font.getFontColorGreen() );
+                writeAddAttribute( ElementTags.RED, font.getFontColorRed() );
+
+                write( text );
+
+                writeEndElement(); // ElementTags.CHUNK
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Unkown events just log a warning message but are ignored otherwise.
+     * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        String msg = "Unknown Sink event: '" + name + "', ignoring!";
+        logMessage( "unknownEvent", msg );
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.actionContext = new SinkActionContext();
+        this.font = new ITextFont();
+        this.header = new ITextHeader();
+
+        this.numberDepth = 1;
+        this.depth = 0;
+        this.tableCaptionWriter = null;
+        this.tableCaptionXMLWriter = null;
+        this.anchorDefined = false;
+        this.figureDefined = false;
+        this.warnMessages = null;
+    }
+
+    /**
+     * Convenience method to write a starting element.
+     *
+     * @param tag the name of the tag
+     */
+    private void writeStartElement( String tag )
+    {
+        if ( tableCaptionXMLWriter == null )
+        {
+            xmlWriter.startElement( tag );
+        }
+        else
+        {
+            tableCaptionXMLWriter.startElement( tag );
+        }
+    }
+
+    /**
+     * Convenience method to write a key-value pair.
+     *
+     * @param key the name of an attribute
+     * @param value the value of an attribute
+     */
+    private void writeAddAttribute( String key, String value )
+    {
+        if ( tableCaptionXMLWriter == null )
+        {
+            xmlWriter.addAttribute( key, value );
+        }
+        else
+        {
+            tableCaptionXMLWriter.addAttribute( key, value );
+        }
+    }
+
+    /**
+     * Convenience method to write a key-value pair.
+     *
+     * @param key the name of an attribute
+     * @param value the value of an attribute
+     */
+    private void writeAddAttribute( String key, int value )
+    {
+        if ( tableCaptionXMLWriter == null )
+        {
+            xmlWriter.addAttribute( key, String.valueOf( value ) );
+        }
+        else
+        {
+            tableCaptionXMLWriter.addAttribute( key, String.valueOf( value ) );
+        }
+    }
+
+    /**
+     * Convenience method to write an end element.
+     */
+    private void writeEndElement()
+    {
+        if ( tableCaptionXMLWriter == null )
+        {
+            xmlWriter.endElement();
+        }
+        else
+        {
+            tableCaptionXMLWriter.endElement();
+        }
+    }
+
+    /**
+     * Convenience method to write a String
+     *
+     * @param aString
+     */
+    protected void write( String aString )
+    {
+        write( aString, false );
+    }
+
+    /**
+     * Convenience method to write a String depending the escapeHtml flag
+     *
+     * @param aString
+     * @param escapeHtml
+     */
+    private void write( String aString, boolean escapeHtml )
+    {
+        write( aString, escapeHtml, true );
+    }
+
+    /**
+     * Convenience method to write a String depending the escapeHtml flag
+     *
+     * @param aString
+     * @param escapeHtml
+     * @param trim
+     */
+    private void write( String aString, boolean escapeHtml, boolean trim )
+    {
+        if ( aString == null )
+        {
+            return;
+        }
+
+        if ( trim )
+        {
+            aString = StringUtils.replace( aString, "\n", "" );
+
+            LineNumberReader lnr = new LineNumberReader( new StringReader( aString ) );
+            StringBuffer sb = new StringBuffer();
+            String line;
+            try
+            {
+                while ( ( line = lnr.readLine() ) != null )
+                {
+                    sb.append( beautifyPhrase( line.trim() ) );
+                    sb.append( " " );
+                }
+
+                aString = sb.toString();
+            }
+            catch ( IOException e )
+            {
+                // nop
+            }
+            if ( aString.trim().length() == 0 )
+            {
+                return;
+            }
+        }
+        if ( escapeHtml )
+        {
+            if ( tableCaptionXMLWriter == null )
+            {
+                xmlWriter.writeMarkup( aString );
+            }
+            else
+            {
+                tableCaptionXMLWriter.writeMarkup( aString );
+            }
+        }
+        else
+        {
+            if ( tableCaptionXMLWriter == null )
+            {
+                xmlWriter.writeText( aString );
+            }
+            else
+            {
+                tableCaptionXMLWriter.writeText( aString );
+            }
+        }
+    }
+
+    /**
+     * Convenience method to return a beautify phrase, i.e. one space between words.
+     *
+     * @param aString
+     * @return a String with only one space between words
+     */
+    private static String beautifyPhrase( String aString )
+    {
+        String[] strings = StringUtils.split( aString, " " );
+        StringBuffer sb = new StringBuffer();
+        for ( int i = 0; i < strings.length; i++ )
+        {
+            if ( strings[i].trim().length() != 0 )
+            {
+                sb.append( strings[i].trim() );
+                sb.append( " " );
+            }
+        }
+
+        return sb.toString().trim();
+    }
+
+    private void startChunk( String fontName, int fontSize, String fontStyle, int fontColorBlue, int fontColorGreen,
+                             int fontColorRed, String localDestination )
+    {
+        writeStartElement( ElementTags.CHUNK );
+        writeAddAttribute( ElementTags.FONT, fontName );
+        writeAddAttribute( ElementTags.SIZE, fontSize );
+        writeAddAttribute( ElementTags.STYLE, fontStyle );
+        writeAddAttribute( ElementTags.BLUE, fontColorBlue );
+        writeAddAttribute( ElementTags.GREEN, fontColorGreen );
+        writeAddAttribute( ElementTags.RED, fontColorRed );
+//        writeAddAttribute( ElementTags.LOCALDESTINATION, localDestination );
+    }
+
+    /**
+     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
+     *
+     * @param key not null
+     * @param msg not null
+     * @see #close()
+     * @since 1.1.1
+     */
+    private void logMessage( String key, String msg )
+    {
+        msg = "[iText Sink] " + msg;
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( msg );
+
+            return;
+        }
+
+        if ( warnMessages == null )
+        {
+            warnMessages = new HashMap();
+        }
+
+        Set set = (Set) warnMessages.get( key );
+        if ( set == null )
+        {
+            set = new TreeSet();
+        }
+        set.add( msg );
+        warnMessages.put( key, set );
+    }
+}
diff --git a/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextSinkFactory.java b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextSinkFactory.java
new file mode 100644
index 0000000..43621db
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextSinkFactory.java
@@ -0,0 +1,73 @@
+package org.apache.maven.doxia.module.itext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractTextSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.util.WriterFactory;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+
+/**
+ * IText implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ITextSinkFactory.java 785531 2009-06-17 09:47:59Z ltheussl $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="itext"
+ */
+public class ITextSinkFactory
+    extends AbstractTextSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        return new ITextSink( writer, encoding );
+    }
+
+    /**
+     * createSink.
+     *
+     * @param writer a {@link java.io.Writer} object.
+     * @return a {@link org.apache.maven.doxia.sink.Sink} object.
+     */
+    public Sink createSink( Writer writer )
+    {
+        // TODO: should this method be deprecated?
+        return createSink( writer, WriterFactory.UTF_8 );
+    }
+
+    /**
+     * Create a <code>Sink</code> into a PrettyPrintXMLWriter.
+     *
+     * @param xmlWriter not null XML writer to write the result.
+     * @return a <code>Sink</code> instance.
+     */
+    public Sink createSink( PrettyPrintXMLWriter xmlWriter )
+    {
+        if ( xmlWriter == null )
+        {
+            throw new IllegalArgumentException( "xmlWriter could not be null." );
+        }
+
+        return new ITextSink( xmlWriter );
+    }
+}
diff --git a/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextUtil.java b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextUtil.java
new file mode 100644
index 0000000..4363413
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/ITextUtil.java
@@ -0,0 +1,181 @@
+package org.apache.maven.doxia.module.itext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.xml.XmlToHtml;
+import com.lowagie.text.xml.XmlToPdf;
+import com.lowagie.text.xml.XmlToRtf;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.util.Locale;
+
+/**
+ * A set of util methods for the <code>iText</code> framework
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ITextUtil.java 1003453 2010-10-01 09:45:16Z ltheussl $
+ */
+public class ITextUtil
+{
+    /**
+     * Set the default page size for the document depending the user's country.
+     * TODO Maybe more generic?
+     *
+     * @return the page size
+     * @see com.lowagie.text.PageSize
+     */
+    public static Rectangle getDefaultPageSize()
+    {
+        String defaultCountry = Locale.getDefault().getCountry();
+        if ( defaultCountry != null
+            && ( defaultCountry.equals( Locale.US.getCountry() )
+                            || defaultCountry.equals( Locale.CANADA.getCountry() ) ) )
+        {
+            return PageSize.LETTER;
+        }
+
+        return PageSize.A4;
+    }
+
+    /**
+     * Return a page size as String.
+     *
+     * @param rect a Rectangle defined in {@link PageSize}.
+     * @return a page size as String or A4 if not found.
+     * @see com.lowagie.text.PageSize
+     */
+    public static String getPageSize( Rectangle rect )
+    {
+        Field[] fields = PageSize.class.getFields();
+        for ( int i = 0; i < fields.length; i++ )
+        {
+            Field currentField = fields[i];
+            try
+            {
+                if ( currentField.getType().equals( Rectangle.class ) )
+                {
+                    Rectangle fPageSize = (Rectangle) currentField.get( null );
+                    if ( ( rect.width() == fPageSize.width() ) && ( rect.height() == fPageSize.height() ) )
+                    {
+                        return currentField.getName();
+                    }
+                }
+            }
+            catch ( Exception e )
+            {
+                // nop
+            }
+        }
+
+        return "A4";
+    }
+
+    /**
+     * Return <code>true</code> if the page size is supported by {@link PageSize} class, <code>false</code> otherwise.
+     *
+     * @param aPageSize a page size
+     * @return <code>true</code> if the page size is supported, <code>false</code> otherwise
+     * @see com.lowagie.text.PageSize
+     */
+    public static boolean isPageSizeSupported( String aPageSize )
+    {
+        Field[] fields = PageSize.class.getFields();
+        for ( int i = 0; i < fields.length; i++ )
+        {
+            Field currentField = fields[i];
+            if ( ( currentField.getName().equalsIgnoreCase( aPageSize ) )
+                && ( currentField.getType().equals( Rectangle.class ) ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Parse an iText XML from the specified <CODE>InputStream</CODE>, writing an Pdf document
+     * specified <CODE>OutputStream</CODE>.
+     *
+     * @param is the <CODE>InputStream</CODE> from which the XML is read.
+     * @param os the <CODE>OutputStream</CODE> to which the result as Pdf is written.
+     * @see com.lowagie.text.xml.XmlToPdf
+     */
+    public static void writePdf( InputStream is, OutputStream os )
+    {
+        try
+        {
+            XmlToPdf x = new XmlToPdf();
+
+            x.parse( is, os );
+        }
+        catch ( DocumentException e )
+        {
+            throw new RuntimeException( "DocumentException : " + e.getMessage(), e );
+        }
+    }
+
+    /**
+     * Parse an iText XML from the specified <CODE>InputStream</CODE>, writing an rtf document
+     * specified <CODE>OutputStream</CODE>.
+     *
+     * @param is the <CODE>InputStream</CODE> from which the XML is read.
+     * @param os the <CODE>OutputStream</CODE> to which the result as RTF is written.
+     * @see com.lowagie.text.xml.XmlToRtf
+     */
+    public static void writeRtf( InputStream is, OutputStream os )
+    {
+        try
+        {
+            XmlToRtf x = new XmlToRtf();
+            x.parse( is, os );
+        }
+        catch ( DocumentException e )
+        {
+            throw new RuntimeException( "DocumentException : " + e.getMessage(), e );
+        }
+    }
+
+    /**
+     * Parse an iText XML from the specified <CODE>InputStream</CODE>, writing an html document
+     * specified <CODE>OutputStream</CODE>.
+     *
+     * @param is the <CODE>InputStream</CODE> from which the XML is read.
+     * @param os the <CODE>OutputStream</CODE> to which the result as Html is written.
+     * @see com.lowagie.text.xml.XmlToHtml
+     */
+    public static void writeHtml( InputStream is, OutputStream os )
+    {
+        try
+        {
+            XmlToHtml x = new XmlToHtml();
+            x.parse( is, os );
+        }
+        catch ( DocumentException e )
+        {
+            throw new RuntimeException( "DocumentException : " + e.getMessage(), e );
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/SinkActionContext.java b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/SinkActionContext.java
new file mode 100644
index 0000000..2d36edd
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/main/java/org/apache/maven/doxia/module/itext/SinkActionContext.java
@@ -0,0 +1,159 @@
+package org.apache.maven.doxia.module.itext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Stack;
+
+/**
+ * <p>SinkActionContext class.</p>
+ *
+ * @author Jason van Zyl
+ * @version $Id: SinkActionContext.java 785531 2009-06-17 09:47:59Z ltheussl $
+ */
+public class SinkActionContext
+{
+    /** Constant <code>TITLE=0</code> */
+    public static final int TITLE = 0;
+    /** Constant <code>AUTHOR=1</code> */
+    public static final int AUTHOR = 1;
+    /** Constant <code>DATE=2</code> */
+    public static final int DATE = 2;
+    /** Constant <code>HEAD=3</code> */
+    public static final int HEAD = 3;
+    /** Constant <code>BODY=4</code> */
+    public static final int BODY = 4;
+
+    /** Constant <code>SECTION_TITLE=10</code> */
+    public static final int SECTION_TITLE = 10;
+    /** Constant <code>SECTION_TITLE_1=11</code> */
+    public static final int SECTION_TITLE_1 = 11;
+    /** Constant <code>SECTION_TITLE_2=12</code> */
+    public static final int SECTION_TITLE_2 = 12;
+    /** Constant <code>SECTION_TITLE_3=13</code> */
+    public static final int SECTION_TITLE_3 = 13;
+    /** Constant <code>SECTION_TITLE_4=14</code> */
+    public static final int SECTION_TITLE_4 = 14;
+    /** Constant <code>SECTION_TITLE_5=15</code> */
+    public static final int SECTION_TITLE_5 = 15;
+
+    /** Constant <code>SECTION_1=20</code> */
+    public static final int SECTION_1 = 20;
+    /** Constant <code>SECTION_2=21</code> */
+    public static final int SECTION_2 = 21;
+    /** Constant <code>SECTION_3=22</code> */
+    public static final int SECTION_3 = 22;
+    /** Constant <code>SECTION_4=23</code> */
+    public static final int SECTION_4 = 23;
+    /** Constant <code>SECTION_5=24</code> */
+    public static final int SECTION_5 = 24;
+
+    /** Constant <code>DEFINITION_LIST=30</code> */
+    public static final int DEFINITION_LIST = 30;
+    /** Constant <code>DEFINITION_LIST_ITEM=31</code> */
+    public static final int DEFINITION_LIST_ITEM = 31;
+    /** Constant <code>DEFINED_TERM=32</code> */
+    public static final int DEFINED_TERM = 32;
+
+    /** Constant <code>LIST_ITEM=40</code> */
+    public static final int LIST_ITEM = 40;
+    /** Constant <code>NUMBERED_LIST_ITEM=41</code> */
+    public static final int NUMBERED_LIST_ITEM = 41;
+    /** Constant <code>NUMBERED_LIST=42</code> */
+    public static final int NUMBERED_LIST = 42;
+    /** Constant <code>DEFINITION=43</code> */
+    public static final int DEFINITION = 43;
+    /** Constant <code>PARAGRAPH=44</code> */
+    public static final int PARAGRAPH = 44;
+    /** Constant <code>LIST=45</code> */
+    public static final int LIST = 45;
+
+    /** Constant <code>TABLE=50</code> */
+    public static final int TABLE = 50;
+    /** Constant <code>TABLE_CAPTION=51</code> */
+    public static final int TABLE_CAPTION = 51;
+    /** Constant <code>TABLE_CELL=52</code> */
+    public static final int TABLE_CELL = 52;
+    /** Constant <code>TABLE_HEADER_CELL=53</code> */
+    public static final int TABLE_HEADER_CELL = 53;
+    /** Constant <code>TABLE_ROW=54</code> */
+    public static final int TABLE_ROW = 54;
+    /** Constant <code>TABLE_ROWS=55</code> */
+    public static final int TABLE_ROWS = 55;
+
+    /** Constant <code>VERBATIM=60</code> */
+    public static final int VERBATIM = 60;
+
+    /** Constant <code>FIGURE=70</code> */
+    public static final int FIGURE = 70;
+    /** Constant <code>FIGURE_CAPTION=71</code> */
+    public static final int FIGURE_CAPTION = 71;
+    /** Constant <code>FIGURE_GRAPHICS=72</code> */
+    public static final int FIGURE_GRAPHICS = 72;
+
+    /** Constant <code>LINK=80</code> */
+    public static final int LINK = 80;
+    /** Constant <code>ANCHOR=81</code> */
+    public static final int ANCHOR = 81;
+    /** Constant <code>UNDEFINED=82</code> */
+    public static final int UNDEFINED = 82;
+
+    private Stack stack = new Stack();
+
+    private int currentAction;
+
+    /**
+     * <p>Getter for the field <code>currentAction</code>.</p>
+     *
+     * @return a int.
+     */
+    public int getCurrentAction()
+    {
+        //return currentAction;
+        if ( stack.empty() )
+        {
+            return UNDEFINED;
+        }
+        else
+        {
+            return ( (Integer) stack.peek() ).intValue();
+        }
+    }
+
+    /**
+     * release.
+     */
+    public void release()
+    {
+        //currentAction = -1;
+        stack.pop();
+    }
+
+    /**
+     * setAction.
+     *
+     * @param action a int.
+     */
+    public void setAction( int action )
+    {
+        //currentAction = action;
+
+        stack.push( new Integer( action ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-itext/src/site/site.xml b/doxia-modules/doxia-module-itext/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-itext/src/test/java/org/apache/maven/doxia/module/itext/ITextSinkTestCase.java b/doxia-modules/doxia-module-itext/src/test/java/org/apache/maven/doxia/module/itext/ITextSinkTestCase.java
new file mode 100644
index 0000000..3e24d07
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/test/java/org/apache/maven/doxia/module/itext/ITextSinkTestCase.java
@@ -0,0 +1,155 @@
+package org.apache.maven.doxia.module.itext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.Writer;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import org.apache.maven.doxia.AbstractModuleTest;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkTestDocument;
+
+/**
+ * <code>iText Sink</code> Test case.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ITextSinkTestCase.java 781348 2009-06-03 11:57:22Z vsiveton $
+ */
+public class ITextSinkTestCase
+    extends AbstractModuleTest
+{
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "xml";
+    }
+
+    /** {@inheritDoc} */
+    protected String getOutputDir()
+    {
+        return "sink/";
+    }
+
+    /**
+     * Convenience method
+     *
+     * @param prefix
+     * @param suffix
+     * @return the input file
+     */
+    protected File getGeneratedFile( String prefix, String suffix )
+    {
+        File outputDirectory = new File( getBasedir(), outputBaseDir() + getOutputDir() );
+        if ( !outputDirectory.exists() )
+        {
+            outputDirectory.mkdirs();
+        }
+
+        return new File( outputDirectory, prefix + "." + suffix );
+    }
+
+    protected Sink createSink( Writer writer )
+    {
+        ITextSink sink = new ITextSink( writer );
+
+        sink.setClassLoader( new URLClassLoader(
+            new URL[] { ITextSinkTestCase.class.getResource( "/images/" ) } ) );
+
+        return sink;
+    }
+
+    /**
+     * Test PDF generation
+     *
+     * @throws Exception
+     */
+    public void testGeneratingPDFFromITextXml()
+        throws Exception
+    {
+        File f = new File( getBasedir(), "src/test/resources/itext/itext.xml" );
+
+        ITextUtil.writePdf( new FileInputStream( f ),
+                            new FileOutputStream( getGeneratedFile( "test_itext", "pdf" ) ) );
+    }
+
+    /**
+     * Generate a pdf and a rtf from the standart test model.
+     *
+     * @throws Exception if any
+     */
+    public void testModel()
+        throws Exception
+    {
+        Sink sink = createSink( getXmlTestWriter( "test_model", "xml" ) );
+
+        SinkTestDocument.generate( sink );
+
+        sink.close();
+
+        ITextUtil.writePdf( new FileInputStream( getGeneratedFile( "test_model", "xml" ) ),
+                            new FileOutputStream( getGeneratedFile( "test_model", "pdf" ) ) );
+        ITextUtil.writeRtf( new FileInputStream( getGeneratedFile( "test_model", "xml" ) ),
+                            new FileOutputStream( getGeneratedFile( "test_model", "rtf" ) ) );
+    }
+
+    /**
+     * Test empty anchor DOXIA-329
+     * @throws Exception if any
+     */
+    public void testEmptyAnchor()
+        throws Exception
+    {
+        Sink sink = createSink( getXmlTestWriter( "empty_anchor", "xml" ) );
+
+        SinkTestDocument.generateHead( sink );
+
+        sink.body();
+
+        sink.anchor( "empty_local_anchor" );
+        sink.anchor_();
+        sink.lineBreak();
+        sink.link( "#empty_local_anchor" );
+        sink.text( "link to empty local anchor" );
+        sink.link_();
+
+        sink.lineBreak();
+
+        sink.anchor( "defined_local_anchor" );
+        sink.text( "defined local anchor" );
+        sink.anchor_();
+        sink.lineBreak();
+        sink.link( "#defined_local_anchor" );
+        sink.text( "link to defined local anchor" );
+        sink.link_();
+
+        sink.body_();
+
+        sink.flush();
+        sink.close();
+
+        ITextUtil.writePdf( new FileInputStream( getGeneratedFile( "empty_anchor", "xml" ) ),
+                            new FileOutputStream( getGeneratedFile( "empty_anchor", "pdf" ) ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-itext/src/test/java/org/apache/maven/doxia/module/itext/ITextUtilTest.java b/doxia-modules/doxia-module-itext/src/test/java/org/apache/maven/doxia/module/itext/ITextUtilTest.java
new file mode 100644
index 0000000..52a2133
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/test/java/org/apache/maven/doxia/module/itext/ITextUtilTest.java
@@ -0,0 +1,94 @@
+package org.apache.maven.doxia.module.itext;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+import com.lowagie.text.PageSize;
+
+/**
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: ITextUtilTest.java 782608 2009-06-08 12:49:39Z vsiveton $
+ */
+public class ITextUtilTest
+    extends TestCase
+{
+    public void testGetDefaultPageSize()
+        throws Exception
+    {
+        Locale oldLocale = Locale.getDefault();
+
+        try
+        {
+            Locale.setDefault( Locale.US );
+            assertEquals( PageSize.LETTER, ITextUtil.getDefaultPageSize() );
+
+            Locale.setDefault( Locale.CANADA );
+            assertEquals( PageSize.LETTER, ITextUtil.getDefaultPageSize() );
+
+            Locale.setDefault( Locale.FRANCE );
+            assertEquals( PageSize.A4, ITextUtil.getDefaultPageSize() );
+        }
+        finally
+        {
+            Locale.setDefault( oldLocale );
+        }
+    }
+
+    public void testGetPageSize()
+        throws Exception
+    {
+        assertEquals( "A0", ITextUtil.getPageSize( PageSize.A0 ) );
+        assertEquals( "A1", ITextUtil.getPageSize( PageSize.A1 ) );
+        assertEquals( "A2", ITextUtil.getPageSize( PageSize.A2 ) );
+        assertEquals( "A3", ITextUtil.getPageSize( PageSize.A3 ) );
+        assertEquals( "A4", ITextUtil.getPageSize( PageSize.A4 ) );
+        assertEquals( "A5", ITextUtil.getPageSize( PageSize.A5 ) );
+        assertEquals( "A6", ITextUtil.getPageSize( PageSize.A6 ) );
+        assertEquals( "A7", ITextUtil.getPageSize( PageSize.A7 ) );
+        assertEquals( "A8", ITextUtil.getPageSize( PageSize.A8 ) );
+        assertEquals( "A9", ITextUtil.getPageSize( PageSize.A9 ) );
+        assertEquals( "A10", ITextUtil.getPageSize( PageSize.A10 ) );
+        assertEquals( "LETTER", ITextUtil.getPageSize( PageSize.LETTER ) );
+        assertEquals( "LEGAL", ITextUtil.getPageSize( PageSize.LEGAL ) );
+    }
+
+    public void testIsPageSupported()
+        throws Exception
+    {
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A0" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A1" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A2" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A3" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A4" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A5" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A6" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A7" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A8" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A9" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "A10" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "LETTER" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "letter" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "LEGAL" ) );
+        assertEquals( true, ITextUtil.isPageSizeSupported( "legal" ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Context_Menu.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Context_Menu.png
new file mode 100644
index 0000000..1ed8df2
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Context_Menu.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Generate_Site.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Generate_Site.png
new file mode 100644
index 0000000..4574a5f
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Generate_Site.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Open_Project.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Open_Project.png
new file mode 100644
index 0000000..3a00243
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Open_Project.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Project_Opened.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Project_Opened.png
new file mode 100644
index 0000000..4e2a05f
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Project_Opened.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Refresh_Project.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Refresh_Project.png
new file mode 100644
index 0000000..f32514d
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Refresh_Project.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Refreshed_Context_Menu.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Refreshed_Context_Menu.png
new file mode 100644
index 0000000..df1e73c
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Refreshed_Context_Menu.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Run_Build_Main_Project.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Run_Build_Main_Project.png
new file mode 100644
index 0000000..d9dca62
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Run_Build_Main_Project.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Run_Clean_Main_Project.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Run_Clean_Main_Project.png
new file mode 100644
index 0000000..8bebfbb
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Run_Clean_Main_Project.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/Window_Files.png b/doxia-modules/doxia-module-itext/src/test/resources/images/Window_Files.png
new file mode 100644
index 0000000..28a0c55
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/Window_Files.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/images/figure.png b/doxia-modules/doxia-module-itext/src/test/resources/images/figure.png
new file mode 100644
index 0000000..4e2a05f
Binary files /dev/null and b/doxia-modules/doxia-module-itext/src/test/resources/images/figure.png differ
diff --git a/doxia-modules/doxia-module-itext/src/test/resources/itext/itext.xml b/doxia-modules/doxia-module-itext/src/test/resources/itext/itext.xml
new file mode 100644
index 0000000..e7ca4b1
--- /dev/null
+++ b/doxia-modules/doxia-module-itext/src/test/resources/itext/itext.xml
@@ -0,0 +1,1937 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<itext creationdate="Sun Feb 08 12:53:24 CET 2004" producer="iTextXML by lowagie.com">
+  <paragraph leading="18.0" font="unknown" align="Default">
+    Please visit my 
+    <anchor leading="18.0" font="Helvetica" size="12.0" fontstyle="normal, underline" red="0" green="0" blue="255" name="top" reference="http://www.lowagie.com/iText/">
+      <chunk font="Helvetica" size="12.0" fontstyle="normal, underline" red="0" green="0" blue="255">website (external reference)</chunk>
+    </anchor>
+  </paragraph>
+  <paragraph leading="18.0" font="unknown" align="Default">
+    These are some special characters: <, >, &, " and '
+  </paragraph>
+  <paragraph leading="18.0" font="unknown" align="Default">
+    some books I really like:
+  </paragraph>
+  <list numbered="true" symbolindent="15" font="unknown">
+    <listitem leading="18.0" font="Times" size="12.0" align="Default" indentationleft="15.0">
+      <chunk font="Times" size="12.0">When Harlie was one</chunk>
+      <chunk font="Times" size="11.0" fontstyle="normal" subsupscript="8.0"> by David Gerrold</chunk>
+    </listitem>
+    <listitem leading="18.0" font="Times" size="12.0" align="Default" indentationleft="15.0">
+      <chunk font="Times" size="12.0">The World according to Garp</chunk>
+      <chunk font="Times" size="11.0" fontstyle="normal" subsupscript="-8.0"> by John Irving</chunk>
+    </listitem>
+    <listitem leading="18.0" font="Times" size="12.0" align="Default" indentationleft="15.0">
+      <chunk font="Times" size="12.0">Decamerone</chunk>
+      <chunk font="Times" size="11.0" fontstyle="normal"> by Giovanni Boccaccio</chunk>
+    </listitem>
+  </list>
+  <paragraph leading="18.0" font="unknown" align="Default">
+    some movies I really like:
+    <list numbered="false" symbolindent="10" listsymbol="-" font="unknown">
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Wild At Heart
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Casablanca
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        When Harry met Sally
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        True Romance
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Le mari de la coiffeuse
+      </listitem>
+    </list>
+  </paragraph>
+  <paragraph leading="18.0" font="unknown" align="Default">
+    Some authors I really like:
+  </paragraph>
+  <list numbered="false" symbolindent="20" first="-2" listsymbol="*" font="Helvetica" size="20.0" fontstyle="normal">
+    <listitem leading="18.0" font="unknown" align="Default" indentationleft="20.0">
+      Isaac Asimov
+    </listitem>
+    <list numbered="true" symbolindent="10" indentationleft="20.0" font="Helvetica" size="8.0">
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        The Foundation Trilogy
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        The Complete Robot
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Caves of Steel
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        The Naked Sun
+      </listitem>
+    </list>
+    <listitem leading="18.0" font="unknown" align="Default" indentationleft="20.0">
+      John Irving
+    </listitem>
+    <list numbered="true" symbolindent="10" indentationleft="20.0" font="Helvetica" size="8.0">
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        The World according to Garp
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Hotel New Hampshire
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        A prayer for Owen Meany
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Widow for a year
+      </listitem>
+    </list>
+    <listitem leading="18.0" font="unknown" align="Default" indentationleft="20.0">
+      Kurt Vonnegut
+    </listitem>
+    <list numbered="true" symbolindent="10" indentationleft="20.0" font="Helvetica" size="8.0">
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Slaughterhouse 5
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Welcome to the Monkey House
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        The great pianola
+      </listitem>
+      <listitem leading="18.0" font="unknown" align="Default" indentationleft="10.0">
+        Galapagos
+      </listitem>
+    </list>
+  </list>
+  <paragraph leading="18.0" font="unknown" align="Default">
+    <newline />
+<newline />
+
+  </paragraph>
+  <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+    <row>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+        <paragraph leading="18.0" font="unknown" align="Default">
+          header
+        </paragraph>
+      </cell>
+    </row>
+    <row>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+        <paragraph leading="18.0" font="unknown" align="Default">
+          example cell with colspan 1 and rowspan 2
+        </paragraph>
+      </cell>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+        <phrase leading="18.0" font="unknown">
+          1.1
+        </phrase>
+      </cell>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+        <phrase leading="18.0" font="unknown">
+          2.1
+        </phrase>
+      </cell>
+    </row>
+    <row>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+        <phrase leading="18.0" font="unknown">
+          1.2
+        </phrase>
+      </cell>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+        <phrase leading="18.0" font="unknown">
+          2.2
+        </phrase>
+      </cell>
+    </row>
+    <row>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+        <phrase leading="18.0" font="unknown">
+          cell test1
+        </phrase>
+      </cell>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+        <paragraph leading="18.0" font="unknown" align="Default">
+          big cell
+        </paragraph>
+      </cell>
+    </row>
+    <row>
+      <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+        <phrase leading="18.0" font="unknown">
+          cell test2
+        </phrase>
+      </cell>
+    </row>
+  </table>
+  <anchor leading="18.0" font="Helvetica" size="12.0" fontstyle="normal" red="0" green="0" blue="255" reference="#top">
+    <chunk font="Helvetica" size="12.0" fontstyle="normal" red="0" green="0" blue="255">please jump to a local destination</chunk>
+  </anchor>
+  <paragraph leading="18.0" font="unknown" align="Default">
+    <newline />
+<newline />
+
+  </paragraph>
+  <chapter numberdepth="1" depth="1" indent="0.0">
+    <title leading="36.0" align="Default" font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">
+      <chunk font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">This is chapter 1</chunk>
+    </title>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 1 in chapter 1</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 2 in chapter 1</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 3 in chapter 1</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blaah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+        <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                header
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                example cell with colspan 1 and rowspan 2
+              </paragraph>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.1
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.2
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.2
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                big cell
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test2
+              </phrase>
+            </cell>
+          </row>
+        </table>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+  </chapter>
+  <chapter numberdepth="1" depth="1" indent="0.0">
+    <title leading="36.0" align="Default" font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">
+      <chunk font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">This is chapter 2</chunk>
+    </title>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 1 in chapter 2</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 2 in chapter 2</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 3 in chapter 2</chunk>
+      </title>
+      <paragraph leading="18.0" font="unknown" align="Default">
+        blah blah blah blah blah blah blaah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blaah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+        <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                header
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                example cell with colspan 1 and rowspan 2
+              </paragraph>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.1
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.2
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.2
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                big cell
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test2
+              </phrase>
+            </cell>
+          </row>
+        </table>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+  </chapter>
+  <chapter numberdepth="1" depth="1" indent="0.0">
+    <title leading="36.0" align="Default" font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">
+      <chunk font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">This is chapter 3</chunk>
+    </title>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 1 in chapter 3</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 2 in chapter 3</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <paragraph leading="18.0" font="unknown" align="Default">
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blaah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              header
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              example cell with colspan 1 and rowspan 2
+            </paragraph>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.1
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.2
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.2
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              big cell
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test2
+            </phrase>
+          </cell>
+        </row>
+      </table>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 3 in chapter 3</chunk>
+      </title>
+      <paragraph leading="18.0" font="unknown" align="Default">
+        blah blah blah blah blah blah blaah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blaah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+        <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                header
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                example cell with colspan 1 and rowspan 2
+              </paragraph>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.1
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.2
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.2
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                big cell
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test2
+              </phrase>
+            </cell>
+          </row>
+        </table>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Default">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+  </chapter>
+  <chapter numberdepth="1" depth="1" indent="0.0">
+    <title leading="36.0" align="Default" font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">
+      <chunk font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">This is chapter 4</chunk>
+    </title>
+    <paragraph leading="18.0" font="unknown" align="Justify">
+      blah blah blah blah blah blah blaah blah blah blah blah blah blah
+      blah blah blah blah blah blah blah blah blah blah blah blah blah
+      blah blah blah blah blah blah blah blah blah blah blah blah blah
+      blah blah blah blah blah blah blah blah blah blah blah blah
+    </paragraph>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 1 in chapter 4</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 2 in chapter 4</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <paragraph leading="18.0" font="unknown" align="Justify">
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blaah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              header
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              example cell with colspan 1 and rowspan 2
+            </paragraph>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.1
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.2
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.2
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              big cell
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test2
+            </phrase>
+          </cell>
+        </row>
+      </table>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 3 in chapter 4</chunk>
+      </title>
+      <paragraph leading="18.0" font="unknown" align="Justify">
+        blah blah blah blah blah blah blaah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blaah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+        <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                header
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                example cell with colspan 1 and rowspan 2
+              </paragraph>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.1
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.2
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.2
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                big cell
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test2
+              </phrase>
+            </cell>
+          </row>
+        </table>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+  </chapter>
+  <chapter numberdepth="1" depth="1" indent="0.0">
+    <title leading="36.0" align="Default" font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">
+      <chunk font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">This is chapter 5</chunk>
+    </title>
+    <paragraph leading="18.0" font="unknown" align="Right">
+      blah blah blah blah blah blah blaah blah blah blah blah blah blah
+      blah blah blah blah blah blah blah blah blah blah blah blah blah
+      blah blah blah blah blah blah blah blah blah blah blah blah blah
+      blah blah blah blah blah blah blah blah blah blah blah blah
+    </paragraph>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 1 in chapter 5</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 2 in chapter 5</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <paragraph leading="18.0" font="unknown" align="Center">
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blaah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              header
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              example cell with colspan 1 and rowspan 2
+            </paragraph>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.1
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.2
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.2
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              big cell
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test2
+            </phrase>
+          </cell>
+        </row>
+      </table>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 3 in chapter 5</chunk>
+      </title>
+      <paragraph leading="18.0" font="unknown" align="Right">
+        blah blah blah blah blah blah blaah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Center">
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blaah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+        <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                header
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                example cell with colspan 1 and rowspan 2
+              </paragraph>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.1
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.2
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.2
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                big cell
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test2
+              </phrase>
+            </cell>
+          </row>
+        </table>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Right">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+  </chapter>
+  <chapter numberdepth="1" depth="1" indent="0.0">
+    <title leading="36.0" align="Default" font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">
+      <chunk font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">This is chapter 6</chunk>
+    </title>
+    <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+      <row>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+          <paragraph leading="18.0" font="unknown" align="Default">
+            header
+          </paragraph>
+        </cell>
+      </row>
+      <row>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+          <paragraph leading="18.0" font="unknown" align="Default">
+            example cell with colspan 1 and rowspan 2
+          </paragraph>
+        </cell>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+          <phrase leading="18.0" font="unknown">
+            1.1
+          </phrase>
+        </cell>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+          <phrase leading="18.0" font="unknown">
+            2.1
+          </phrase>
+        </cell>
+      </row>
+      <row>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+          <phrase leading="18.0" font="unknown">
+            1.2
+          </phrase>
+        </cell>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+          <phrase leading="18.0" font="unknown">
+            2.2
+          </phrase>
+        </cell>
+      </row>
+      <row>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+          <phrase leading="18.0" font="unknown">
+            cell test1
+          </phrase>
+        </cell>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+          <paragraph leading="18.0" font="unknown" align="Default">
+            big cell
+          </paragraph>
+        </cell>
+      </row>
+      <row>
+        <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+          <phrase leading="18.0" font="unknown">
+            cell test2
+          </phrase>
+        </cell>
+      </row>
+    </table>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 1 in chapter 6</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 2 in chapter 6</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <paragraph leading="18.0" font="unknown" align="Center">
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blaah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              header
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              example cell with colspan 1 and rowspan 2
+            </paragraph>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.1
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.2
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.2
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              big cell
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test2
+            </phrase>
+          </cell>
+        </row>
+      </table>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 3 in chapter 6</chunk>
+      </title>
+      <paragraph leading="18.0" font="unknown" align="Justify">
+        blah blah blah blah blah blah blaah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Center">
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blaah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+        <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                header
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                example cell with colspan 1 and rowspan 2
+              </paragraph>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.1
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.2
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.2
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                big cell
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test2
+              </phrase>
+            </cell>
+          </row>
+        </table>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+  </chapter>
+  <chapter numberdepth="1" depth="1" indent="0.0">
+    <title leading="36.0" align="Default" font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">
+      <chunk font="Helvetica" size="24.0" fontstyle="normal" red="255" green="0" blue="0">This is chapter 7</chunk>
+    </title>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 1 in chapter 7</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 1</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 2 in chapter 7</chunk>
+      </title>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 2</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <paragraph leading="18.0" font="unknown" align="Center">
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blaah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              header
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              example cell with colspan 1 and rowspan 2
+            </paragraph>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.1
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              1.2
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              2.2
+            </phrase>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test1
+            </phrase>
+          </cell>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+            <paragraph leading="18.0" font="unknown" align="Default">
+              big cell
+            </paragraph>
+          </cell>
+        </row>
+        <row>
+          <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+            <phrase leading="18.0" font="unknown">
+              cell test2
+            </phrase>
+          </cell>
+        </row>
+      </table>
+    </section>
+    <section numberdepth="1" depth="2" indent="0.0">
+      <title leading="30.0" align="Default" font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">
+        <chunk font="Helvetica" size="20.0" fontstyle="normal" red="0" green="0" blue="255">This is section 3 in chapter 7</chunk>
+      </title>
+      <paragraph leading="18.0" font="unknown" align="Justify">
+        blah blah blah blah blah blah blaah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah blah
+        blah blah blah blah blah blah blah blah blah blah blah blah
+      </paragraph>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 1 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Center">
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blaah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+        <table columns="3" width="80.0%" align="Center" cellpadding="5.0" cellspacing="5.0" widths="33.333332;33.333332;33.333332" borderwidth="1.0" left="true" right="true" top="true" bottom="true" red="0" green="0" blue="255">
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="3" header="true" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                header
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" red="255" green="0" blue="0" horizontalalign="Default" verticalalign="Default" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                example cell with colspan 1 and rowspan 2
+              </paragraph>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.1
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                1.2
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                2.2
+              </phrase>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test1
+              </phrase>
+            </cell>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" colspan="2" rowspan="2" leading="18.0">
+              <paragraph leading="18.0" font="unknown" align="Default">
+                big cell
+              </paragraph>
+            </cell>
+          </row>
+          <row>
+            <cell borderwidth="0.5" left="true" right="true" top="true" bottom="true" horizontalalign="Default" verticalalign="Default" leading="18.0">
+              <phrase leading="18.0" font="unknown">
+                cell test2
+              </phrase>
+            </cell>
+          </row>
+        </table>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 2 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+      <section numberdepth="3" depth="3" indent="0.0">
+        <title leading="27.0" align="Default" font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">
+          <chunk font="Helvetica" size="18.0" fontstyle="normal" red="0" green="64" blue="64">This is subsection 3 of section 3</chunk>
+        </title>
+        <paragraph leading="18.0" font="unknown" align="Justify">
+          blah blah blah blah blah blah blaah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah blah
+          blah blah blah blah blah blah blah blah blah blah blah blah
+        </paragraph>
+      </section>
+    </section>
+  </chapter>
+</itext>
diff --git a/doxia-modules/doxia-module-latex/pom.xml b/doxia-modules/doxia-module-latex/pom.xml
new file mode 100644
index 0000000..398182b
--- /dev/null
+++ b/doxia-modules/doxia-module-latex/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-latex</artifactId>
+
+  <name>Doxia :: Latex Module</name>
+  <description>A Doxia module for LaTeX source documents.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-latex/src/main/java/org/apache/maven/doxia/module/latex/LatexSink.java b/doxia-modules/doxia-module-latex/src/main/java/org/apache/maven/doxia/module/latex/LatexSink.java
new file mode 100644
index 0000000..49e5911
--- /dev/null
+++ b/doxia-modules/doxia-module-latex/src/main/java/org/apache/maven/doxia/module/latex/LatexSink.java
@@ -0,0 +1,1459 @@
+package org.apache.maven.doxia.module.latex;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.AbstractTextSink;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.util.DoxiaUtils;
+import org.apache.maven.doxia.util.LineBreaker;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.util.Locale;
+
+/**
+ * Latex Sink implementation.
+ * <br/>
+ * <b>Note</b>: The encoding used is UTF-8.
+ *
+ * @version $Id: LatexSink.java 807174 2009-08-24 12:15:08Z vsiveton $
+ * @since 1.0
+ */
+public class LatexSink
+    extends AbstractTextSink
+{
+    /**
+     * Flag that indicates if the document to be written is only a fragment.
+     *
+     * This implies that <code>\\begin{document}</code>, <code>\\title{..}</code> will not be output.
+     */
+    private final boolean fragmentDocument;
+
+    private boolean ignoreText;
+
+    private final LineBreaker out;
+
+    private final String sinkCommands;
+
+    private final String preamble;
+
+    private boolean titleFlag;
+
+    private int numberedListNesting;
+
+    private boolean verbatimFlag;
+
+    private boolean figureFlag;
+
+    private boolean tableFlag;
+
+    private boolean gridFlag;
+
+    private int[] cellJustif;
+
+    private int cellCount;
+
+    private boolean isTitle;
+
+    private String title;
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * Constructor, initialize the Writer and the variables.
+     *
+     * @param out not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     */
+    protected LatexSink( Writer out )
+    {
+        this( out, null, null );
+    }
+
+    /**
+     * Constructor, initialize the Writer and the variables.
+     *
+     * @param out not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     * @param sinkCommands A String representation of commands that go before \documentclass.
+     * @param preamble A String representation of commands that go between \documentclass and \begin{document}.
+     */
+    protected LatexSink( Writer out, String sinkCommands, String preamble )
+    {
+        this( out, sinkCommands, preamble, false );
+    }
+
+    /**
+     * Constructor, initialize the Writer and the variables.
+     *
+     * @param out not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     * @param sinkCommands A String representation of commands that go before \documentclass.
+     * @param preamble A String representation of commands that go between \documentclass and \begin{document}.
+     * @param fragmentDocument If this receives events that that are only part of a document.
+     * Typically, headers are omitted if this is true.
+     */
+    protected LatexSink( Writer out, String sinkCommands, String preamble, boolean fragmentDocument )
+    {
+        this.out = new LineBreaker( out );
+
+        if ( sinkCommands == null )
+        {
+            sinkCommands = defaultSinkCommands();
+        }
+        if ( preamble == null )
+        {
+            preamble = defaultPreamble();
+        }
+
+        this.sinkCommands = sinkCommands;
+        this.preamble = preamble;
+        this.fragmentDocument = fragmentDocument;
+
+        init();
+    }
+
+    // ----------------------------------------------------------------------
+    // Overridables
+    // ----------------------------------------------------------------------
+
+    /**
+     * Returns a default \documentclass declaration.
+     *
+     * @return String.
+     */
+    protected String getDocumentStart()
+    {
+        return "\\documentclass[a4paper]{article}" + EOL + EOL;
+    }
+
+    /**
+     * Returns a default \begin{document} declaration.
+     *
+     * @return String.
+     */
+    protected String getDocumentBegin()
+    {
+        return "\\begin{document}" + EOL + EOL;
+    }
+
+    /**
+     * Returns a default \end{document} declaration.
+     *
+     * @return String.
+     */
+    protected String getDocumentEnd()
+    {
+        return "\\end{document}" + EOL;
+    }
+
+    // ----------------------------------------------------------------------
+    // Sink Implementation
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void head()
+    {
+        head( null );
+    }
+
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        init();
+
+        if ( !fragmentDocument )
+        {
+            markup( sinkCommands );
+
+            markup( getDocumentStart() );
+
+            markup( preamble );
+
+            markup( getDocumentBegin() );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void body()
+    {
+        body( null );
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        if ( titleFlag )
+        {
+            if ( fragmentDocument  )
+            {
+                markup( "\\section" );
+            }
+            else
+            {
+                titleFlag = false;
+                markup( "\\maketitle" + EOL + EOL );
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void body_()
+    {
+        if ( !fragmentDocument )
+        {
+            markup( getDocumentEnd() );
+        }
+
+        flush();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void title()
+    {
+        title( null );
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        if ( !fragmentDocument )
+        {
+            titleFlag = true;
+            markup( "\\title{" );
+        }
+        else
+        {
+            ignoreText = true;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void title_()
+    {
+        if ( !fragmentDocument )
+        {
+            markup( "}" + EOL );
+        }
+        else
+        {
+            ignoreText = false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void author()
+    {
+        author( null );
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        if ( !fragmentDocument )
+        {
+            markup( "\\author{" );
+        }
+        else
+        {
+            ignoreText = true;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void author_()
+    {
+        if ( !fragmentDocument )
+        {
+            markup( "}" + EOL );
+        }
+        else
+        {
+            ignoreText = false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void date()
+    {
+        date( null );
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        if ( !fragmentDocument )
+        {
+            markup( "\\date{" );
+        }
+        else
+        {
+            ignoreText = true;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void date_()
+    {
+        if ( !fragmentDocument )
+        {
+            markup( "}" + EOL );
+        }
+        else
+        {
+            ignoreText = false;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        isTitle = true;
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+        String command = "";
+        switch ( level )
+        {
+            case SECTION_LEVEL_1:
+                command = "section";
+                break;
+            case SECTION_LEVEL_2:
+                command = "subsection";
+                break;
+            case SECTION_LEVEL_3:
+                command = "subsubsection";
+                break;
+            case SECTION_LEVEL_4:
+                command = "paragraph";
+                break;
+            case SECTION_LEVEL_5:
+                command = "subparagraph";
+                break;
+            default:
+                throw new IllegalArgumentException( "Not a section level: " + level );
+        }
+
+        isTitle = false;
+
+        if ( StringUtils.isNotEmpty( title ) )
+        {
+            markup( EOL + "\\" + command + "{" + title + "}" + EOL );
+
+            title = null;
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Section Title 1
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle1()
+    {
+        sectionTitle( SECTION_LEVEL_1, null );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle1_()
+    {
+        sectionTitle_( SECTION_LEVEL_1 );
+    }
+
+    // ----------------------------------------------------------------------
+    // Section Title 2
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle2()
+    {
+        sectionTitle( SECTION_LEVEL_2, null );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle2_()
+    {
+        sectionTitle_( SECTION_LEVEL_2 );
+    }
+
+    // ----------------------------------------------------------------------
+    // Section Title 3
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle3()
+    {
+        sectionTitle( SECTION_LEVEL_3, null );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle3_()
+    {
+        sectionTitle_( SECTION_LEVEL_3 );
+    }
+
+    // ----------------------------------------------------------------------
+    // Section Title 4
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle4()
+    {
+        sectionTitle( SECTION_LEVEL_4, null );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle4_()
+    {
+        sectionTitle_( SECTION_LEVEL_4 );
+    }
+
+    // ----------------------------------------------------------------------
+    // Section Title 5
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle5()
+    {
+        sectionTitle( SECTION_LEVEL_5, null );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void sectionTitle5_()
+    {
+        sectionTitle_( SECTION_LEVEL_5 );
+    }
+
+    // ----------------------------------------------------------------------
+    // List
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void list()
+    {
+        list( null );
+    }
+
+    /** {@inheritDoc} */
+    public void list( SinkEventAttributes attributes )
+    {
+        markup( EOL + "\\begin{itemize}" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void list_()
+    {
+        markup( EOL + "\\end{itemize}" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void listItem()
+    {
+        listItem( null );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        markup( EOL + "\\item " );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void numberedList( int numbering )
+    {
+        numberedList( numbering, null );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        ++numberedListNesting;
+
+        String counter;
+        switch ( numberedListNesting )
+        {
+            case 1:
+                counter = "enumi";
+                break;
+            case 2:
+                counter = "enumii";
+                break;
+            case 3:
+                counter = "enumiii";
+                break;
+            case 4:
+            default:
+                counter = "enumiv";
+        }
+
+        String style;
+        switch ( numbering )
+        {
+            case NUMBERING_UPPER_ALPHA:
+                style = "Alph";
+                break;
+            case NUMBERING_LOWER_ALPHA:
+                style = "alph";
+                break;
+            case NUMBERING_UPPER_ROMAN:
+                style = "Roman";
+                break;
+            case NUMBERING_LOWER_ROMAN:
+                style = "roman";
+                break;
+            case NUMBERING_DECIMAL:
+            default:
+                style = "arabic";
+        }
+
+        markup( EOL + "\\begin{enumerate}" + EOL );
+        markup( "\\renewcommand{\\the" + counter + "}{\\" + style + "{" + counter + "}}" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void numberedList_()
+    {
+        markup( EOL + "\\end{enumerate}" + EOL );
+        --numberedListNesting;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void numberedListItem()
+    {
+        numberedListItem( null );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        markup( "\\item " );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void definitionList()
+    {
+        definitionList( null );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        markup( EOL + "\\begin{description}" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void definitionList_()
+    {
+        markup( EOL + "\\end{description}" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void definedTerm()
+    {
+        definedTerm( null );
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        markup( EOL + "\\item[\\mbox{" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void definedTerm_()
+    {
+        markup( "}] " );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        definitionListItem( null );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem( SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        definition( null );
+    }
+
+    /** {@inheritDoc} */
+    public void definition( SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        // nop
+    }
+
+    // ----------------------------------------------------------------------
+    // Figure
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void figure()
+    {
+        figure( null );
+    }
+
+    /** {@inheritDoc} */
+    public void figure( SinkEventAttributes attributes )
+    {
+        figureFlag = true;
+        markup( EOL + "\\begin{figure}[htb]" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void figure_()
+    {
+        markup( "\\end{figure}" + EOL );
+        figureFlag = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void figureGraphics( String name )
+    {
+        figureGraphics( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        if ( !src.toLowerCase( Locale.ENGLISH ).endsWith( ".eps" ) )
+        {
+            getLog().warn( "[Latex Sink] Found non-eps figure graphics!" );
+        }
+
+        markup( "\\begin{center}" + EOL );
+        markup( "\\includegraphics{" + src + "}" + EOL );
+        markup( "\\end{center}" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void figureCaption()
+    {
+        figureCaption( null );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        markup( "\\caption{" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void figureCaption_()
+    {
+        markup( "}" + EOL );
+    }
+
+    // ----------------------------------------------------------------------
+    // Table
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void table()
+    {
+        table( null );
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        tableFlag = true;
+        markup( EOL + "\\begin{table}[htp]" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void table_()
+    {
+        markup( "\\end{table}" + EOL );
+        tableFlag = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableRows( int[] justification, boolean grid )
+
+    {
+        StringBuffer justif = new StringBuffer();
+        for ( int i = 0; i < justification.length; ++i )
+        {
+            if ( grid )
+            {
+                justif.append( '|' );
+            }
+            switch ( justification[i] )
+            {
+                case Sink.JUSTIFY_CENTER:
+                    justif.append( 'c' );
+                    break;
+                case Sink.JUSTIFY_LEFT:
+                    justif.append( 'l' );
+                    break;
+                case Sink.JUSTIFY_RIGHT:
+                    justif.append( 'r' );
+                    break;
+                default:
+                    break;
+            }
+        }
+        if ( grid )
+        {
+            justif.append( '|' );
+        }
+
+        markup( "\\begin{center}" + EOL );
+        markup( "\\begin{tabular}{" + justif.toString() + "}" + EOL );
+        if ( grid )
+        {
+            markup( "\\hline" + EOL );
+        }
+        gridFlag = grid;
+        cellJustif = justification;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableRows_()
+    {
+        markup( "\\end{tabular}" + EOL );
+        markup( "\\end{center}" + EOL );
+
+        gridFlag = false;
+        cellJustif = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableRow()
+    {
+        tableRow( null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        cellCount = 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableRow_()
+    {
+        markup( "\\\\" + EOL );
+        if ( gridFlag || lastCellWasHeader )
+        {
+            markup( "\\hline" + EOL );
+        }
+        cellCount = 0;
+        lastCellWasHeader = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableCell()
+    {
+        tableCell( (SinkEventAttributes) null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        SinkEventAttributeSet att = new SinkEventAttributeSet();
+        att.addAttribute( javax.swing.text.html.HTML.Attribute.WIDTH, width );
+
+        tableCell( att );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        tableCell( false );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableCell_()
+    {
+        markup( "\\end{tabular}" );
+        ++cellCount;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableHeaderCell()
+    {
+        tableCell( (SinkEventAttributes) null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        SinkEventAttributeSet att = new SinkEventAttributeSet();
+        att.addAttribute( javax.swing.text.html.HTML.Attribute.WIDTH, width );
+
+        tableHeaderCell( att );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        tableCell( true );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableHeaderCell_()
+    {
+        tableCell_();
+    }
+
+    private boolean lastCellWasHeader = false;
+
+    /**
+     * Starts a table cell.
+     *
+     * @param header True if this is a header cell.
+     */
+    private void tableCell( boolean header )
+    {
+        lastCellWasHeader = header;
+
+        if ( cellCount > 0 )
+        {
+            markup( " &" + EOL );
+        }
+
+        char justif;
+        switch ( cellJustif[cellCount] )
+        {
+            case Sink.JUSTIFY_LEFT:
+                justif = 'l';
+                break;
+            case Sink.JUSTIFY_RIGHT:
+                justif = 'r';
+                break;
+            case Sink.JUSTIFY_CENTER:
+            default:
+                justif = 'c';
+                break;
+        }
+        markup( "\\begin{tabular}[t]{" + justif + "}" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableCaption()
+    {
+        tableCaption( null );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        markup( "\\caption{" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void tableCaption_()
+    {
+        markup( "}" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void paragraph()
+    {
+        paragraph( null );
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        markup( EOL + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void paragraph_()
+    {
+        markup( EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void verbatim( boolean boxed )
+    {
+        verbatim( boxed ? SinkEventAttributeSet.BOXED : null );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        boolean boxed = false;
+
+        if ( attributes != null && attributes.isDefined( SinkEventAttributes.DECORATION ) )
+        {
+            boxed = "boxed".equals(
+                attributes.getAttribute( SinkEventAttributes.DECORATION ) );
+        }
+
+        markup( EOL + "\\begin{small}" + EOL );
+
+        if ( boxed )
+        {
+            markup( "\\begin{Verbatim}[frame=single]" + EOL );
+        }
+        else
+        {
+            markup( "\\begin{Verbatim}" + EOL );
+        }
+
+        verbatimFlag = true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void verbatim_()
+    {
+        markup( EOL + "\\end{Verbatim}" + EOL );
+        markup( "\\end{small}" + EOL );
+
+        verbatimFlag = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void horizontalRule()
+    {
+        horizontalRule( null );
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        markup( EOL + "\\begin{center}\\rule[0.5ex]{\\linewidth}{1pt}\\end{center}" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void pageBreak()
+    {
+        markup( EOL + "\\newpage" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void anchor( String name )
+    {
+        anchor( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        markup( "\\hypertarget{" + name + "}{" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void anchor_()
+    {
+        markup( "}" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void link( String name )
+    {
+        link( name, null );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        // TODO: use \\url for simple links
+        if ( DoxiaUtils.isExternalLink( name ) )
+        {
+            markup( "\\href{" + name + "}{" );
+        }
+        else
+        {
+            markup( "\\hyperlink{" + name + "}{" );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void link_()
+    {
+        markup( "}" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void italic()
+    {
+        markup( "\\textit{" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void italic_()
+    {
+        markup( "}" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void bold()
+    {
+        markup( "\\textbf{" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void bold_()
+    {
+        markup( "}" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void monospaced()
+    {
+        markup( "\\texttt{\\small " );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void monospaced_()
+    {
+        markup( "}" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void lineBreak()
+    {
+        lineBreak( null );
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        markup( ( figureFlag || tableFlag || titleFlag || verbatimFlag ) ? EOL : "\\newline" + EOL );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void nonBreakingSpace()
+    {
+        markup( "~" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void text( String text )
+    {
+        text( text, null );
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        if ( ignoreText )
+        {
+            return;
+        }
+        if ( isTitle )
+        {
+            title = text;
+        }
+        else if ( verbatimFlag )
+        {
+            verbatimContent( text );
+        }
+        else
+        {
+            content( text );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void rawText( String text )
+    {
+        verbatimContent( text );
+    }
+
+    /** {@inheritDoc} */
+    public void comment( String comment )
+    {
+        rawText( EOL + "% " + comment );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Unkown events just log a warning message but are ignored otherwise.
+     * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        getLog().warn( "[Latex Sink] Unknown Sink event: '" + name + "', ignoring!" );
+    }
+
+    // -----------------------------------------------------------------------
+
+    /**
+     * Writes the text, preserving whitespace.
+     *
+     * @param text the text to write.
+     */
+    protected void markup( String text )
+    {
+        if ( text != null )
+        {
+            out.write( text, /*preserveSpace*/ true );
+        }
+    }
+
+    /**
+     * Writes the text, without preserving whitespace.
+     *
+     * @param text the text to write.
+     */
+    protected void content( String text )
+    {
+        out.write( escaped( text ), /*preserveSpace*/ false );
+    }
+
+    /**
+     * Writes the text, preserving whitespace.
+     *
+     * @param text the text to write.
+     */
+    protected void verbatimContent( String text )
+    {
+        out.write( text, /*preserveSpace*/ true );
+    }
+
+    // -----------------------------------------------------------------------
+
+    /**
+     * Escapes special characters.
+     *
+     * @param text The text to escape.
+     * @return The text with special characters replaced.
+     */
+    public static String escaped( String text )
+    {
+        int length = text.length();
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = text.charAt( i );
+            switch ( c )
+            {
+                case '-':
+                case '<':
+                case '>':
+                    buffer.append( "\\symbol{" ).append( (int) c ).append( "}" );
+                    break;
+                case '~':
+                    buffer.append( "\\textasciitilde " );
+                    break;
+                case '^':
+                    buffer.append( "\\textasciicircum " );
+                    break;
+                case '|':
+                    buffer.append( "\\textbar " );
+                    break;
+                case '\\':
+                    buffer.append( "\\textbackslash " );
+                    break;
+                case '$':
+                    buffer.append( "\\$" );
+                    break;
+                case '&':
+                    buffer.append( "\\&" );
+                    break;
+                case '%':
+                    buffer.append( "\\%" );
+                    break;
+                case '#':
+                    buffer.append( "\\#" );
+                    break;
+                case '{':
+                    buffer.append( "\\{" );
+                    break;
+                case '}':
+                    buffer.append( "\\}" );
+                    break;
+                case '_':
+                    buffer.append( "\\_" );
+                    break;
+                default:
+                    buffer.append( c );
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     */
+    public void flush()
+    {
+        out.flush();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void close()
+    {
+        out.close();
+
+        init();
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * Returns the default sink commands from a resource.
+     *
+     * @throws java.io.IOException if the resource file cannot be read.
+     * @return InputStream
+     */
+    private static InputStream getDefaultSinkCommands()
+        throws IOException
+    {
+        return LatexSink.class.getResource( "default_sink_commands.tex" ).openStream();
+    }
+
+    /**
+     * Returns the default preamble from a resource.
+     *
+     * @return InputStream
+     * @throws java.io.IOException if the resource file cannot be read.
+     */
+    private static InputStream getDefaultPreamble()
+        throws IOException
+    {
+        return LatexSink.class.getResource( "default_preamble.tex" ).openStream();
+    }
+
+    /**
+     * Returns the default sink commands.
+     *
+     * @return String.
+     */
+    protected String defaultSinkCommands()
+    {
+        try
+        {
+            return IOUtil.toString( getDefaultSinkCommands() );
+        }
+        catch ( IOException ioe )
+        {
+            // this should not happen
+            getLog().warn( "Could not read default LaTeX commands, the generated LaTeX file will not compile!" );
+            getLog().debug( ioe );
+
+            return "";
+        }
+    }
+
+    /**
+     * Returns the default preamble.
+     *
+     * @return String.
+     */
+    protected String defaultPreamble()
+    {
+        try
+        {
+            return IOUtil.toString( getDefaultPreamble() );
+        }
+        catch ( IOException ioe )
+        {
+            // this should not happen
+            getLog().warn( "Could not read default LaTeX preamble, the generated LaTeX file will not compile!" );
+            getLog().debug( ioe );
+
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.ignoreText = false;
+        this.titleFlag = false;
+        this.numberedListNesting = 0;
+        this.verbatimFlag = false;
+        this.figureFlag = false;
+        this.tableFlag = false;
+        this.gridFlag = false;
+        this.cellJustif = null;
+        this.cellCount = 0;
+        this.isTitle = false;
+        this.title = null;
+    }
+}
diff --git a/doxia-modules/doxia-module-latex/src/main/java/org/apache/maven/doxia/module/latex/LatexSinkFactory.java b/doxia-modules/doxia-module-latex/src/main/java/org/apache/maven/doxia/module/latex/LatexSinkFactory.java
new file mode 100644
index 0000000..46108c3
--- /dev/null
+++ b/doxia-modules/doxia-module-latex/src/main/java/org/apache/maven/doxia/module/latex/LatexSinkFactory.java
@@ -0,0 +1,45 @@
+package org.apache.maven.doxia.module.latex;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractTextSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Latex implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: LatexSinkFactory.java 712574 2008-11-09 22:16:42Z hboutemy $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="latex"
+ */
+public class LatexSinkFactory
+    extends AbstractTextSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        // TODO: can encoding safely be ignored? Shouldn't it be written into the generated Latex source
+        // as inputenc package parameter?
+        return new LatexSink( writer );
+    }
+}
diff --git a/doxia-modules/doxia-module-latex/src/main/resources/org/apache/maven/doxia/module/latex/default_preamble.tex b/doxia-modules/doxia-module-latex/src/main/resources/org/apache/maven/doxia/module/latex/default_preamble.tex
new file mode 100644
index 0000000..9e12f2d
--- /dev/null
+++ b/doxia-modules/doxia-module-latex/src/main/resources/org/apache/maven/doxia/module/latex/default_preamble.tex
@@ -0,0 +1,14 @@
+% required (do not remove!):
+\usepackage{graphics} % for images
+\usepackage{fancyvrb} % for verbatim
+\usepackage[colorlinks=true,urlcolor=blue,linkcolor=green]{hyperref} % for links and anchors
+
+% optional:
+\usepackage{times}
+\usepackage[latin1]{inputenc}
+
+\pagestyle{plain}
+
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{1ex}
+
diff --git a/doxia-modules/doxia-module-latex/src/main/resources/org/apache/maven/doxia/module/latex/default_sink_commands.tex b/doxia-modules/doxia-module-latex/src/main/resources/org/apache/maven/doxia/module/latex/default_sink_commands.tex
new file mode 100644
index 0000000..e69de29
diff --git a/doxia-modules/doxia-module-latex/src/site/site.xml b/doxia-modules/doxia-module-latex/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-modules/doxia-module-latex/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-latex/src/test/java/org/apache/maven/doxia/module/latex/LatexSinkTest.java b/doxia-modules/doxia-module-latex/src/test/java/org/apache/maven/doxia/module/latex/LatexSinkTest.java
new file mode 100644
index 0000000..bc3bdd5
--- /dev/null
+++ b/doxia-modules/doxia-module-latex/src/test/java/org/apache/maven/doxia/module/latex/LatexSinkTest.java
@@ -0,0 +1,242 @@
+package org.apache.maven.doxia.module.latex;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: LatexSinkTest.java 795033 2009-07-17 10:46:21Z ltheussl $
+ */
+public class LatexSinkTest
+    extends AbstractSinkTest
+{
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "tex";
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new LatexSink( writer );
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        return "\\title{" + LatexSink.escaped( title ) + "}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        return "\\author{" + LatexSink.escaped( author ) + "}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        return "\\date{" + LatexSink.escaped( date ) + "}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return ( (LatexSink) getSink() ).defaultSinkCommands()
+            + "\\documentclass[a4paper]{article}" + EOL + EOL
+            + ( (LatexSink) getSink() ).defaultPreamble()
+            + "\\begin{document}" + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return "\\end{document}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return EOL + "\\section{" + title + "}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return EOL + "\\subsection{" + title + "}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        return EOL + "\\subsubsection{" + title + "}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        return EOL + "\\paragraph{" + title + "}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        return EOL + "\\subparagraph{" + title + "}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return EOL + "\\begin{itemize}" + EOL + "\\item " + LatexSink.escaped( item ) + EOL + "\\end{itemize}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return EOL + "\\begin{enumerate}" + EOL + "\\renewcommand{\\theenumi}{\\roman{enumi}}" + EOL + "\\item "
+            + LatexSink.escaped( item ) + EOL + "\\end{enumerate}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return EOL + "\\begin{description}" + EOL + "\\item[\\mbox{" + definum + "}] "
+                + definition + EOL + "\\end{description}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        return EOL + "\\begin{figure}[htb]" + EOL + "\\begin{center}" + EOL + "\\includegraphics{" + source + "}" + EOL
+            + "\\end{center}" + EOL + "\\caption{Figure\\_caption}" + EOL + "\\end{figure}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        return EOL + "\\begin{table}[htp]" + EOL + "\\begin{center}" + EOL + "\\begin{tabular}{c}" + EOL
+            + "\\begin{tabular}[t]{c}cell\\end{tabular}\\\\" + EOL + "\\end{tabular}" + EOL
+            + "\\end{center}" + EOL + "\\caption{Table\\_caption}" + EOL + "\\end{table}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        return  EOL + EOL + text + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        return EOL + "\\begin{small}" + EOL + "\\begin{Verbatim}[frame=single]" + EOL + text + EOL
+            + "\\end{Verbatim}" + EOL + "\\end{small}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return EOL + "\\begin{center}\\rule[0.5ex]{\\linewidth}{1pt}\\end{center}" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return EOL + "\\newpage" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        return "\\hypertarget{" + anchor + "}{" + anchor + "}";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        return "\\hyperlink{" + link + "}{" + text + "}";
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        return "\\textit{" + text + "}";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        return "\\textbf{" + text + "}";
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        return "\\texttt{\\small " + text + "}";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return "\\newline" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return "~";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        // TODO: how to retrieve those outside the sink?
+        return "\\textasciitilde" + EOL + ",\\_=,\\_\\symbol{45},\\_+,\\_*,\\_[,\\_],"
+                + "\\_\\symbol{60},\\_\\symbol{62},\\_\\{,\\_\\},\\_\\textbackslash";
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        return "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return EOL + "% Simple comment with ----";
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/pom.xml b/doxia-modules/doxia-module-rtf/pom.xml
new file mode 100644
index 0000000..c1ede47
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-rtf</artifactId>
+
+  <name>Doxia :: RTF Module</name>
+  <description>A Doxia module for Rich Text Format source documents.</description>
+
+  <dependencies>
+    <!-- test -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-apt</artifactId>
+      <version>${projectVersion}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/AlphaNumerals.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/AlphaNumerals.java
new file mode 100644
index 0000000..657a97a
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/AlphaNumerals.java
@@ -0,0 +1,51 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: AlphaNumerals.java 703394 2008-10-10 10:53:51Z vsiveton $
+ */
+class AlphaNumerals
+{
+    static String toString( int n )
+    {
+        return toString( n, false );
+    }
+
+    static String toString( int n, boolean lowerCase )
+    {
+        StringBuffer alpha = new StringBuffer();
+        char zeroLetter = lowerCase ? '`' : '@';
+
+        while ( n > 0 )
+        {
+            char letter = (char) ( zeroLetter + ( n % 27 ) );
+            if ( letter == zeroLetter )
+            {
+                letter = '0';
+            }
+            alpha.insert( 0, letter );
+
+            n /= 27;
+        }
+
+        return alpha.toString();
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Font.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Font.java
new file mode 100644
index 0000000..85a3d47
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Font.java
@@ -0,0 +1,104 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * A basic font descriptor using standard PostScript font metrics to compute
+ * text extents. All dimensions returned are in twips.
+ *
+ * @version $Id: Font.java 703397 2008-10-10 11:01:15Z vsiveton $
+ */
+class Font
+{
+    private int size;
+
+    private FontMetrics metrics;
+
+    Font( int style, int size /*pts*/ )
+        throws Exception
+    {
+        this.size = size;
+        metrics = FontMetrics.find( style );
+    }
+
+    int ascent()
+    {
+        return toTwips( metrics.ascent );
+    }
+
+    int descent()
+    {
+        return toTwips( metrics.descent );
+    }
+
+    TextExtents textExtents( String text )
+    {
+        int i, n;
+        int width = 0;
+        int ascent = 0;
+        int descent = 0;
+
+        for ( i = 0, n = text.length(); i < n; ++i )
+        {
+            char c = text.charAt( i );
+            if ( c > 255 )
+            {
+                c = ' ';
+            }
+            FontMetrics.CharMetrics charMetrics = this.metrics.charMetrics[c];
+            width += charMetrics.wx;
+            if ( charMetrics.ury > ascent )
+            {
+                ascent = charMetrics.ury;
+            }
+            if ( charMetrics.lly < descent )
+            {
+                descent = charMetrics.lly;
+            }
+        }
+
+        int height = ascent + Math.abs( descent );
+
+        return new TextExtents( toTwips( width ), toTwips( height ), toTwips( ascent ) );
+    }
+
+    private int toTwips( int length )
+    {
+        return (int) Math.rint( (double) length * size / 50. );
+    }
+
+    static class TextExtents
+    {
+
+        int width;
+
+        int height;
+
+        int ascent;
+
+        TextExtents( int width, int height, int ascent )
+        {
+            this.width = width;
+            this.height = height;
+            this.ascent = ascent;
+        }
+
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/FontMetrics.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/FontMetrics.java
new file mode 100644
index 0000000..392bc9c
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/FontMetrics.java
@@ -0,0 +1,102 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: FontMetrics.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class FontMetrics
+{
+    boolean fixedPitch;
+
+    short ascent;
+
+    short descent;
+
+    CharMetrics bounds;
+
+    CharMetrics[] charMetrics;
+
+    FontMetrics( boolean fixedPitch, int ascent, int descent, CharMetrics bounds, CharMetrics[] metrics )
+    {
+        this.fixedPitch = fixedPitch;
+        this.ascent = (short) ascent;
+        this.descent = (short) descent;
+        this.bounds = bounds;
+        this.charMetrics = metrics;
+    }
+
+    static FontMetrics find( int style )
+        throws Exception
+    {
+        String s = FontMetrics.class.getName();
+        String packageName = s.substring( 0, s.lastIndexOf( '.' ) );
+
+        StringBuffer buf = new StringBuffer( packageName + "." );
+
+        switch ( style )
+        {
+            case RtfSink.STYLE_ROMAN:
+            default:
+                buf.append( "Serif" );
+                break;
+            case RtfSink.STYLE_ITALIC:
+                buf.append( "SerifItalic" );
+                break;
+            case RtfSink.STYLE_BOLD:
+                buf.append( "SerifBold" );
+                break;
+            case RtfSink.STYLE_TYPEWRITER:
+                buf.append( "Monospace" );
+                break;
+        }
+
+        String className = buf.toString();
+        Class classObject = Class.forName( className );
+        return (FontMetrics) classObject.newInstance();
+    }
+
+    static class CharMetrics
+    {
+
+        short wx;
+
+        short wy;
+
+        short llx;
+
+        short lly;
+
+        short urx;
+
+        short ury;
+
+        CharMetrics( int wx, int wy, int llx, int lly, int urx, int ury )
+        {
+            this.wx = (short) wx;
+            this.wy = (short) wy;
+            this.llx = (short) llx;
+            this.lly = (short) lly;
+            this.urx = (short) urx;
+            this.ury = (short) ury;
+        }
+
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Monospace.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Monospace.java
new file mode 100644
index 0000000..c761da5
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Monospace.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: Monospace.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class Monospace
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 236, -15, 364, 572 ), new CharMetrics( 600, 0, 187, 328, 413, 562 ),
+        new CharMetrics( 600, 0, 93, -32, 507, 639 ), new CharMetrics( 600, 0, 105, -126, 496, 662 ),
+        new CharMetrics( 600, 0, 81, -15, 518, 622 ), new CharMetrics( 600, 0, 63, -15, 538, 543 ),
+        new CharMetrics( 600, 0, 213, 328, 376, 562 ), new CharMetrics( 600, 0, 269, -108, 440, 622 ),
+        new CharMetrics( 600, 0, 160, -108, 331, 622 ), new CharMetrics( 600, 0, 116, 257, 484, 607 ),
+        new CharMetrics( 600, 0, 80, 44, 520, 470 ), new CharMetrics( 600, 0, 181, -112, 344, 122 ),
+        new CharMetrics( 600, 0, 103, 231, 497, 285 ), new CharMetrics( 600, 0, 229, -15, 371, 109 ),
+        new CharMetrics( 600, 0, 125, -80, 475, 629 ), new CharMetrics( 600, 0, 106, -15, 494, 622 ),
+        new CharMetrics( 600, 0, 96, 0, 505, 622 ), new CharMetrics( 600, 0, 70, 0, 471, 622 ),
+        new CharMetrics( 600, 0, 75, -15, 466, 622 ), new CharMetrics( 600, 0, 78, 0, 500, 622 ),
+        new CharMetrics( 600, 0, 92, -15, 497, 607 ), new CharMetrics( 600, 0, 111, -15, 497, 622 ),
+        new CharMetrics( 600, 0, 82, 0, 483, 607 ), new CharMetrics( 600, 0, 102, -15, 498, 622 ),
+        new CharMetrics( 600, 0, 96, -15, 489, 622 ), new CharMetrics( 600, 0, 229, -15, 371, 385 ),
+        new CharMetrics( 600, 0, 181, -112, 371, 385 ), new CharMetrics( 600, 0, 41, 42, 519, 472 ),
+        new CharMetrics( 600, 0, 80, 138, 520, 376 ), new CharMetrics( 600, 0, 66, 42, 544, 472 ),
+        new CharMetrics( 600, 0, 129, -15, 492, 572 ), new CharMetrics( 600, 0, 77, -15, 533, 622 ),
+        new CharMetrics( 600, 0, 3, 0, 597, 562 ), new CharMetrics( 600, 0, 43, 0, 559, 562 ),
+        new CharMetrics( 600, 0, 41, -18, 540, 580 ), new CharMetrics( 600, 0, 43, 0, 574, 562 ),
+        new CharMetrics( 600, 0, 53, 0, 550, 562 ), new CharMetrics( 600, 0, 53, 0, 545, 562 ),
+        new CharMetrics( 600, 0, 31, -18, 575, 580 ), new CharMetrics( 600, 0, 32, 0, 568, 562 ),
+        new CharMetrics( 600, 0, 96, 0, 504, 562 ), new CharMetrics( 600, 0, 34, -18, 566, 562 ),
+        new CharMetrics( 600, 0, 38, 0, 582, 562 ), new CharMetrics( 600, 0, 47, 0, 554, 562 ),
+        new CharMetrics( 600, 0, 4, 0, 596, 562 ), new CharMetrics( 600, 0, 7, -13, 593, 562 ),
+        new CharMetrics( 600, 0, 43, -18, 557, 580 ), new CharMetrics( 600, 0, 79, 0, 558, 562 ),
+        new CharMetrics( 600, 0, 43, -138, 557, 580 ), new CharMetrics( 600, 0, 38, 0, 588, 562 ),
+        new CharMetrics( 600, 0, 72, -20, 529, 580 ), new CharMetrics( 600, 0, 38, 0, 563, 562 ),
+        new CharMetrics( 600, 0, 17, -18, 583, 562 ), new CharMetrics( 600, 0, -4, -13, 604, 562 ),
+        new CharMetrics( 600, 0, -3, -13, 603, 562 ), new CharMetrics( 600, 0, 23, 0, 577, 562 ),
+        new CharMetrics( 600, 0, 24, 0, 576, 562 ), new CharMetrics( 600, 0, 86, 0, 514, 562 ),
+        new CharMetrics( 600, 0, 269, -108, 442, 622 ), new CharMetrics( 600, 0, 118, -80, 482, 629 ),
+        new CharMetrics( 600, 0, 158, -108, 331, 622 ), new CharMetrics( 600, 0, 94, 354, 506, 622 ),
+        new CharMetrics( 600, 0, 0, -125, 600, -75 ), new CharMetrics( 600, 0, 224, 328, 387, 562 ),
+        new CharMetrics( 600, 0, 53, -15, 559, 441 ), new CharMetrics( 600, 0, 14, -15, 575, 629 ),
+        new CharMetrics( 600, 0, 66, -15, 529, 441 ), new CharMetrics( 600, 0, 45, -15, 591, 629 ),
+        new CharMetrics( 600, 0, 66, -15, 548, 441 ), new CharMetrics( 600, 0, 114, 0, 531, 629 ),
+        new CharMetrics( 600, 0, 45, -157, 566, 441 ), new CharMetrics( 600, 0, 18, 0, 582, 629 ),
+        new CharMetrics( 600, 0, 95, 0, 505, 657 ), new CharMetrics( 600, 0, 82, -157, 410, 657 ),
+        new CharMetrics( 600, 0, 43, 0, 580, 629 ), new CharMetrics( 600, 0, 95, 0, 505, 629 ),
+        new CharMetrics( 600, 0, -5, 0, 605, 441 ), new CharMetrics( 600, 0, 26, 0, 575, 441 ),
+        new CharMetrics( 600, 0, 62, -15, 538, 441 ), new CharMetrics( 600, 0, 9, -157, 555, 441 ),
+        new CharMetrics( 600, 0, 45, -157, 591, 441 ), new CharMetrics( 600, 0, 60, 0, 559, 441 ),
+        new CharMetrics( 600, 0, 80, -15, 513, 441 ), new CharMetrics( 600, 0, 87, -15, 530, 561 ),
+        new CharMetrics( 600, 0, 21, -15, 562, 426 ), new CharMetrics( 600, 0, 10, -10, 590, 426 ),
+        new CharMetrics( 600, 0, -4, -10, 604, 426 ), new CharMetrics( 600, 0, 20, 0, 580, 426 ),
+        new CharMetrics( 600, 0, 7, -157, 592, 426 ), new CharMetrics( 600, 0, 99, 0, 502, 426 ),
+        new CharMetrics( 600, 0, 182, -108, 437, 622 ), new CharMetrics( 600, 0, 275, -250, 326, 750 ),
+        new CharMetrics( 600, 0, 163, -108, 418, 622 ), new CharMetrics( 600, 0, 63, 197, 540, 320 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 95, 0, 505, 426 ),
+        new CharMetrics( 600, 0, 151, 497, 378, 672 ), new CharMetrics( 600, 0, 242, 497, 469, 672 ),
+        new CharMetrics( 600, 0, 124, 477, 476, 654 ), new CharMetrics( 600, 0, 105, 489, 503, 606 ),
+        new CharMetrics( 600, 0, 120, 525, 480, 565 ), new CharMetrics( 600, 0, 153, 501, 447, 609 ),
+        new CharMetrics( 600, 0, 249, 477, 352, 580 ), new CharMetrics( 600, 0, 148, 492, 453, 595 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 218, 463, 382, 627 ),
+        new CharMetrics( 600, 0, 224, -151, 362, 10 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 133, 497, 540, 672 ), new CharMetrics( 600, 0, 227, -151, 370, 0 ),
+        new CharMetrics( 600, 0, 124, 492, 476, 669 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 236, -157, 364, 430 ), new CharMetrics( 600, 0, 96, -49, 500, 614 ),
+        new CharMetrics( 600, 0, 84, -21, 521, 611 ), new CharMetrics( 600, 0, 73, 58, 527, 506 ),
+        new CharMetrics( 600, 0, 26, 0, 574, 562 ), new CharMetrics( 600, 0, 275, -175, 326, 675 ),
+        new CharMetrics( 600, 0, 113, -78, 488, 580 ), new CharMetrics( 600, 0, 148, 492, 453, 595 ),
+        new CharMetrics( 600, 0, 0, -18, 600, 580 ), new CharMetrics( 600, 0, 156, 249, 442, 580 ),
+        new CharMetrics( 600, 0, 37, 70, 563, 446 ), new CharMetrics( 600, 0, 87, 108, 513, 369 ),
+        new CharMetrics( 600, 0, 103, 231, 497, 285 ), new CharMetrics( 600, 0, 0, -18, 600, 580 ),
+        new CharMetrics( 600, 0, 120, 525, 480, 565 ), new CharMetrics( 600, 0, 123, 269, 477, 622 ),
+        new CharMetrics( 600, 0, 87, 44, 513, 558 ), new CharMetrics( 600, 0, 177, 249, 424, 622 ),
+        new CharMetrics( 600, 0, 155, 240, 406, 622 ), new CharMetrics( 600, 0, 242, 497, 469, 672 ),
+        new CharMetrics( 600, 0, 21, -157, 562, 426 ), new CharMetrics( 600, 0, 50, -78, 511, 562 ),
+        new CharMetrics( 600, 0, 222, 189, 378, 327 ), new CharMetrics( 600, 0, 224, -151, 362, 10 ),
+        new CharMetrics( 600, 0, 172, 249, 428, 622 ), new CharMetrics( 600, 0, 157, 249, 443, 580 ),
+        new CharMetrics( 600, 0, 37, 70, 563, 446 ), new CharMetrics( 600, 0, 0, -57, 600, 665 ),
+        new CharMetrics( 600, 0, 0, -57, 611, 665 ), new CharMetrics( 600, 0, 8, -56, 593, 666 ),
+        new CharMetrics( 600, 0, 108, -157, 471, 430 ), new CharMetrics( 600, 0, 3, 0, 597, 793 ),
+        new CharMetrics( 600, 0, 3, 0, 597, 793 ), new CharMetrics( 600, 0, 3, 0, 597, 775 ),
+        new CharMetrics( 600, 0, 3, 0, 597, 732 ), new CharMetrics( 600, 0, 3, 0, 597, 731 ),
+        new CharMetrics( 600, 0, 3, 0, 597, 753 ), new CharMetrics( 600, 0, 3, 0, 550, 562 ),
+        new CharMetrics( 600, 0, 41, -151, 540, 580 ), new CharMetrics( 600, 0, 53, 0, 550, 793 ),
+        new CharMetrics( 600, 0, 53, 0, 550, 793 ), new CharMetrics( 600, 0, 53, 0, 550, 775 ),
+        new CharMetrics( 600, 0, 53, 0, 550, 731 ), new CharMetrics( 600, 0, 96, 0, 504, 793 ),
+        new CharMetrics( 600, 0, 96, 0, 504, 793 ), new CharMetrics( 600, 0, 96, 0, 504, 775 ),
+        new CharMetrics( 600, 0, 96, 0, 504, 731 ), new CharMetrics( 600, 0, 30, 0, 574, 562 ),
+        new CharMetrics( 600, 0, 7, -13, 593, 732 ), new CharMetrics( 600, 0, 43, -18, 557, 793 ),
+        new CharMetrics( 600, 0, 43, -18, 557, 793 ), new CharMetrics( 600, 0, 43, -18, 557, 775 ),
+        new CharMetrics( 600, 0, 43, -18, 557, 732 ), new CharMetrics( 600, 0, 43, -18, 557, 731 ),
+        new CharMetrics( 600, 0, 87, 43, 515, 470 ), new CharMetrics( 600, 0, 43, -80, 557, 629 ),
+        new CharMetrics( 600, 0, 17, -18, 583, 793 ), new CharMetrics( 600, 0, 17, -18, 583, 793 ),
+        new CharMetrics( 600, 0, 17, -18, 583, 775 ), new CharMetrics( 600, 0, 17, -18, 583, 731 ),
+        new CharMetrics( 600, 0, 24, 0, 576, 793 ), new CharMetrics( 600, 0, 79, 0, 538, 562 ),
+        new CharMetrics( 600, 0, 48, -15, 588, 629 ), new CharMetrics( 600, 0, 53, -15, 559, 672 ),
+        new CharMetrics( 600, 0, 53, -15, 559, 672 ), new CharMetrics( 600, 0, 53, -15, 559, 654 ),
+        new CharMetrics( 600, 0, 53, -15, 559, 606 ), new CharMetrics( 600, 0, 53, -15, 559, 595 ),
+        new CharMetrics( 600, 0, 53, -15, 559, 627 ), new CharMetrics( 600, 0, 19, -15, 570, 441 ),
+        new CharMetrics( 600, 0, 66, -151, 529, 441 ), new CharMetrics( 600, 0, 66, -15, 548, 672 ),
+        new CharMetrics( 600, 0, 66, -15, 548, 672 ), new CharMetrics( 600, 0, 66, -15, 548, 654 ),
+        new CharMetrics( 600, 0, 66, -15, 548, 595 ), new CharMetrics( 600, 0, 95, 0, 505, 672 ),
+        new CharMetrics( 600, 0, 95, 0, 505, 672 ), new CharMetrics( 600, 0, 94, 0, 505, 654 ),
+        new CharMetrics( 600, 0, 95, 0, 505, 595 ), new CharMetrics( 600, 0, 62, -15, 538, 629 ),
+        new CharMetrics( 600, 0, 26, 0, 575, 606 ), new CharMetrics( 600, 0, 62, -15, 538, 672 ),
+        new CharMetrics( 600, 0, 62, -15, 538, 672 ), new CharMetrics( 600, 0, 62, -15, 538, 654 ),
+        new CharMetrics( 600, 0, 62, -15, 538, 606 ), new CharMetrics( 600, 0, 62, -15, 538, 595 ),
+        new CharMetrics( 600, 0, 87, 48, 513, 467 ), new CharMetrics( 600, 0, 62, -80, 538, 506 ),
+        new CharMetrics( 600, 0, 21, -15, 562, 672 ), new CharMetrics( 600, 0, 21, -15, 562, 672 ),
+        new CharMetrics( 600, 0, 21, -15, 562, 654 ), new CharMetrics( 600, 0, 21, -15, 562, 595 ),
+        new CharMetrics( 600, 0, 7, -157, 592, 672 ), new CharMetrics( 600, 0, -6, -157, 555, 629 ),
+        new CharMetrics( 600, 0, 7, -157, 592, 595 )};
+
+    Monospace()
+    {
+        super( true, 629, -157, new CharMetrics( 0, 0, -28, -250, 628, 805 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceBold.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceBold.java
new file mode 100644
index 0000000..a0b2222
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceBold.java
@@ -0,0 +1,163 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: MonospaceBold.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class MonospaceBold
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 202, -15, 398, 572 ), new CharMetrics( 600, 0, 135, 277, 465, 562 ),
+        new CharMetrics( 600, 0, 56, -45, 544, 651 ), new CharMetrics( 600, 0, 82, -126, 519, 666 ),
+        new CharMetrics( 600, 0, 5, -15, 595, 616 ), new CharMetrics( 600, 0, 36, -15, 546, 543 ),
+        new CharMetrics( 600, 0, 171, 277, 423, 562 ), new CharMetrics( 600, 0, 219, -102, 461, 616 ),
+        new CharMetrics( 600, 0, 139, -102, 381, 616 ), new CharMetrics( 600, 0, 91, 219, 509, 601 ),
+        new CharMetrics( 600, 0, 71, 39, 529, 478 ), new CharMetrics( 600, 0, 123, -111, 393, 174 ),
+        new CharMetrics( 600, 0, 100, 203, 500, 313 ), new CharMetrics( 600, 0, 192, -15, 408, 171 ),
+        new CharMetrics( 600, 0, 98, -77, 502, 626 ), new CharMetrics( 600, 0, 87, -15, 513, 616 ),
+        new CharMetrics( 600, 0, 81, 0, 539, 616 ), new CharMetrics( 600, 0, 61, 0, 499, 616 ),
+        new CharMetrics( 600, 0, 63, -15, 501, 616 ), new CharMetrics( 600, 0, 53, 0, 507, 616 ),
+        new CharMetrics( 600, 0, 70, -15, 521, 601 ), new CharMetrics( 600, 0, 90, -15, 521, 616 ),
+        new CharMetrics( 600, 0, 55, 0, 494, 601 ), new CharMetrics( 600, 0, 83, -15, 517, 616 ),
+        new CharMetrics( 600, 0, 79, -15, 510, 616 ), new CharMetrics( 600, 0, 191, -15, 407, 425 ),
+        new CharMetrics( 600, 0, 123, -111, 408, 425 ), new CharMetrics( 600, 0, 66, 15, 523, 501 ),
+        new CharMetrics( 600, 0, 71, 118, 529, 398 ), new CharMetrics( 600, 0, 77, 15, 534, 501 ),
+        new CharMetrics( 600, 0, 98, -14, 501, 580 ), new CharMetrics( 600, 0, 16, -15, 584, 616 ),
+        new CharMetrics( 600, 0, -9, 0, 609, 562 ), new CharMetrics( 600, 0, 30, 0, 573, 562 ),
+        new CharMetrics( 600, 0, 22, -18, 560, 580 ), new CharMetrics( 600, 0, 30, 0, 594, 562 ),
+        new CharMetrics( 600, 0, 25, 0, 560, 562 ), new CharMetrics( 600, 0, 39, 0, 570, 562 ),
+        new CharMetrics( 600, 0, 22, -18, 594, 580 ), new CharMetrics( 600, 0, 20, 0, 580, 562 ),
+        new CharMetrics( 600, 0, 77, 0, 523, 562 ), new CharMetrics( 600, 0, 37, -18, 601, 562 ),
+        new CharMetrics( 600, 0, 21, 0, 599, 562 ), new CharMetrics( 600, 0, 39, 0, 578, 562 ),
+        new CharMetrics( 600, 0, -2, 0, 602, 562 ), new CharMetrics( 600, 0, 8, -12, 610, 562 ),
+        new CharMetrics( 600, 0, 22, -18, 578, 580 ), new CharMetrics( 600, 0, 48, 0, 559, 562 ),
+        new CharMetrics( 600, 0, 32, -138, 578, 580 ), new CharMetrics( 600, 0, 24, 0, 599, 562 ),
+        new CharMetrics( 600, 0, 47, -22, 553, 582 ), new CharMetrics( 600, 0, 21, 0, 579, 562 ),
+        new CharMetrics( 600, 0, 4, -18, 596, 562 ), new CharMetrics( 600, 0, -13, 0, 613, 562 ),
+        new CharMetrics( 600, 0, -18, 0, 618, 562 ), new CharMetrics( 600, 0, 12, 0, 588, 562 ),
+        new CharMetrics( 600, 0, 12, 0, 589, 562 ), new CharMetrics( 600, 0, 62, 0, 539, 562 ),
+        new CharMetrics( 600, 0, 245, -102, 475, 616 ), new CharMetrics( 600, 0, 99, -77, 503, 626 ),
+        new CharMetrics( 600, 0, 125, -102, 355, 616 ), new CharMetrics( 600, 0, 108, 250, 492, 616 ),
+        new CharMetrics( 600, 0, 0, -125, 600, -75 ), new CharMetrics( 600, 0, 178, 277, 428, 562 ),
+        new CharMetrics( 600, 0, 35, -15, 570, 454 ), new CharMetrics( 600, 0, 0, -15, 584, 626 ),
+        new CharMetrics( 600, 0, 40, -15, 545, 459 ), new CharMetrics( 600, 0, 20, -15, 591, 626 ),
+        new CharMetrics( 600, 0, 40, -15, 563, 454 ), new CharMetrics( 600, 0, 83, 0, 547, 626 ),
+        new CharMetrics( 600, 0, 30, -146, 580, 454 ), new CharMetrics( 600, 0, 5, 0, 592, 626 ),
+        new CharMetrics( 600, 0, 77, 0, 523, 658 ), new CharMetrics( 600, 0, 63, -146, 440, 658 ),
+        new CharMetrics( 600, 0, 20, 0, 585, 626 ), new CharMetrics( 600, 0, 77, 0, 523, 626 ),
+        new CharMetrics( 600, 0, -22, 0, 626, 454 ), new CharMetrics( 600, 0, 18, 0, 592, 454 ),
+        new CharMetrics( 600, 0, 30, -15, 570, 454 ), new CharMetrics( 600, 0, -1, -142, 570, 454 ),
+        new CharMetrics( 600, 0, 20, -142, 591, 454 ), new CharMetrics( 600, 0, 47, 0, 580, 454 ),
+        new CharMetrics( 600, 0, 68, -17, 535, 459 ), new CharMetrics( 600, 0, 47, -15, 532, 562 ),
+        new CharMetrics( 600, 0, -1, -15, 569, 439 ), new CharMetrics( 600, 0, -1, 0, 601, 439 ),
+        new CharMetrics( 600, 0, -18, 0, 618, 439 ), new CharMetrics( 600, 0, 6, 0, 594, 439 ),
+        new CharMetrics( 600, 0, -4, -142, 601, 439 ), new CharMetrics( 600, 0, 81, 0, 520, 439 ),
+        new CharMetrics( 600, 0, 160, -102, 464, 616 ), new CharMetrics( 600, 0, 255, -250, 345, 750 ),
+        new CharMetrics( 600, 0, 136, -102, 440, 616 ), new CharMetrics( 600, 0, 71, 153, 530, 356 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 77, 0, 523, 439 ),
+        new CharMetrics( 600, 0, 132, 508, 395, 661 ), new CharMetrics( 600, 0, 205, 508, 468, 661 ),
+        new CharMetrics( 600, 0, 103, 483, 497, 657 ), new CharMetrics( 600, 0, 89, 493, 512, 636 ),
+        new CharMetrics( 600, 0, 88, 505, 512, 585 ), new CharMetrics( 600, 0, 83, 468, 517, 631 ),
+        new CharMetrics( 600, 0, 230, 485, 370, 625 ), new CharMetrics( 600, 0, 128, 485, 472, 625 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 198, 481, 402, 678 ),
+        new CharMetrics( 600, 0, 205, -206, 387, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 68, 488, 588, 661 ), new CharMetrics( 600, 0, 169, -199, 367, 0 ),
+        new CharMetrics( 600, 0, 103, 493, 497, 667 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 202, -146, 398, 449 ), new CharMetrics( 600, 0, 66, -49, 518, 614 ),
+        new CharMetrics( 600, 0, 72, -28, 558, 611 ), new CharMetrics( 600, 0, 54, 49, 546, 517 ),
+        new CharMetrics( 600, 0, 10, 0, 590, 562 ), new CharMetrics( 600, 0, 255, -175, 345, 675 ),
+        new CharMetrics( 600, 0, 83, -70, 517, 580 ), new CharMetrics( 600, 0, 128, 485, 472, 625 ),
+        new CharMetrics( 600, 0, 0, -18, 600, 580 ), new CharMetrics( 600, 0, 147, 196, 453, 580 ),
+        new CharMetrics( 600, 0, 8, 70, 553, 446 ), new CharMetrics( 600, 0, 71, 103, 529, 413 ),
+        new CharMetrics( 600, 0, 100, 203, 500, 313 ), new CharMetrics( 600, 0, 0, -18, 600, 580 ),
+        new CharMetrics( 600, 0, 88, 505, 512, 585 ), new CharMetrics( 600, 0, 86, 243, 474, 616 ),
+        new CharMetrics( 600, 0, 71, 24, 529, 515 ), new CharMetrics( 600, 0, 143, 230, 436, 616 ),
+        new CharMetrics( 600, 0, 138, 222, 433, 616 ), new CharMetrics( 600, 0, 205, 508, 468, 661 ),
+        new CharMetrics( 600, 0, -1, -142, 569, 439 ), new CharMetrics( 600, 0, 6, -70, 576, 580 ),
+        new CharMetrics( 600, 0, 196, 165, 404, 351 ), new CharMetrics( 600, 0, 205, -206, 387, 0 ),
+        new CharMetrics( 600, 0, 153, 230, 447, 616 ), new CharMetrics( 600, 0, 147, 196, 453, 580 ),
+        new CharMetrics( 600, 0, 47, 70, 592, 446 ), new CharMetrics( 600, 0, -56, -60, 656, 661 ),
+        new CharMetrics( 600, 0, -47, -60, 648, 661 ), new CharMetrics( 600, 0, -47, -60, 648, 661 ),
+        new CharMetrics( 600, 0, 99, -146, 502, 449 ), new CharMetrics( 600, 0, -9, 0, 609, 784 ),
+        new CharMetrics( 600, 0, -9, 0, 609, 784 ), new CharMetrics( 600, 0, -9, 0, 609, 780 ),
+        new CharMetrics( 600, 0, -9, 0, 609, 759 ), new CharMetrics( 600, 0, -9, 0, 609, 748 ),
+        new CharMetrics( 600, 0, -9, 0, 609, 801 ), new CharMetrics( 600, 0, -29, 0, 602, 562 ),
+        new CharMetrics( 600, 0, 22, -206, 560, 580 ), new CharMetrics( 600, 0, 25, 0, 560, 784 ),
+        new CharMetrics( 600, 0, 25, 0, 560, 784 ), new CharMetrics( 600, 0, 25, 0, 560, 780 ),
+        new CharMetrics( 600, 0, 25, 0, 560, 748 ), new CharMetrics( 600, 0, 77, 0, 523, 784 ),
+        new CharMetrics( 600, 0, 77, 0, 523, 784 ), new CharMetrics( 600, 0, 77, 0, 523, 780 ),
+        new CharMetrics( 600, 0, 77, 0, 523, 748 ), new CharMetrics( 600, 0, 30, 0, 594, 562 ),
+        new CharMetrics( 600, 0, 8, -12, 610, 759 ), new CharMetrics( 600, 0, 22, -18, 578, 784 ),
+        new CharMetrics( 600, 0, 22, -18, 578, 784 ), new CharMetrics( 600, 0, 22, -18, 578, 780 ),
+        new CharMetrics( 600, 0, 22, -18, 578, 759 ), new CharMetrics( 600, 0, 22, -18, 578, 748 ),
+        new CharMetrics( 600, 0, 81, 39, 520, 478 ), new CharMetrics( 600, 0, 22, -22, 578, 584 ),
+        new CharMetrics( 600, 0, 4, -18, 596, 784 ), new CharMetrics( 600, 0, 4, -18, 596, 784 ),
+        new CharMetrics( 600, 0, 4, -18, 596, 780 ), new CharMetrics( 600, 0, 4, -18, 596, 748 ),
+        new CharMetrics( 600, 0, 12, 0, 589, 784 ), new CharMetrics( 600, 0, 48, 0, 557, 562 ),
+        new CharMetrics( 600, 0, 22, -15, 596, 626 ), new CharMetrics( 600, 0, 35, -15, 570, 661 ),
+        new CharMetrics( 600, 0, 35, -15, 570, 661 ), new CharMetrics( 600, 0, 35, -15, 570, 657 ),
+        new CharMetrics( 600, 0, 35, -15, 570, 636 ), new CharMetrics( 600, 0, 35, -15, 570, 625 ),
+        new CharMetrics( 600, 0, 35, -15, 570, 678 ), new CharMetrics( 600, 0, -4, -15, 601, 454 ),
+        new CharMetrics( 600, 0, 40, -206, 545, 459 ), new CharMetrics( 600, 0, 40, -15, 563, 661 ),
+        new CharMetrics( 600, 0, 40, -15, 563, 661 ), new CharMetrics( 600, 0, 40, -15, 563, 657 ),
+        new CharMetrics( 600, 0, 40, -15, 563, 625 ), new CharMetrics( 600, 0, 77, 0, 523, 661 ),
+        new CharMetrics( 600, 0, 77, 0, 523, 661 ), new CharMetrics( 600, 0, 63, 0, 523, 657 ),
+        new CharMetrics( 600, 0, 77, 0, 523, 625 ), new CharMetrics( 600, 0, 58, -27, 543, 626 ),
+        new CharMetrics( 600, 0, 18, 0, 592, 636 ), new CharMetrics( 600, 0, 30, -15, 570, 661 ),
+        new CharMetrics( 600, 0, 30, -15, 570, 661 ), new CharMetrics( 600, 0, 30, -15, 570, 657 ),
+        new CharMetrics( 600, 0, 30, -15, 570, 636 ), new CharMetrics( 600, 0, 30, -15, 570, 625 ),
+        new CharMetrics( 600, 0, 71, 16, 529, 500 ), new CharMetrics( 600, 0, 30, -24, 570, 463 ),
+        new CharMetrics( 600, 0, -1, -15, 569, 661 ), new CharMetrics( 600, 0, -1, -15, 569, 661 ),
+        new CharMetrics( 600, 0, -1, -15, 569, 657 ), new CharMetrics( 600, 0, -1, -15, 569, 625 ),
+        new CharMetrics( 600, 0, -4, -142, 601, 661 ), new CharMetrics( 600, 0, -14, -142, 570, 626 ),
+        new CharMetrics( 600, 0, -4, -142, 601, 625 )};
+
+    MonospaceBold()
+    {
+        super( true, 626, -142, new CharMetrics( 0, 0, -113, -250, 749, 801 ), metrics );
+    }
+
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceBoldItalic.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceBoldItalic.java
new file mode 100644
index 0000000..2e815fa
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceBoldItalic.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: MonospaceBoldItalic.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class MonospaceBoldItalic
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 216, -15, 495, 572 ), new CharMetrics( 600, 0, 212, 277, 584, 562 ),
+        new CharMetrics( 600, 0, 88, -45, 640, 651 ), new CharMetrics( 600, 0, 87, -126, 629, 666 ),
+        new CharMetrics( 600, 0, 102, -15, 624, 616 ), new CharMetrics( 600, 0, 62, -15, 594, 543 ),
+        new CharMetrics( 600, 0, 230, 277, 542, 562 ), new CharMetrics( 600, 0, 266, -102, 592, 616 ),
+        new CharMetrics( 600, 0, 117, -102, 444, 616 ), new CharMetrics( 600, 0, 179, 219, 597, 601 ),
+        new CharMetrics( 600, 0, 114, 39, 596, 478 ), new CharMetrics( 600, 0, 99, -111, 430, 174 ),
+        new CharMetrics( 600, 0, 143, 203, 567, 313 ), new CharMetrics( 600, 0, 207, -15, 426, 171 ),
+        new CharMetrics( 600, 0, 91, -77, 626, 626 ), new CharMetrics( 600, 0, 136, -15, 592, 616 ),
+        new CharMetrics( 600, 0, 93, 0, 561, 616 ), new CharMetrics( 600, 0, 61, 0, 593, 616 ),
+        new CharMetrics( 600, 0, 72, -15, 571, 616 ), new CharMetrics( 600, 0, 82, 0, 558, 616 ),
+        new CharMetrics( 600, 0, 77, -15, 621, 601 ), new CharMetrics( 600, 0, 136, -15, 652, 616 ),
+        new CharMetrics( 600, 0, 147, 0, 622, 601 ), new CharMetrics( 600, 0, 115, -15, 604, 616 ),
+        new CharMetrics( 600, 0, 76, -15, 592, 616 ), new CharMetrics( 600, 0, 206, -15, 479, 425 ),
+        new CharMetrics( 600, 0, 99, -111, 480, 425 ), new CharMetrics( 600, 0, 121, 15, 612, 501 ),
+        new CharMetrics( 600, 0, 96, 118, 614, 398 ), new CharMetrics( 600, 0, 97, 15, 589, 501 ),
+        new CharMetrics( 600, 0, 183, -14, 591, 580 ), new CharMetrics( 600, 0, 66, -15, 641, 616 ),
+        new CharMetrics( 600, 0, -9, 0, 631, 562 ), new CharMetrics( 600, 0, 30, 0, 629, 562 ),
+        new CharMetrics( 600, 0, 75, -18, 674, 580 ), new CharMetrics( 600, 0, 30, 0, 664, 562 ),
+        new CharMetrics( 600, 0, 25, 0, 669, 562 ), new CharMetrics( 600, 0, 39, 0, 683, 562 ),
+        new CharMetrics( 600, 0, 75, -18, 674, 580 ), new CharMetrics( 600, 0, 20, 0, 699, 562 ),
+        new CharMetrics( 600, 0, 77, 0, 642, 562 ), new CharMetrics( 600, 0, 59, -18, 720, 562 ),
+        new CharMetrics( 600, 0, 21, 0, 691, 562 ), new CharMetrics( 600, 0, 39, 0, 635, 562 ),
+        new CharMetrics( 600, 0, -2, 0, 721, 562 ), new CharMetrics( 600, 0, 8, -12, 729, 562 ),
+        new CharMetrics( 600, 0, 74, -18, 645, 580 ), new CharMetrics( 600, 0, 48, 0, 642, 562 ),
+        new CharMetrics( 600, 0, 84, -138, 636, 580 ), new CharMetrics( 600, 0, 24, 0, 617, 562 ),
+        new CharMetrics( 600, 0, 54, -22, 672, 582 ), new CharMetrics( 600, 0, 86, 0, 678, 562 ),
+        new CharMetrics( 600, 0, 101, -18, 715, 562 ), new CharMetrics( 600, 0, 84, 0, 732, 562 ),
+        new CharMetrics( 600, 0, 84, 0, 737, 562 ), new CharMetrics( 600, 0, 12, 0, 689, 562 ),
+        new CharMetrics( 600, 0, 109, 0, 708, 562 ), new CharMetrics( 600, 0, 62, 0, 636, 562 ),
+        new CharMetrics( 600, 0, 223, -102, 606, 616 ), new CharMetrics( 600, 0, 223, -77, 496, 626 ),
+        new CharMetrics( 600, 0, 103, -102, 486, 616 ), new CharMetrics( 600, 0, 171, 250, 555, 616 ),
+        new CharMetrics( 600, 0, -27, -125, 584, -75 ), new CharMetrics( 600, 0, 297, 277, 487, 562 ),
+        new CharMetrics( 600, 0, 62, -15, 592, 454 ), new CharMetrics( 600, 0, 13, -15, 636, 626 ),
+        new CharMetrics( 600, 0, 81, -15, 631, 459 ), new CharMetrics( 600, 0, 61, -15, 644, 626 ),
+        new CharMetrics( 600, 0, 81, -15, 604, 454 ), new CharMetrics( 600, 0, 83, 0, 677, 626 ),
+        new CharMetrics( 600, 0, 41, -146, 673, 454 ), new CharMetrics( 600, 0, 18, 0, 614, 626 ),
+        new CharMetrics( 600, 0, 77, 0, 545, 658 ), new CharMetrics( 600, 0, 37, -146, 580, 658 ),
+        new CharMetrics( 600, 0, 33, 0, 642, 626 ), new CharMetrics( 600, 0, 77, 0, 545, 626 ),
+        new CharMetrics( 600, 0, -22, 0, 648, 454 ), new CharMetrics( 600, 0, 18, 0, 614, 454 ),
+        new CharMetrics( 600, 0, 71, -15, 622, 454 ), new CharMetrics( 600, 0, -31, -142, 622, 454 ),
+        new CharMetrics( 600, 0, 61, -142, 684, 454 ), new CharMetrics( 600, 0, 47, 0, 654, 454 ),
+        new CharMetrics( 600, 0, 67, -17, 607, 459 ), new CharMetrics( 600, 0, 118, -15, 566, 562 ),
+        new CharMetrics( 600, 0, 70, -15, 591, 439 ), new CharMetrics( 600, 0, 70, 0, 694, 439 ),
+        new CharMetrics( 600, 0, 53, 0, 711, 439 ), new CharMetrics( 600, 0, 6, 0, 670, 439 ),
+        new CharMetrics( 600, 0, -20, -142, 694, 439 ), new CharMetrics( 600, 0, 81, 0, 613, 439 ),
+        new CharMetrics( 600, 0, 204, -102, 595, 616 ), new CharMetrics( 600, 0, 202, -250, 504, 750 ),
+        new CharMetrics( 600, 0, 114, -102, 506, 616 ), new CharMetrics( 600, 0, 120, 153, 589, 356 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 77, 0, 545, 439 ),
+        new CharMetrics( 600, 0, 272, 508, 503, 661 ), new CharMetrics( 600, 0, 313, 508, 608, 661 ),
+        new CharMetrics( 600, 0, 212, 483, 606, 657 ), new CharMetrics( 600, 0, 200, 493, 642, 636 ),
+        new CharMetrics( 600, 0, 195, 505, 636, 585 ), new CharMetrics( 600, 0, 217, 468, 651, 631 ),
+        new CharMetrics( 600, 0, 346, 485, 490, 625 ), new CharMetrics( 600, 0, 244, 485, 592, 625 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 319, 481, 528, 678 ),
+        new CharMetrics( 600, 0, 169, -206, 367, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 172, 488, 728, 661 ), new CharMetrics( 600, 0, 144, -199, 350, 0 ),
+        new CharMetrics( 600, 0, 238, 493, 632, 667 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 197, -146, 477, 449 ), new CharMetrics( 600, 0, 121, -49, 604, 614 ),
+        new CharMetrics( 600, 0, 107, -28, 650, 611 ), new CharMetrics( 600, 0, 77, 49, 643, 517 ),
+        new CharMetrics( 600, 0, 98, 0, 709, 562 ), new CharMetrics( 600, 0, 218, -175, 488, 675 ),
+        new CharMetrics( 600, 0, 74, -70, 619, 580 ), new CharMetrics( 600, 0, 244, 485, 592, 625 ),
+        new CharMetrics( 600, 0, 53, -18, 667, 580 ), new CharMetrics( 600, 0, 189, 196, 526, 580 ),
+        new CharMetrics( 600, 0, 63, 70, 638, 446 ), new CharMetrics( 600, 0, 135, 103, 617, 413 ),
+        new CharMetrics( 600, 0, 143, 203, 567, 313 ), new CharMetrics( 600, 0, 53, -18, 667, 580 ),
+        new CharMetrics( 600, 0, 195, 505, 636, 585 ), new CharMetrics( 600, 0, 173, 243, 569, 616 ),
+        new CharMetrics( 600, 0, 76, 24, 614, 515 ), new CharMetrics( 600, 0, 192, 230, 541, 616 ),
+        new CharMetrics( 600, 0, 193, 222, 525, 616 ), new CharMetrics( 600, 0, 313, 508, 608, 661 ),
+        new CharMetrics( 600, 0, 50, -142, 591, 439 ), new CharMetrics( 600, 0, 61, -70, 699, 580 ),
+        new CharMetrics( 600, 0, 249, 165, 461, 351 ), new CharMetrics( 600, 0, 169, -206, 367, 0 ),
+        new CharMetrics( 600, 0, 213, 230, 514, 616 ), new CharMetrics( 600, 0, 189, 196, 542, 580 ),
+        new CharMetrics( 600, 0, 72, 70, 647, 446 ), new CharMetrics( 600, 0, 14, -60, 706, 661 ),
+        new CharMetrics( 600, 0, 23, -60, 715, 661 ), new CharMetrics( 600, 0, 8, -60, 698, 661 ),
+        new CharMetrics( 600, 0, 101, -146, 509, 449 ), new CharMetrics( 600, 0, -9, 0, 631, 784 ),
+        new CharMetrics( 600, 0, -9, 0, 665, 784 ), new CharMetrics( 600, 0, -9, 0, 631, 780 ),
+        new CharMetrics( 600, 0, -9, 0, 638, 759 ), new CharMetrics( 600, 0, -9, 0, 631, 748 ),
+        new CharMetrics( 600, 0, -9, 0, 631, 801 ), new CharMetrics( 600, 0, -29, 0, 707, 562 ),
+        new CharMetrics( 600, 0, 74, -206, 674, 580 ), new CharMetrics( 600, 0, 25, 0, 669, 784 ),
+        new CharMetrics( 600, 0, 25, 0, 669, 784 ), new CharMetrics( 600, 0, 25, 0, 669, 780 ),
+        new CharMetrics( 600, 0, 25, 0, 669, 748 ), new CharMetrics( 600, 0, 77, 0, 642, 784 ),
+        new CharMetrics( 600, 0, 77, 0, 642, 784 ), new CharMetrics( 600, 0, 77, 0, 642, 780 ),
+        new CharMetrics( 600, 0, 77, 0, 642, 748 ), new CharMetrics( 600, 0, 30, 0, 664, 562 ),
+        new CharMetrics( 600, 0, 8, -12, 729, 759 ), new CharMetrics( 600, 0, 74, -18, 645, 784 ),
+        new CharMetrics( 600, 0, 74, -18, 645, 784 ), new CharMetrics( 600, 0, 74, -18, 645, 780 ),
+        new CharMetrics( 600, 0, 74, -18, 668, 759 ), new CharMetrics( 600, 0, 74, -18, 645, 748 ),
+        new CharMetrics( 600, 0, 105, 39, 606, 478 ), new CharMetrics( 600, 0, 48, -22, 672, 584 ),
+        new CharMetrics( 600, 0, 101, -18, 715, 784 ), new CharMetrics( 600, 0, 101, -18, 715, 784 ),
+        new CharMetrics( 600, 0, 101, -18, 715, 780 ), new CharMetrics( 600, 0, 101, -18, 715, 748 ),
+        new CharMetrics( 600, 0, 109, 0, 708, 784 ), new CharMetrics( 600, 0, 48, 0, 619, 562 ),
+        new CharMetrics( 600, 0, 22, -15, 628, 626 ), new CharMetrics( 600, 0, 62, -15, 592, 661 ),
+        new CharMetrics( 600, 0, 62, -15, 608, 661 ), new CharMetrics( 600, 0, 62, -15, 592, 657 ),
+        new CharMetrics( 600, 0, 62, -15, 642, 636 ), new CharMetrics( 600, 0, 62, -15, 592, 625 ),
+        new CharMetrics( 600, 0, 62, -15, 592, 678 ), new CharMetrics( 600, 0, 21, -15, 651, 454 ),
+        new CharMetrics( 600, 0, 81, -206, 631, 459 ), new CharMetrics( 600, 0, 81, -15, 604, 661 ),
+        new CharMetrics( 600, 0, 81, -15, 608, 661 ), new CharMetrics( 600, 0, 81, -15, 606, 657 ),
+        new CharMetrics( 600, 0, 81, -15, 604, 625 ), new CharMetrics( 600, 0, 77, 0, 545, 661 ),
+        new CharMetrics( 600, 0, 77, 0, 608, 661 ), new CharMetrics( 600, 0, 77, 0, 566, 657 ),
+        new CharMetrics( 600, 0, 77, 0, 552, 625 ), new CharMetrics( 600, 0, 93, -27, 661, 626 ),
+        new CharMetrics( 600, 0, 18, 0, 642, 636 ), new CharMetrics( 600, 0, 71, -15, 622, 661 ),
+        new CharMetrics( 600, 0, 71, -15, 622, 661 ), new CharMetrics( 600, 0, 71, -15, 622, 657 ),
+        new CharMetrics( 600, 0, 71, -15, 642, 636 ), new CharMetrics( 600, 0, 71, -15, 622, 625 ),
+        new CharMetrics( 600, 0, 114, 16, 596, 500 ), new CharMetrics( 600, 0, 55, -24, 637, 463 ),
+        new CharMetrics( 600, 0, 70, -15, 591, 661 ), new CharMetrics( 600, 0, 70, -15, 608, 661 ),
+        new CharMetrics( 600, 0, 70, -15, 591, 657 ), new CharMetrics( 600, 0, 70, -15, 591, 625 ),
+        new CharMetrics( 600, 0, -20, -142, 694, 661 ), new CharMetrics( 600, 0, -31, -142, 622, 626 ),
+        new CharMetrics( 600, 0, -20, -142, 694, 625 )};
+
+    MonospaceBoldItalic()
+    {
+        super( true, 626, -142, new CharMetrics( 0, 0, -56, -250, 868, 801 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceItalic.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceItalic.java
new file mode 100644
index 0000000..5acd822
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/MonospaceItalic.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: MonospaceItalic.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class MonospaceItalic
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 243, -15, 464, 572 ), new CharMetrics( 600, 0, 273, 328, 532, 562 ),
+        new CharMetrics( 600, 0, 133, -32, 596, 639 ), new CharMetrics( 600, 0, 108, -126, 596, 662 ),
+        new CharMetrics( 600, 0, 134, -15, 599, 622 ), new CharMetrics( 600, 0, 87, -15, 580, 543 ),
+        new CharMetrics( 600, 0, 283, 328, 495, 562 ), new CharMetrics( 600, 0, 313, -108, 572, 622 ),
+        new CharMetrics( 600, 0, 137, -108, 396, 622 ), new CharMetrics( 600, 0, 212, 257, 580, 607 ),
+        new CharMetrics( 600, 0, 129, 44, 580, 470 ), new CharMetrics( 600, 0, 157, -112, 370, 122 ),
+        new CharMetrics( 600, 0, 152, 231, 558, 285 ), new CharMetrics( 600, 0, 238, -15, 382, 109 ),
+        new CharMetrics( 600, 0, 112, -80, 604, 629 ), new CharMetrics( 600, 0, 154, -15, 575, 622 ),
+        new CharMetrics( 600, 0, 98, 0, 515, 622 ), new CharMetrics( 600, 0, 70, 0, 568, 622 ),
+        new CharMetrics( 600, 0, 82, -15, 538, 622 ), new CharMetrics( 600, 0, 108, 0, 541, 622 ),
+        new CharMetrics( 600, 0, 99, -15, 589, 607 ), new CharMetrics( 600, 0, 155, -15, 629, 622 ),
+        new CharMetrics( 600, 0, 182, 0, 612, 607 ), new CharMetrics( 600, 0, 132, -15, 588, 622 ),
+        new CharMetrics( 600, 0, 93, -15, 574, 622 ), new CharMetrics( 600, 0, 238, -15, 441, 385 ),
+        new CharMetrics( 600, 0, 157, -112, 441, 385 ), new CharMetrics( 600, 0, 96, 42, 610, 472 ),
+        new CharMetrics( 600, 0, 109, 138, 600, 376 ), new CharMetrics( 600, 0, 85, 42, 599, 472 ),
+        new CharMetrics( 600, 0, 222, -15, 583, 572 ), new CharMetrics( 600, 0, 127, -15, 582, 622 ),
+        new CharMetrics( 600, 0, 3, 0, 607, 562 ), new CharMetrics( 600, 0, 43, 0, 616, 562 ),
+        new CharMetrics( 600, 0, 93, -18, 655, 580 ), new CharMetrics( 600, 0, 43, 0, 645, 562 ),
+        new CharMetrics( 600, 0, 53, 0, 660, 562 ), new CharMetrics( 600, 0, 53, 0, 660, 562 ),
+        new CharMetrics( 600, 0, 83, -18, 645, 580 ), new CharMetrics( 600, 0, 32, 0, 687, 562 ),
+        new CharMetrics( 600, 0, 96, 0, 623, 562 ), new CharMetrics( 600, 0, 52, -18, 685, 562 ),
+        new CharMetrics( 600, 0, 38, 0, 671, 562 ), new CharMetrics( 600, 0, 47, 0, 607, 562 ),
+        new CharMetrics( 600, 0, 4, 0, 715, 562 ), new CharMetrics( 600, 0, 7, -13, 712, 562 ),
+        new CharMetrics( 600, 0, 94, -18, 625, 580 ), new CharMetrics( 600, 0, 79, 0, 644, 562 ),
+        new CharMetrics( 600, 0, 95, -138, 625, 580 ), new CharMetrics( 600, 0, 38, 0, 598, 562 ),
+        new CharMetrics( 600, 0, 76, -20, 650, 580 ), new CharMetrics( 600, 0, 108, 0, 665, 562 ),
+        new CharMetrics( 600, 0, 125, -18, 702, 562 ), new CharMetrics( 600, 0, 105, -13, 723, 562 ),
+        new CharMetrics( 600, 0, 106, -13, 722, 562 ), new CharMetrics( 600, 0, 23, 0, 675, 562 ),
+        new CharMetrics( 600, 0, 133, 0, 695, 562 ), new CharMetrics( 600, 0, 86, 0, 610, 562 ),
+        new CharMetrics( 600, 0, 246, -108, 574, 622 ), new CharMetrics( 600, 0, 249, -80, 468, 629 ),
+        new CharMetrics( 600, 0, 135, -108, 463, 622 ), new CharMetrics( 600, 0, 175, 354, 587, 622 ),
+        new CharMetrics( 600, 0, -27, -125, 584, -75 ), new CharMetrics( 600, 0, 343, 328, 457, 562 ),
+        new CharMetrics( 600, 0, 76, -15, 569, 441 ), new CharMetrics( 600, 0, 29, -15, 625, 629 ),
+        new CharMetrics( 600, 0, 106, -15, 608, 441 ), new CharMetrics( 600, 0, 85, -15, 640, 629 ),
+        new CharMetrics( 600, 0, 106, -15, 598, 441 ), new CharMetrics( 600, 0, 114, 0, 662, 629 ),
+        new CharMetrics( 600, 0, 61, -157, 657, 441 ), new CharMetrics( 600, 0, 33, 0, 592, 629 ),
+        new CharMetrics( 600, 0, 95, 0, 515, 657 ), new CharMetrics( 600, 0, 52, -157, 550, 657 ),
+        new CharMetrics( 600, 0, 58, 0, 633, 629 ), new CharMetrics( 600, 0, 95, 0, 515, 629 ),
+        new CharMetrics( 600, 0, -5, 0, 615, 441 ), new CharMetrics( 600, 0, 26, 0, 585, 441 ),
+        new CharMetrics( 600, 0, 102, -15, 588, 441 ), new CharMetrics( 600, 0, -24, -157, 605, 441 ),
+        new CharMetrics( 600, 0, 85, -157, 682, 441 ), new CharMetrics( 600, 0, 60, 0, 636, 441 ),
+        new CharMetrics( 600, 0, 78, -15, 584, 441 ), new CharMetrics( 600, 0, 167, -15, 561, 561 ),
+        new CharMetrics( 600, 0, 101, -15, 572, 426 ), new CharMetrics( 600, 0, 90, -10, 681, 426 ),
+        new CharMetrics( 600, 0, 76, -10, 695, 426 ), new CharMetrics( 600, 0, 20, 0, 655, 426 ),
+        new CharMetrics( 600, 0, -4, -157, 683, 426 ), new CharMetrics( 600, 0, 99, 0, 593, 426 ),
+        new CharMetrics( 600, 0, 233, -108, 569, 622 ), new CharMetrics( 600, 0, 222, -250, 485, 750 ),
+        new CharMetrics( 600, 0, 140, -108, 477, 622 ), new CharMetrics( 600, 0, 116, 197, 600, 320 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 95, 0, 515, 426 ),
+        new CharMetrics( 600, 0, 294, 497, 484, 672 ), new CharMetrics( 600, 0, 348, 497, 612, 672 ),
+        new CharMetrics( 600, 0, 229, 477, 581, 654 ), new CharMetrics( 600, 0, 212, 489, 629, 606 ),
+        new CharMetrics( 600, 0, 232, 525, 600, 565 ), new CharMetrics( 600, 0, 279, 501, 576, 609 ),
+        new CharMetrics( 600, 0, 360, 477, 466, 580 ), new CharMetrics( 600, 0, 262, 492, 570, 595 ),
+        new CharMetrics( 600, 0, 0, 0, 0, 0 ), new CharMetrics( 600, 0, 332, 463, 500, 627 ),
+        new CharMetrics( 600, 0, 197, -151, 344, 10 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 239, 497, 683, 672 ), new CharMetrics( 600, 0, 207, -151, 348, 0 ),
+        new CharMetrics( 600, 0, 262, 492, 614, 669 ), new CharMetrics( 600, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 600, 0, 225, -157, 445, 430 ), new CharMetrics( 600, 0, 151, -49, 588, 614 ),
+        new CharMetrics( 600, 0, 124, -21, 621, 611 ), new CharMetrics( 600, 0, 94, 58, 628, 506 ),
+        new CharMetrics( 600, 0, 120, 0, 693, 562 ), new CharMetrics( 600, 0, 238, -175, 469, 675 ),
+        new CharMetrics( 600, 0, 104, -78, 590, 580 ), new CharMetrics( 600, 0, 262, 492, 570, 595 ),
+        new CharMetrics( 600, 0, 53, -18, 667, 580 ), new CharMetrics( 600, 0, 209, 249, 512, 580 ),
+        new CharMetrics( 600, 0, 92, 70, 652, 446 ), new CharMetrics( 600, 0, 155, 108, 591, 369 ),
+        new CharMetrics( 600, 0, 152, 231, 558, 285 ), new CharMetrics( 600, 0, 53, -18, 667, 580 ),
+        new CharMetrics( 600, 0, 232, 525, 600, 565 ), new CharMetrics( 600, 0, 214, 269, 576, 622 ),
+        new CharMetrics( 600, 0, 96, 44, 594, 558 ), new CharMetrics( 600, 0, 230, 249, 535, 622 ),
+        new CharMetrics( 600, 0, 213, 240, 501, 622 ), new CharMetrics( 600, 0, 348, 497, 612, 672 ),
+        new CharMetrics( 600, 0, 72, -157, 572, 426 ), new CharMetrics( 600, 0, 100, -78, 630, 562 ),
+        new CharMetrics( 600, 0, 275, 189, 434, 327 ), new CharMetrics( 600, 0, 197, -151, 344, 10 ),
+        new CharMetrics( 600, 0, 231, 249, 491, 622 ), new CharMetrics( 600, 0, 210, 249, 535, 580 ),
+        new CharMetrics( 600, 0, 58, 70, 618, 446 ), new CharMetrics( 600, 0, 65, -57, 674, 665 ),
+        new CharMetrics( 600, 0, 65, -57, 669, 665 ), new CharMetrics( 600, 0, 73, -56, 659, 666 ),
+        new CharMetrics( 600, 0, 105, -157, 466, 430 ), new CharMetrics( 600, 0, 3, 0, 607, 793 ),
+        new CharMetrics( 600, 0, 3, 0, 658, 793 ), new CharMetrics( 600, 0, 3, 0, 607, 775 ),
+        new CharMetrics( 600, 0, 3, 0, 656, 732 ), new CharMetrics( 600, 0, 3, 0, 607, 731 ),
+        new CharMetrics( 600, 0, 3, 0, 607, 753 ), new CharMetrics( 600, 0, 3, 0, 655, 562 ),
+        new CharMetrics( 600, 0, 93, -151, 658, 580 ), new CharMetrics( 600, 0, 53, 0, 660, 793 ),
+        new CharMetrics( 600, 0, 53, 0, 668, 793 ), new CharMetrics( 600, 0, 53, 0, 660, 775 ),
+        new CharMetrics( 600, 0, 53, 0, 660, 731 ), new CharMetrics( 600, 0, 96, 0, 623, 793 ),
+        new CharMetrics( 600, 0, 96, 0, 638, 793 ), new CharMetrics( 600, 0, 96, 0, 623, 775 ),
+        new CharMetrics( 600, 0, 96, 0, 623, 731 ), new CharMetrics( 600, 0, 43, 0, 645, 562 ),
+        new CharMetrics( 600, 0, 7, -13, 712, 732 ), new CharMetrics( 600, 0, 94, -18, 625, 793 ),
+        new CharMetrics( 600, 0, 94, -18, 638, 793 ), new CharMetrics( 600, 0, 94, -18, 625, 775 ),
+        new CharMetrics( 600, 0, 94, -18, 656, 732 ), new CharMetrics( 600, 0, 94, -18, 625, 731 ),
+        new CharMetrics( 600, 0, 103, 43, 607, 470 ), new CharMetrics( 600, 0, 94, -80, 625, 629 ),
+        new CharMetrics( 600, 0, 125, -18, 702, 793 ), new CharMetrics( 600, 0, 125, -18, 702, 793 ),
+        new CharMetrics( 600, 0, 125, -18, 702, 775 ), new CharMetrics( 600, 0, 125, -18, 702, 731 ),
+        new CharMetrics( 600, 0, 133, 0, 695, 793 ), new CharMetrics( 600, 0, 79, 0, 606, 562 ),
+        new CharMetrics( 600, 0, 48, -15, 617, 629 ), new CharMetrics( 600, 0, 76, -15, 569, 672 ),
+        new CharMetrics( 600, 0, 76, -15, 612, 672 ), new CharMetrics( 600, 0, 76, -15, 581, 654 ),
+        new CharMetrics( 600, 0, 76, -15, 629, 606 ), new CharMetrics( 600, 0, 76, -15, 570, 595 ),
+        new CharMetrics( 600, 0, 76, -15, 569, 627 ), new CharMetrics( 600, 0, 41, -15, 626, 441 ),
+        new CharMetrics( 600, 0, 106, -151, 614, 441 ), new CharMetrics( 600, 0, 106, -15, 598, 672 ),
+        new CharMetrics( 600, 0, 106, -15, 612, 672 ), new CharMetrics( 600, 0, 106, -15, 598, 654 ),
+        new CharMetrics( 600, 0, 106, -15, 598, 595 ), new CharMetrics( 600, 0, 95, 0, 515, 672 ),
+        new CharMetrics( 600, 0, 95, 0, 612, 672 ), new CharMetrics( 600, 0, 95, 0, 551, 654 ),
+        new CharMetrics( 600, 0, 95, 0, 540, 595 ), new CharMetrics( 600, 0, 102, -15, 639, 629 ),
+        new CharMetrics( 600, 0, 26, 0, 629, 606 ), new CharMetrics( 600, 0, 102, -15, 588, 672 ),
+        new CharMetrics( 600, 0, 102, -15, 612, 672 ), new CharMetrics( 600, 0, 102, -15, 588, 654 ),
+        new CharMetrics( 600, 0, 102, -15, 629, 606 ), new CharMetrics( 600, 0, 102, -15, 588, 595 ),
+        new CharMetrics( 600, 0, 136, 48, 573, 467 ), new CharMetrics( 600, 0, 102, -80, 588, 506 ),
+        new CharMetrics( 600, 0, 101, -15, 572, 672 ), new CharMetrics( 600, 0, 101, -15, 602, 672 ),
+        new CharMetrics( 600, 0, 101, -15, 572, 654 ), new CharMetrics( 600, 0, 101, -15, 572, 595 ),
+        new CharMetrics( 600, 0, -4, -157, 683, 672 ), new CharMetrics( 600, 0, -24, -157, 605, 629 ),
+        new CharMetrics( 600, 0, -4, -157, 683, 595 )};
+
+    MonospaceItalic()
+    {
+        super( true, 629, -157, new CharMetrics( 0, 0, -28, -250, 742, 805 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/PBMReader.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/PBMReader.java
new file mode 100644
index 0000000..0bbe956
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/PBMReader.java
@@ -0,0 +1,309 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Portable_pixmap">PBM</a> images reader.
+ *
+ * @version $Id: PBMReader.java 724307 2008-12-08 11:15:00Z vsiveton $
+ */
+class PBMReader
+{
+    static final int TYPE_PBM = 1;
+
+    static final int TYPE_PGM = 2;
+
+    static final int TYPE_PPM = 3;
+
+    private static final String BAD_FILE_FORMAT = "bad file format";
+
+    private static final String UNSUPPORTED_TYPE = "unsupported file type";
+
+    private static final String UNSUPPORTED_FORMAT = "unsupported data format";
+
+    private static final String UNSUPPORTED_DEPTH = "unsupported color depth";
+
+    protected int type;
+
+    protected boolean binary;
+
+    protected int width;
+
+    protected int height;
+
+    protected int maxValue;
+
+    private int bytesPerLine;
+
+    private InputStream stream;
+
+    PBMReader( String fileName )
+        throws Exception
+    {
+        HeaderReader header = new HeaderReader();
+
+        int length = header.read( fileName );
+
+        if ( type != TYPE_PPM )
+        {
+            throw new Exception( UNSUPPORTED_TYPE );
+        }
+
+        if ( !binary )
+        {
+            throw new Exception( UNSUPPORTED_FORMAT );
+        }
+
+        if ( maxValue > 255 )
+        {
+            throw new Exception( UNSUPPORTED_DEPTH );
+        }
+
+        switch ( type )
+        {
+            case TYPE_PBM:
+                bytesPerLine = ( width + 7 ) / 8;
+                break;
+            case TYPE_PGM:
+                bytesPerLine = width;
+                break;
+            case TYPE_PPM:
+                bytesPerLine = 3 * width;
+                break;
+        }
+
+        stream = new BufferedInputStream( new FileInputStream( fileName ) );
+
+        skip( length );
+    }
+
+    int type()
+    {
+        return type;
+    }
+
+    int width()
+    {
+        return width;
+    }
+
+    int height()
+    {
+        return height;
+    }
+
+    int maxValue()
+    {
+        return maxValue;
+    }
+
+    int bytesPerLine()
+    {
+        return bytesPerLine;
+    }
+
+    long skip( long count )
+        throws IOException
+    {
+        long skipped = stream.skip( count );
+
+        if ( skipped < count )
+        {
+            byte[] b = new byte[512];
+            while ( skipped < count )
+            {
+                int len = (int) Math.min( b.length, ( count - skipped ) );
+                int n = stream.read( b, 0, len );
+                if ( n < 0 )
+                {
+                    break; // end of file
+                }
+                skipped += n;
+            }
+        }
+
+        return skipped;
+    }
+
+    int read( byte[] b, int off, int len )
+        throws IOException
+    {
+        int count = 0;
+        while ( count < len )
+        {
+            int n = stream.read( b, off + count, len - count );
+            if ( n < 0 )
+            {
+                break; // end of file
+            }
+            count += n;
+        }
+        return count;
+    }
+
+    // -----------------------------------------------------------------------
+
+    class HeaderReader
+    {
+
+        private Reader reader;
+
+        private int offset;
+
+        int read( String fileName )
+            throws Exception
+        {
+            String field;
+
+            reader = new BufferedReader( new InputStreamReader( new FileInputStream( fileName ), "US-ASCII" ) );
+            offset = 0;
+
+            field = getField();
+            if ( field.length() != 2 || field.charAt( 0 ) != 'P' )
+            {
+                reader.close();
+                throw new Exception( BAD_FILE_FORMAT );
+            }
+            switch ( field.charAt( 1 ) )
+            {
+                case '1':
+                case '4':
+                    type = TYPE_PBM;
+                    break;
+                case '2':
+                case '5':
+                    type = TYPE_PGM;
+                    break;
+                case '3':
+                case '6':
+                    type = TYPE_PPM;
+                    break;
+                default:
+                    reader.close();
+                    throw new Exception( BAD_FILE_FORMAT );
+            }
+            if ( field.charAt( 1 ) > '3' )
+            {
+                binary = true;
+            }
+            else
+            {
+                binary = false;
+            }
+
+            try
+            {
+                width = Integer.parseInt( getField() );
+                height = Integer.parseInt( getField() );
+                if ( type == TYPE_PBM )
+                {
+                    maxValue = 1;
+                }
+                else
+                {
+                    maxValue = Integer.parseInt( getField() );
+                }
+            }
+            catch ( NumberFormatException e )
+            {
+                reader.close();
+                throw new Exception( BAD_FILE_FORMAT );
+            }
+
+            reader.close();
+
+            return offset;
+        }
+
+        private String getField()
+            throws IOException
+        {
+            char c;
+            StringBuffer field = new StringBuffer();
+
+            try
+            {
+                do
+                {
+                    while ( ( c = getChar() ) == '#' )
+                    {
+                        skipComment();
+                    }
+                }
+                while ( Character.isWhitespace( c ) );
+
+                field.append( c );
+
+                while ( !Character.isWhitespace( c = getChar() ) )
+                {
+                    if ( c == '#' )
+                    {
+                        skipComment();
+                        break;
+                    }
+                    field.append( c );
+                }
+            }
+            catch ( EOFException ignore )
+            {
+                // nop
+            }
+
+            return field.toString();
+        }
+
+        private char getChar()
+            throws IOException, EOFException
+        {
+            int c = reader.read();
+            if ( c < 0 )
+            {
+                throw new EOFException();
+            }
+            offset += 1;
+            return (char) c;
+        }
+
+        private void skipComment()
+            throws IOException
+        {
+            try
+            {
+                while ( getChar() != '\n' )
+                {
+                    ;
+                }
+            }
+            catch ( EOFException ignore )
+            {
+                // nop
+            }
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RomanNumerals.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RomanNumerals.java
new file mode 100644
index 0000000..2c52a13
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RomanNumerals.java
@@ -0,0 +1,58 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: RomanNumerals.java 703394 2008-10-10 10:53:51Z vsiveton $
+ */
+class RomanNumerals
+{
+    private static final int[] NUMBERS = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
+
+    private static final String[] UPPER_CASE_LETTERS =
+        {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
+
+    private static final String[] LOWER_CASE_LETTERS =
+        {"m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i"};
+
+    // -----------------------------------------------------------------------
+
+    static String toString( int n )
+    {
+        return toString( n, false );
+    }
+
+    static String toString( int n, boolean lowerCase )
+    {
+        StringBuffer roman = new StringBuffer();
+        String[] letters = lowerCase ? LOWER_CASE_LETTERS : UPPER_CASE_LETTERS;
+
+        for ( int i = 0; i < NUMBERS.length; ++i )
+        {
+            while ( n >= NUMBERS[i] )
+            {
+                roman.append( letters[i] );
+                n -= NUMBERS[i];
+            }
+        }
+
+        return roman.toString();
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RtfSink.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RtfSink.java
new file mode 100644
index 0000000..f4a4cc2
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RtfSink.java
@@ -0,0 +1,2244 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.awt.Color;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import org.apache.maven.doxia.sink.AbstractTextSink;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Rich_Text_Format">RTF</a> Sink implementation.
+ *
+ * @version $Id: RtfSink.java 807178 2009-08-24 12:19:14Z vsiveton $
+ * @since 1.0
+ */
+public class RtfSink
+    extends AbstractTextSink
+{
+    /** Paper width, 21 cm */
+    public static final double DEFAULT_PAPER_WIDTH = 21.;   /*cm*/
+
+    /** Paper height, 29.7 cm */
+    public static final double DEFAULT_PAPER_HEIGHT = 29.7; /*cm*/
+
+    /** Paper top margin, 2 cm */
+    public static final double DEFAULT_TOP_MARGIN = 2.;    /*cm*/
+
+    /** Paper bottom margin, 2 cm */
+    public static final double DEFAULT_BOTTOM_MARGIN = 2.; /*cm*/
+
+    /** Paper left margin, 2 cm */
+    public static final double DEFAULT_LEFT_MARGIN = 2.;   /*cm*/
+
+    /** Paper right margin, 2 cm */
+    public static final double DEFAULT_RIGHT_MARGIN = 2.;  /*cm*/
+
+    /** Font size, 10 pts */
+    public static final int DEFAULT_FONT_SIZE = 10; /*pts*/
+
+    /** Spacing, 10 pts */
+    public static final int DEFAULT_SPACING = 10;   /*pts*/
+
+    /** Resolution, 72 dpi */
+    public static final int DEFAULT_RESOLUTION = 72; /*dpi*/
+
+    /** Image format, bmp */
+    public static final String DEFAULT_IMAGE_FORMAT = "bmp";
+
+    /** Image type, palette */
+    public static final String DEFAULT_IMAGE_TYPE = "palette";
+
+    /** Data format, ascii */
+    public static final String DEFAULT_DATA_FORMAT = "ascii";
+
+    /** Codepage, 1252 */
+    public static final int DEFAULT_CODE_PAGE = 1252;
+
+    /** Constant <code>DEFAULT_CHAR_SET=0</code> */
+    public static final int DEFAULT_CHAR_SET = 0;
+
+    /** Constant <code>IMG_FORMAT_BMP="bmp"</code> */
+    public static final String IMG_FORMAT_BMP = "bmp";
+
+    /** Constant <code>IMG_FORMAT_WMF="wmf"</code> */
+    public static final String IMG_FORMAT_WMF = "wmf";
+
+    /** Constant <code>IMG_TYPE_PALETTE="palette"</code> */
+    public static final String IMG_TYPE_PALETTE = "palette";
+
+    /** Constant <code>IMG_TYPE_RGB="rgb"</code> */
+    public static final String IMG_TYPE_RGB = "rgb";
+
+    /** Constant <code>IMG_DATA_ASCII="ascii"</code> */
+    public static final String IMG_DATA_ASCII = "ascii";
+
+    /** Constant <code>IMG_DATA_RAW="raw"</code> */
+    public static final String IMG_DATA_RAW = "raw";
+
+    /** Constant <code>STYLE_ROMAN=0</code> */
+    public static final int STYLE_ROMAN = 0;
+
+    /** Constant <code>STYLE_ITALIC=1</code> */
+    public static final int STYLE_ITALIC = 1;
+
+    /** Constant <code>STYLE_BOLD=2</code> */
+    public static final int STYLE_BOLD = 2;
+
+    /** Constant <code>STYLE_TYPEWRITER=3</code> */
+    public static final int STYLE_TYPEWRITER = 3;
+
+    private static final int CONTEXT_UNDEFINED = 0;
+
+    private static final int CONTEXT_VERBATIM = 1;
+
+    private static final int CONTEXT_TABLE = 2;
+
+    private static final int UNIT_MILLIMETER = 1;
+
+    private static final int UNIT_CENTIMETER = 2;
+
+    private static final int UNIT_INCH = 3;
+
+    private static final int UNIT_PIXEL = 4;
+
+    private static final int LIST_INDENT = 300; /*twips*/
+
+    private static final String LIST_ITEM_HEADER = "-  ";
+
+    private static final int DEFINITION_INDENT = 300; /*twips*/
+
+    private static final int CELL_HORIZONTAL_PAD = 60; /*twips*/
+
+    private static final int CELL_VERTICAL_PAD = 20;   /*twips*/
+
+    private static final int BORDER_WIDTH = 15; /*twips*/
+
+    private double paperWidth = DEFAULT_PAPER_WIDTH;
+
+    private double paperHeight = DEFAULT_PAPER_HEIGHT;
+
+    private double topMargin = DEFAULT_TOP_MARGIN;
+
+    private double bottomMargin = DEFAULT_BOTTOM_MARGIN;
+
+    private double leftMargin = DEFAULT_LEFT_MARGIN;
+
+    private double rightMargin = DEFAULT_RIGHT_MARGIN;
+
+    protected int fontSize = DEFAULT_FONT_SIZE;
+
+    private int resolution = DEFAULT_RESOLUTION;
+
+    private String imageFormat = DEFAULT_IMAGE_FORMAT;
+
+    private String imageType = DEFAULT_IMAGE_TYPE;
+
+    private String imageDataFormat = DEFAULT_DATA_FORMAT;
+
+    private boolean imageCompression = true;
+
+    private int codePage = DEFAULT_CODE_PAGE;
+
+    private int charSet = DEFAULT_CHAR_SET;
+
+    private final Hashtable fontTable;
+
+    private Context context;
+
+    private Paragraph paragraph;
+
+    protected Indentation indentation;
+
+    protected Space space;
+
+    private int listItemIndent;
+
+    private final Vector numbering;
+
+    private final Vector itemNumber;
+
+    private int style = STYLE_ROMAN;
+
+    private int sectionLevel;
+
+    private boolean emptyHeader;
+
+    private StringBuffer verbatim;
+
+    private boolean frame;
+
+    private Table table;
+
+    private Row row;
+
+    private Cell cell;
+
+    private Line line;
+
+    protected PrintWriter writer;
+
+    protected OutputStream stream; // for raw image data
+
+    /** Map of warn messages with a String as key to describe the error type and a Set as value.
+     * Using to reduce warn messages. */
+    private Map warnMessages;
+
+    // -----------------------------------------------------------------------
+
+    /**
+     * <p>Constructor for RtfSink.</p>
+     *
+     * @throws java.io.IOException if any
+     */
+    protected RtfSink()
+        throws IOException
+    {
+        this( System.out );
+    }
+
+    /**
+     * <p>Constructor for RtfSink.</p>
+     *
+     * @param output not null
+     * @throws java.io.IOException if any
+     */
+    protected RtfSink( OutputStream output )
+        throws IOException
+    {
+        this( output, null );
+    }
+
+    /**
+     * <p>Constructor for RtfSink.</p>
+     *
+     * @param output not null
+     * @param encoding a valid charset
+     * @throws java.io.IOException if any
+     */
+    protected RtfSink( OutputStream output, String encoding )
+        throws IOException
+    {
+        this.fontTable = new Hashtable();
+        this.numbering = new Vector();
+        this.itemNumber = new Vector();
+
+        Writer w;
+        this.stream = new BufferedOutputStream( output );
+        // TODO: encoding should be consistent with codePage
+        if ( encoding != null )
+        {
+            w = new OutputStreamWriter( stream, encoding );
+        }
+        else
+        {
+            w = new OutputStreamWriter( stream );
+        }
+        this.writer = new PrintWriter( new BufferedWriter( w ) );
+
+        init();
+    }
+
+    /**
+     * setPaperSize.
+     *
+     * @param width in cm.
+     * @param height in cm.
+     */
+    public void setPaperSize( double width /*cm*/, double height /*cm*/ )
+    {
+        paperWidth = width;
+        paperHeight = height;
+    }
+
+    /**
+     * <p>Setter for the field <code>topMargin</code>.</p>
+     *
+     * @param margin margin.
+     */
+    public void setTopMargin( double margin )
+    {
+        topMargin = margin;
+    }
+
+    /**
+     * <p>Setter for the field <code>bottomMargin</code>.</p>
+     *
+     * @param margin margin.
+     */
+    public void setBottomMargin( double margin )
+    {
+        bottomMargin = margin;
+    }
+
+    /**
+     * <p>Setter for the field <code>leftMargin</code>.</p>
+     *
+     * @param margin margin
+     */
+    public void setLeftMargin( double margin )
+    {
+        leftMargin = margin;
+    }
+
+    /**
+     * <p>Setter for the field <code>rightMargin</code>.</p>
+     *
+     * @param margin margin
+     */
+    public void setRightMargin( double margin )
+    {
+        rightMargin = margin;
+    }
+
+    /**
+     * <p>Setter for the field <code>fontSize</code>.</p>
+     *
+     * @param size in pts
+     */
+    public void setFontSize( int size /*pts*/ )
+    {
+        fontSize = size;
+    }
+
+    /**
+     * <p>setSpacing.</p>
+     *
+     * @param spacing in pts.
+     */
+    public void setSpacing( int spacing /*pts*/ )
+    {
+        space.set( 20 * spacing );
+    }
+
+    /**
+     * <p>Setter for the field <code>resolution</code>.</p>
+     *
+     * @param resolution in dpi
+     */
+    public void setResolution( int resolution /*dpi*/ )
+    {
+        this.resolution = resolution;
+    }
+
+    /**
+     * <p>Setter for the field <code>imageFormat</code>.</p>
+     *
+     * @param format
+     */
+    public void setImageFormat( String format )
+    {
+        imageFormat = format;
+    }
+
+    /**
+     * <p>Setter for the field <code>imageType</code>.</p>
+     *
+     * @param type
+     */
+    public void setImageType( String type )
+    {
+        imageType = type;
+    }
+
+    /**
+     * <p>Setter for the field <code>imageDataFormat</code>.</p>
+     *
+     * @param format
+     */
+    public void setImageDataFormat( String format )
+    {
+        imageDataFormat = format;
+    }
+
+    /**
+     * <p>Setter for the field <code>imageCompression</code>.</p>
+     *
+     * @param compression
+     */
+    public void setImageCompression( boolean compression )
+    {
+        imageCompression = compression;
+    }
+
+    /**
+     * <p>Setter for the field <code>codePage</code>.</p>
+     *
+     * @param cp
+     */
+    public void setCodePage( int cp )
+    {
+        codePage = cp;
+    }
+
+    /**
+     * <p>Setter for the field <code>charSet</code>.</p>
+     *
+     * @param cs
+     */
+    public void setCharSet( int cs )
+    {
+        charSet = cs;
+    }
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        init();
+
+        writer.println( "{\\rtf1\\ansi\\ansicpg" + codePage + "\\deff0" );
+
+        writer.println( "{\\fonttbl" );
+        writer.println( "{\\f0\\froman\\fcharset" + charSet + " Times;}" );
+        writer.println( "{\\f1\\fmodern\\fcharset" + charSet + " Courier;}" );
+        writer.println( "}" );
+
+        writer.println( "{\\stylesheet" );
+        for ( int level = 1; level <= 5; ++level )
+        {
+            writer.print( "{\\s" + styleNumber( level ) );
+            writer.print( "\\outlinelevel" + level );
+            writer.print( " Section Title " + level );
+            writer.println( ";}" );
+        }
+        writer.println( "}" );
+
+        writer.println( "\\paperw" + toTwips( paperWidth, UNIT_CENTIMETER ) );
+        writer.println( "\\paperh" + toTwips( paperHeight, UNIT_CENTIMETER ) );
+        writer.println( "\\margl" + toTwips( leftMargin, UNIT_CENTIMETER ) );
+        writer.println( "\\margr" + toTwips( rightMargin, UNIT_CENTIMETER ) );
+        writer.println( "\\margt" + toTwips( topMargin, UNIT_CENTIMETER ) );
+        writer.println( "\\margb" + toTwips( bottomMargin, UNIT_CENTIMETER ) );
+
+        space.set( space.get() / 2 );
+        space.setNext( 0 );
+
+        emptyHeader = true;
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        space.restore();
+        if ( emptyHeader )
+        {
+            space.setNext( 0 );
+        }
+        else
+        {
+            space.setNext( 2 * space.get() );
+        }
+    }
+
+    /**
+     * <p>toTwips.</p>
+     *
+     * @param length a double.
+     * @param unit a int.
+     * @return a int.
+     */
+    protected int toTwips( double length, int unit )
+    {
+        double points;
+
+        switch ( unit )
+        {
+            case UNIT_MILLIMETER:
+                points = ( length / 25.4 ) * 72.;
+                break;
+            case UNIT_CENTIMETER:
+                points = ( length / 2.54 ) * 72.;
+                break;
+            case UNIT_INCH:
+                points = length * 72.;
+                break;
+            case UNIT_PIXEL:
+            default:
+                points = ( length / resolution ) * 72.;
+                break;
+        }
+
+        return (int) Math.rint( points * 20. );
+    }
+
+    /** {@inheritDoc} */
+    public void title()
+    {
+        Paragraph p = new Paragraph( STYLE_BOLD, fontSize + 6 );
+        p.justification = Sink.JUSTIFY_CENTER;
+        beginParagraph( p );
+        emptyHeader = false;
+    }
+
+    /** {@inheritDoc} */
+    public void title_()
+    {
+        endParagraph();
+    }
+
+    /** {@inheritDoc} */
+    public void author()
+    {
+        Paragraph p = new Paragraph( STYLE_ROMAN, fontSize + 2 );
+        p.justification = Sink.JUSTIFY_CENTER;
+        beginParagraph( p );
+        emptyHeader = false;
+    }
+
+    /** {@inheritDoc} */
+    public void author_()
+    {
+        endParagraph();
+    }
+
+    /** {@inheritDoc} */
+    public void date()
+    {
+        Paragraph p = new Paragraph( STYLE_ROMAN, fontSize );
+        p.justification = Sink.JUSTIFY_CENTER;
+        beginParagraph( p );
+        emptyHeader = false;
+    }
+
+    /** {@inheritDoc} */
+    public void date_()
+    {
+        endParagraph();
+    }
+
+    /** {@inheritDoc} */
+    public void body()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void body_()
+    {
+        writer.println( "}" );
+        writer.flush();
+    }
+
+    /** {@inheritDoc} */
+    public void section1()
+    {
+        sectionLevel = 1;
+    }
+
+    /** {@inheritDoc} */
+    public void section1_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section2()
+    {
+        sectionLevel = 2;
+    }
+
+    /** {@inheritDoc} */
+    public void section2_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section3()
+    {
+        sectionLevel = 3;
+    }
+
+    /** {@inheritDoc} */
+    public void section3_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section4()
+    {
+        sectionLevel = 4;
+    }
+
+    /** {@inheritDoc} */
+    public void section4_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void section5()
+    {
+        sectionLevel = 5;
+    }
+
+    /** {@inheritDoc} */
+    public void section5_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle()
+    {
+        int stl = STYLE_BOLD;
+        int size = fontSize;
+
+        switch ( sectionLevel )
+        {
+            case 1:
+                size = fontSize + 6;
+                break;
+            case 2:
+                size = fontSize + 4;
+                break;
+            case 3:
+                size = fontSize + 2;
+                break;
+            case 4:
+                break;
+            case 5:
+                stl = STYLE_ROMAN;
+                break;
+        }
+
+        Paragraph p = new Paragraph( stl, size );
+        p.style = styleNumber( sectionLevel );
+
+        beginParagraph( p );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_()
+    {
+        endParagraph();
+    }
+
+    private int styleNumber( int level )
+    {
+        return level;
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        indentation.add( LIST_INDENT );
+        space.set( space.get() / 2 );
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        indentation.restore();
+        space.restore();
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        Paragraph p = new Paragraph();
+        p.leftIndent = indentation.get() + listItemIndent;
+        p.firstLineIndent = ( -listItemIndent );
+        beginParagraph( p );
+
+        beginStyle( STYLE_BOLD );
+        writer.println( LIST_ITEM_HEADER );
+        endStyle();
+
+        indentation.add( listItemIndent );
+        space.set( space.get() / 2 );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        endParagraph();
+
+        indentation.restore();
+        space.restore();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        this.numbering.addElement( new Integer( numbering ) );
+        itemNumber.addElement( new Counter( 0 ) );
+
+        indentation.add( LIST_INDENT );
+        space.set( space.get() / 2 );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        numbering.removeElementAt( numbering.size() - 1 );
+        itemNumber.removeElementAt( itemNumber.size() - 1 );
+
+        indentation.restore();
+        space.restore();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        ( (Counter) itemNumber.lastElement() ).increment();
+
+        int indent = 0;
+        String header = getItemHeader();
+        Font font = getFont( STYLE_TYPEWRITER, fontSize );
+        if ( font != null )
+        {
+            indent = textWidth( header, font );
+        }
+
+        Paragraph p = new Paragraph();
+        p.leftIndent = indentation.get() + indent;
+        p.firstLineIndent = ( -indent );
+        beginParagraph( p );
+
+        beginStyle( STYLE_TYPEWRITER );
+        writer.println( header );
+        endStyle();
+
+        indentation.add( indent );
+        space.set( space.get() / 2 );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        endParagraph();
+
+        indentation.restore();
+        space.restore();
+    }
+
+    private String getItemHeader()
+    {
+        int nmb = ( (Integer) this.numbering.lastElement() ).intValue();
+        int iNmb = ( (Counter) this.itemNumber.lastElement() ).get();
+        StringBuffer buf = new StringBuffer();
+
+        switch ( nmb )
+        {
+            case Sink.NUMBERING_DECIMAL:
+            default:
+                buf.append( iNmb );
+                buf.append( ". " );
+                while ( buf.length() < 4 )
+                {
+                    buf.append( ' ' );
+                }
+                break;
+
+            case Sink.NUMBERING_LOWER_ALPHA:
+                buf.append( AlphaNumerals.toString( iNmb, true ) );
+                buf.append( ") " );
+                break;
+
+            case Sink.NUMBERING_UPPER_ALPHA:
+                buf.append( AlphaNumerals.toString( iNmb, false ) );
+                buf.append( ". " );
+                break;
+
+            case Sink.NUMBERING_LOWER_ROMAN:
+                buf.append( RomanNumerals.toString( iNmb, true ) );
+                buf.append( ") " );
+                while ( buf.length() < 6 )
+                {
+                    buf.append( ' ' );
+                }
+                break;
+
+            case Sink.NUMBERING_UPPER_ROMAN:
+                buf.append( RomanNumerals.toString( iNmb, false ) );
+                buf.append( ". " );
+                while ( buf.length() < 6 )
+                {
+                    buf.append( ' ' );
+                }
+                break;
+        }
+
+        return buf.toString();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList()
+    {
+        int next = space.getNext();
+
+        indentation.add( LIST_INDENT );
+        space.set( space.get() / 2 );
+        space.setNext( next );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList_()
+    {
+        indentation.restore();
+        space.restore();
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        int next = space.getNext();
+        space.set( space.get() / 2 );
+        space.setNext( next );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem_()
+    {
+        space.restore();
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm_()
+    {
+        endParagraph();
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        int next = space.getNext();
+
+        indentation.add( DEFINITION_INDENT );
+        space.set( space.get() / 2 );
+        space.setNext( next );
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        endParagraph();
+
+        indentation.restore();
+        space.restore();
+    }
+
+    /** {@inheritDoc} */
+    public void table()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void table_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows( int[] justification, boolean grid )
+
+    {
+        table = new Table( justification, grid );
+        context.set( CONTEXT_TABLE );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRows_()
+    {
+        boolean bb = false;
+        boolean br = false;
+
+        int offset = ( pageWidth() - ( table.width() + indentation.get() ) ) / 2;
+        int x0 = indentation.get() + offset;
+
+        space.skip();
+
+        for ( int i = 0; i < table.rows.size(); ++i )
+        {
+            Row r = (Row) table.rows.elementAt( i );
+
+            writer.print( "\\trowd" );
+            writer.print( "\\trleft" + x0 );
+            writer.print( "\\trgaph" + CELL_HORIZONTAL_PAD );
+            writer.println( "\\trrh" + r.height() );
+
+            if ( table.grid )
+            {
+                if ( i == ( table.rows.size() - 1 ) )
+                {
+                    bb = true;
+                }
+                br = false;
+            }
+
+            for ( int j = 0, x = x0; j < table.numColumns; ++j )
+            {
+                if ( table.grid )
+                {
+                    if ( j == ( table.numColumns - 1 ) )
+                    {
+                        br = true;
+                    }
+                    setBorder( true, bb, true, br );
+                    x += BORDER_WIDTH;
+                }
+                x += table.columnWidths[j];
+                writer.println( "\\clvertalc\\cellx" + x );
+            }
+
+            for ( int j = 0; j < table.numColumns; ++j )
+            {
+                if ( j >= r.cells.size() )
+                {
+                    break;
+                }
+                Cell c = (Cell) r.cells.elementAt( j );
+
+                writer.print( "\\pard\\intbl" );
+                setJustification( table.justification[j] );
+                writer.println( "\\plain\\f0\\fs" + ( 2 * fontSize ) );
+
+                for ( int k = 0; k < c.lines.size(); ++k )
+                {
+                    if ( k > 0 )
+                    {
+                        writer.println( "\\line" );
+                    }
+                    Line l = (Line) c.lines.elementAt( k );
+
+                    for ( int n = 0; n < l.items.size(); ++n )
+                    {
+                        Item item = (Item) l.items.elementAt( n );
+                        writer.print( "{" );
+                        setStyle( item.style );
+                        writer.println( escape( item.text ) );
+                        writer.println( "}" );
+                    }
+                }
+
+                writer.println( "\\cell" );
+            }
+
+            writer.println( "\\row" );
+        }
+
+        context.restore();
+    }
+
+    private int pageWidth()
+    {
+        double width = paperWidth - ( leftMargin + rightMargin );
+        return toTwips( width, UNIT_CENTIMETER );
+    }
+
+    private void setBorder( boolean bt, boolean bb, boolean bl, boolean br )
+    {
+        if ( bt )
+        {
+            writer.println( "\\clbrdrt\\brdrs\\brdrw" + BORDER_WIDTH );
+        }
+        if ( bb )
+        {
+            writer.println( "\\clbrdrb\\brdrs\\brdrw" + BORDER_WIDTH );
+        }
+        if ( bl )
+        {
+            writer.println( "\\clbrdrl\\brdrs\\brdrw" + BORDER_WIDTH );
+        }
+        if ( br )
+        {
+            writer.println( "\\clbrdrr\\brdrs\\brdrw" + BORDER_WIDTH );
+        }
+    }
+
+    private void setJustification( int justification )
+    {
+        switch ( justification )
+        {
+            case Sink.JUSTIFY_LEFT:
+            default:
+                writer.println( "\\ql" );
+                break;
+            case Sink.JUSTIFY_CENTER:
+                writer.println( "\\qc" );
+                break;
+            case Sink.JUSTIFY_RIGHT:
+                writer.println( "\\qr" );
+                break;
+        }
+    }
+
+    private void setStyle( int style )
+    {
+        switch ( style )
+        {
+            case STYLE_ITALIC:
+                writer.println( "\\i" );
+                break;
+            case STYLE_BOLD:
+                writer.println( "\\b" );
+                break;
+            case STYLE_TYPEWRITER:
+                writer.println( "\\f1" );
+                break;
+            default:
+                break;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        row = new Row();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        table.add( row );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        tableCell_();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        cell = new Cell();
+        line = new Line();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        cell.add( line );
+        row.add( cell );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption()
+    {
+        Paragraph p = new Paragraph();
+        p.justification = Sink.JUSTIFY_CENTER;
+        p.spaceBefore /= 2;
+        beginParagraph( p );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption_()
+    {
+        endParagraph();
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph()
+    {
+        if ( paragraph == null )
+        {
+            beginParagraph( new Paragraph() );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        endParagraph();
+    }
+
+    private void beginParagraph( Paragraph p )
+    {
+        p.begin();
+        this.paragraph = p;
+        if ( style != STYLE_ROMAN )
+        {
+            beginStyle( style );
+        }
+    }
+
+    private void endParagraph()
+    {
+        if ( paragraph != null )
+        {
+            if ( style != STYLE_ROMAN )
+            {
+                endStyle();
+            }
+            paragraph.end();
+            paragraph = null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        verbatim = new StringBuffer();
+        frame = boxed;
+
+        context.set( CONTEXT_VERBATIM );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        String text = verbatim.toString();
+
+        Paragraph p = new Paragraph();
+        p.fontStyle = STYLE_TYPEWRITER;
+        p.frame = frame;
+
+        beginParagraph( p );
+
+        StringTokenizer t = new StringTokenizer( text, EOL, true );
+        while ( t.hasMoreTokens() )
+        {
+            String s = t.nextToken();
+            if ( s.equals( EOL ) && t.hasMoreTokens() )
+            {
+                writer.println( "\\line" );
+            }
+            else
+            {
+                writer.println( escape( s ) );
+            }
+        }
+
+        endParagraph();
+
+        context.restore();
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figure_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        Paragraph p = new Paragraph();
+        p.justification = Sink.JUSTIFY_CENTER;
+        beginParagraph( p );
+
+        try
+        {
+            writeImage( name );
+        }
+        catch ( Exception e )
+        {
+            getLog().error( e.getMessage(), e );
+        }
+
+        endParagraph();
+    }
+
+    private void writeImage( String source )
+        throws Exception
+    {
+        if ( !source.toLowerCase().endsWith( ".ppm" ) )
+        {
+            // TODO support more image types!
+            String msg =
+                "Unsupported image type for image file: '" + source + "'. Only PPM image type is "
+                    + "currently supported.";
+            logMessage( "unsupportedImage", msg );
+
+            return;
+        }
+
+        int bytesPerLine;
+        PBMReader ppm = new PBMReader( source );
+        WMFWriter.Dib dib = new WMFWriter.Dib();
+        WMFWriter wmf = new WMFWriter();
+
+        int srcWidth = ppm.width();
+        int srcHeight = ppm.height();
+
+        dib.biWidth = srcWidth;
+        dib.biHeight = srcHeight;
+        dib.biXPelsPerMeter = (int) ( resolution * 100. / 2.54 );
+        dib.biYPelsPerMeter = dib.biXPelsPerMeter;
+
+        if ( imageType.equals( IMG_TYPE_RGB ) )
+        {
+            dib.biBitCount = 24;
+            dib.biCompression = WMFWriter.Dib.BI_RGB; // no compression
+
+            bytesPerLine = 4 * ( ( 3 * srcWidth + 3 ) / 4 );
+            dib.bitmap = new byte[srcHeight * bytesPerLine];
+
+            byte[] l = new byte[3 * srcWidth];
+            for ( int i = ( srcHeight - 1 ); i >= 0; --i )
+            {
+                ppm.read( l, 0, l.length );
+                for ( int j = 0, k = ( i * bytesPerLine ); j < l.length; j += 3 )
+                {
+                    // component order = BGR
+                    dib.bitmap[k++] = l[j + 2];
+                    dib.bitmap[k++] = l[j + 1];
+                    dib.bitmap[k++] = l[j];
+                }
+            }
+        }
+        else
+        {
+            dib.biBitCount = 8;
+
+            bytesPerLine = 4 * ( ( srcWidth + 3 ) / 4 );
+            byte[] bitmap = new byte[srcHeight * bytesPerLine];
+
+            Vector colors = new Vector( 256 );
+            colors.addElement( Color.white );
+            colors.addElement( Color.black );
+
+            byte[] l = new byte[3 * srcWidth];
+            for ( int i = ( srcHeight - 1 ); i >= 0; --i )
+            {
+                ppm.read( l, 0, l.length );
+                for ( int j = 0, k = ( i * bytesPerLine ); j < l.length; )
+                {
+                    int r = (int) l[j++] & 0xff;
+                    int g = (int) l[j++] & 0xff;
+                    int b = (int) l[j++] & 0xff;
+                    Color color = new Color( r, g, b );
+                    int index = colors.indexOf( color );
+                    if ( index < 0 )
+                    {
+                        if ( colors.size() < colors.capacity() )
+                        {
+                            colors.addElement( color );
+                            index = colors.size() - 1;
+                        }
+                        else
+                        {
+                            index = 1;
+                        }
+                    }
+                    bitmap[k++] = (byte) index;
+                }
+            }
+
+            dib.biClrUsed = colors.size();
+            dib.biClrImportant = dib.biClrUsed;
+            dib.palette = new byte[4 * dib.biClrUsed];
+            for ( int i = 0, j = 0; i < dib.biClrUsed; ++i, ++j )
+            {
+                Color color = (Color) colors.elementAt( i );
+                dib.palette[j++] = (byte) color.getBlue();
+                dib.palette[j++] = (byte) color.getGreen();
+                dib.palette[j++] = (byte) color.getRed();
+            }
+
+            if ( imageCompression )
+            {
+                dib.biCompression = WMFWriter.Dib.BI_RLE8;
+                dib.bitmap = new byte[bitmap.length + ( 2 * ( bitmap.length / 255 + 1 ) )];
+                dib.biSizeImage = WMFWriter.Dib.rlEncode8( bitmap, 0, bitmap.length, dib.bitmap, 0 );
+            }
+            else
+            {
+                dib.biCompression = WMFWriter.Dib.BI_RGB;
+                dib.bitmap = bitmap;
+            }
+        }
+
+        if ( imageFormat.equals( IMG_FORMAT_WMF ) )
+        {
+            int[] parameters;
+            WMFWriter.Record record;
+
+            /*
+             * See the libwmf library documentation
+             * (http://www.wvware.com/wmf_doc_index.html)
+             * for a description of WMF records.
+             */
+
+            // set mapping mode to MM_TEXT (logical unit = pixel)
+            parameters = new int[1];
+            parameters[0] = 1;
+            record = new WMFWriter.Record( 0x0103, parameters );
+            wmf.add( record );
+
+            // set window origin and dimensions
+            parameters = new int[2];
+            record = new WMFWriter.Record( 0x020b, parameters );
+            wmf.add( record );
+            parameters = new int[2];
+            parameters[0] = srcHeight;
+            parameters[1] = srcWidth;
+            record = new WMFWriter.Record( 0x020c, parameters );
+            wmf.add( record );
+
+            parameters = new int[WMFWriter.DibBitBltRecord.P_COUNT];
+            // raster operation = SRCCOPY (0x00cc0020)
+            parameters[WMFWriter.DibBitBltRecord.P_ROP_H] = 0x00cc;
+            parameters[WMFWriter.DibBitBltRecord.P_ROP_L] = 0x0020;
+            parameters[WMFWriter.DibBitBltRecord.P_WIDTH] = srcWidth;
+            parameters[WMFWriter.DibBitBltRecord.P_HEIGHT] = srcHeight;
+            record = new WMFWriter.DibBitBltRecord( parameters, dib );
+            wmf.add( record );
+        }
+
+        if ( imageFormat.equals( IMG_FORMAT_WMF ) )
+        {
+            writer.print( "{\\pict\\wmetafile1" );
+            writer.println( "\\picbmp\\picbpp" + dib.biBitCount );
+        }
+        else
+        {
+            writer.print( "{\\pict\\dibitmap0\\wbmplanes1" );
+            writer.print( "\\wbmbitspixel" + dib.biBitCount );
+            writer.println( "\\wbmwidthbytes" + bytesPerLine );
+        }
+
+        writer.print( "\\picw" + srcWidth );
+        writer.print( "\\pich" + srcHeight );
+        writer.print( "\\picwgoal" + toTwips( srcWidth, UNIT_PIXEL ) );
+        writer.println( "\\pichgoal" + toTwips( srcHeight, UNIT_PIXEL ) );
+
+        if ( imageFormat.equals( IMG_FORMAT_WMF ) )
+        {
+            if ( imageDataFormat.equals( IMG_DATA_RAW ) )
+            {
+                writer.print( "\\bin" + ( 2 * wmf.size() ) + " " );
+                writer.flush();
+                wmf.write( stream );
+                stream.flush();
+            }
+            else
+            {
+                wmf.print( writer );
+            }
+        }
+        else
+        {
+            if ( imageDataFormat.equals( IMG_DATA_RAW ) )
+            {
+                writer.print( "\\bin" + ( 2 * dib.size() ) + " " );
+                writer.flush();
+                dib.write( stream );
+                stream.flush();
+            }
+            else
+            {
+                dib.print( writer );
+            }
+        }
+
+        writer.println( "}" );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption()
+    {
+        Paragraph p = new Paragraph();
+        p.justification = Sink.JUSTIFY_CENTER;
+        p.spaceBefore /= 2;
+        beginParagraph( p );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption_()
+    {
+        endParagraph();
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        writer.print( "\\pard\\li" + indentation.get() );
+
+        int skip = space.getNext();
+        if ( skip > 0 )
+        {
+            writer.print( "\\sb" + skip );
+        }
+        space.setNext( skip );
+
+        writer.print( "\\brdrb\\brdrs\\brdrw" + BORDER_WIDTH );
+        writer.println( "\\plain\\fs1\\par" );
+    }
+
+    /** {@inheritDoc} */
+    public void pageBreak()
+    {
+        writer.println( "\\page" );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void anchor_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        beginStyle( STYLE_ITALIC );
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        endStyle();
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        beginStyle( STYLE_BOLD );
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        endStyle();
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        beginStyle( STYLE_TYPEWRITER );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        endStyle();
+    }
+
+    private void beginStyle( int style )
+    {
+        this.style = style;
+
+        switch ( context.get() )
+        {
+            case CONTEXT_TABLE:
+                break;
+            default:
+                if ( paragraph != null )
+                {
+                    switch ( style )
+                    {
+                        case STYLE_ITALIC:
+                            writer.println( "{\\i" );
+                            break;
+                        case STYLE_BOLD:
+                            writer.println( "{\\b" );
+                            break;
+                        case STYLE_TYPEWRITER:
+                            writer.println( "{\\f1" );
+                            break;
+                        default:
+                            writer.println( "{" );
+                            break;
+                    }
+                }
+                break;
+        }
+    }
+
+    private void endStyle()
+    {
+        style = STYLE_ROMAN;
+
+        switch ( context.get() )
+        {
+            case CONTEXT_TABLE:
+                break;
+            default:
+                if ( paragraph != null )
+                {
+                    writer.println( "}" );
+                }
+                break;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak()
+    {
+        switch ( context.get() )
+        {
+            case CONTEXT_TABLE:
+                cell.add( line );
+                line = new Line();
+                break;
+            default:
+                writer.println( "\\line" );
+                break;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void nonBreakingSpace()
+    {
+        switch ( context.get() )
+        {
+            case CONTEXT_TABLE:
+                line.add( new Item( style, " " ) );
+                break;
+            default:
+                writer.println( "\\~" );
+                break;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        switch ( context.get() )
+        {
+            case CONTEXT_VERBATIM:
+                verbatim.append( text );
+                break;
+
+            case CONTEXT_TABLE:
+                StringTokenizer t = new StringTokenizer( text, EOL, true );
+                while ( t.hasMoreTokens() )
+                {
+                    String token = t.nextToken();
+                    if ( token.equals( EOL ) )
+                    {
+                        cell.add( line );
+                        line = new Line();
+                    }
+                    else
+                    {
+                        line.add( new Item( style, normalize( token ) ) );
+                    }
+                }
+                break;
+
+            default:
+                if ( paragraph == null )
+                {
+                    beginParagraph( new Paragraph() );
+                }
+                writer.println( escape( normalize( text ) ) );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Unkown events just log a warning message but are ignored otherwise.
+     * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        String msg = "Unknown Sink event: '" + name + "', ignoring!";
+        logMessage( "unknownEvent", msg );
+    }
+
+    private static String normalize( String s )
+    {
+        int length = s.length();
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = s.charAt( i );
+
+            if ( Character.isWhitespace( c ) )
+            {
+                if ( buffer.length() == 0 || buffer.charAt( buffer.length() - 1 ) != ' ' )
+                {
+                    buffer.append( ' ' );
+                }
+            }
+
+            else
+            {
+                buffer.append( c );
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    private static String escape( String s )
+    {
+        int length = s.length();
+        StringBuffer buffer = new StringBuffer( length );
+
+        for ( int i = 0; i < length; ++i )
+        {
+            char c = s.charAt( i );
+            switch ( c )
+            {
+                case '\\':
+                    buffer.append( "\\\\" );
+                    break;
+                case '{':
+                    buffer.append( "\\{" );
+                    break;
+                case '}':
+                    buffer.append( "\\}" );
+                    break;
+                default:
+                    buffer.append( c );
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * <p>getFont.</p>
+     *
+     * @param style a int.
+     * @param size a int.
+     * @return a {@link org.apache.maven.doxia.module.rtf.Font} object.
+     */
+    protected Font getFont( int style, int size )
+    {
+        Font font = null;
+
+        StringBuffer buf = new StringBuffer();
+        buf.append( style );
+        buf.append( size );
+        String key = buf.toString();
+
+        Object object = fontTable.get( key );
+        if ( object == null )
+        {
+            try
+            {
+                font = new Font( style, size );
+                fontTable.put( key, font );
+            }
+            catch ( Exception ignored )
+            {
+                if ( getLog().isDebugEnabled() )
+                {
+                    getLog().debug( ignored.getMessage(), ignored );
+                }
+            }
+        }
+        else
+        {
+            font = (Font) object;
+        }
+
+        return font;
+    }
+
+    private static int textWidth( String text, Font font )
+    {
+        int width = 0;
+        StringTokenizer t = new StringTokenizer( text, EOL );
+
+        while ( t.hasMoreTokens() )
+        {
+            int w = font.textExtents( t.nextToken() ).width;
+            if ( w > width )
+            {
+                width = w;
+            }
+        }
+
+        return width;
+    }
+
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        writer.flush();
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        writer.close();
+
+        if ( getLog().isWarnEnabled() && this.warnMessages != null )
+        {
+            for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) it.next();
+
+                Set set = (Set) entry.getValue();
+
+                for ( Iterator it2 = set.iterator(); it2.hasNext(); )
+                {
+                    String msg = (String) it2.next();
+
+                    getLog().warn( msg );
+                }
+            }
+
+            this.warnMessages = null;
+        }
+
+        init();
+    }
+
+    /**
+     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
+     *
+     * @param key not null
+     * @param msg not null
+     * @see #close()
+     * @since 1.1.1
+     */
+    private void logMessage( String key, String msg )
+    {
+        msg = "[RTF Sink] " + msg;
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( msg );
+
+            return;
+        }
+
+        if ( warnMessages == null )
+        {
+            warnMessages = new HashMap();
+        }
+
+        Set set = (Set) warnMessages.get( key );
+        if ( set == null )
+        {
+            set = new TreeSet();
+        }
+        set.add( msg );
+        warnMessages.put( key, set );
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.fontTable.clear();
+        this.context = new Context();
+        this.paragraph = null;
+        this.indentation = new Indentation( 0 );
+        this.space = new Space( 20 * DEFAULT_SPACING );
+        Font font = getFont( STYLE_BOLD, fontSize );
+        if ( font != null )
+        {
+            this.listItemIndent = textWidth( LIST_ITEM_HEADER, font );
+        }
+        this.numbering.clear();
+        this.itemNumber.clear();
+        this.style = STYLE_ROMAN;
+        this.sectionLevel = 0;
+        this.emptyHeader = false;
+        this.verbatim = null;
+        this.frame = false;
+        this.table = null;
+        this.row = null;
+        this.cell = null;
+        this.line = null;
+        this.warnMessages = null;
+    }
+
+    // -----------------------------------------------------------------------
+
+    static class Counter
+    {
+        private int value;
+
+        Counter( int value )
+        {
+            set( value );
+        }
+
+        void set( int value )
+        {
+            this.value = value;
+        }
+
+        int get()
+        {
+            return value;
+        }
+
+        void increment()
+        {
+            increment( 1 );
+        }
+
+        void increment( int value )
+        {
+            this.value += value;
+        }
+    }
+
+    static class Context
+    {
+        private int context = CONTEXT_UNDEFINED;
+
+        private Vector stack = new Vector();
+
+        void set( int context )
+        {
+            stack.addElement( new Integer( this.context ) );
+            this.context = context;
+        }
+
+        void restore()
+        {
+            if ( !stack.isEmpty() )
+            {
+                context = ( (Integer) stack.lastElement() ).intValue();
+                stack.removeElementAt( stack.size() - 1 );
+            }
+        }
+
+        int get()
+        {
+            return context;
+        }
+    }
+
+    class Paragraph
+    {
+        int style = 0;
+
+        int justification = Sink.JUSTIFY_LEFT;
+
+        int leftIndent = indentation.get();
+
+        int rightIndent = 0;
+
+        int firstLineIndent = 0;
+
+        int spaceBefore = space.getNext();
+
+        int spaceAfter = 0;
+
+        boolean frame = false;
+
+        int fontStyle = STYLE_ROMAN;
+
+        int fontSize = RtfSink.this.fontSize;
+
+        Paragraph()
+        {
+            // nop
+        }
+
+        Paragraph( int style, int size )
+        {
+            fontStyle = style;
+            fontSize = size;
+        }
+
+        void begin()
+        {
+            writer.print( "\\pard" );
+            if ( style > 0 )
+            {
+                writer.print( "\\s" + style );
+            }
+            switch ( justification )
+            {
+                case Sink.JUSTIFY_LEFT:
+                default:
+                    break;
+                case Sink.JUSTIFY_CENTER:
+                    writer.print( "\\qc" );
+                    break;
+                case Sink.JUSTIFY_RIGHT:
+                    writer.print( "\\qr" );
+                    break;
+            }
+            if ( leftIndent != 0 )
+            {
+                writer.print( "\\li" + leftIndent );
+            }
+            if ( rightIndent != 0 )
+            {
+                writer.print( "\\ri" + rightIndent );
+            }
+            if ( firstLineIndent != 0 )
+            {
+                writer.print( "\\fi" + firstLineIndent );
+            }
+            if ( spaceBefore != 0 )
+            {
+                writer.print( "\\sb" + spaceBefore );
+            }
+            if ( spaceAfter != 0 )
+            {
+                writer.print( "\\sa" + spaceAfter );
+            }
+
+            if ( frame )
+            {
+                writer.print( "\\box\\brdrs\\brdrw" + BORDER_WIDTH );
+            }
+
+            writer.print( "\\plain" );
+            switch ( fontStyle )
+            {
+                case STYLE_ROMAN:
+                default:
+                    writer.print( "\\f0" );
+                    break;
+                case STYLE_ITALIC:
+                    writer.print( "\\f0\\i" );
+                    break;
+                case STYLE_BOLD:
+                    writer.print( "\\f0\\b" );
+                    break;
+                case STYLE_TYPEWRITER:
+                    writer.print( "\\f1" );
+                    break;
+            }
+            writer.println( "\\fs" + ( 2 * fontSize ) );
+        }
+
+        void end()
+        {
+            writer.println( "\\par" );
+        }
+    }
+
+    class Space
+    {
+        private int space;
+
+        private int next;
+
+        private Vector stack = new Vector();
+
+        Space( int space /*twips*/ )
+        {
+            this.space = space;
+            next = space;
+        }
+
+        void set( int space /*twips*/ )
+        {
+            stack.addElement( new Integer( this.space ) );
+            this.space = space;
+            next = space;
+        }
+
+        int get()
+        {
+            return space;
+        }
+
+        void restore()
+        {
+            if ( !stack.isEmpty() )
+            {
+                space = ( (Integer) stack.lastElement() ).intValue();
+                stack.removeElementAt( stack.size() - 1 );
+                next = space;
+            }
+        }
+
+        void setNext( int space /*twips*/ )
+        {
+            next = space;
+        }
+
+        int getNext()
+        {
+            int nxt = this.next;
+            this.next = space;
+            return nxt;
+        }
+
+        void skip()
+        {
+            skip( getNext() );
+        }
+
+        void skip( int space /*twips*/ )
+        {
+            writer.print( "\\pard" );
+            if ( ( space -= 10 ) > 0 )
+            {
+                writer.print( "\\sb" + space );
+            }
+            writer.println( "\\plain\\fs1\\par" );
+        }
+    }
+
+    static class Indentation
+    {
+        private int indent;
+
+        private Vector stack = new Vector();
+
+        Indentation( int indent /*twips*/ )
+        {
+            this.indent = indent;
+        }
+
+        void set( int indent /*twips*/ )
+        {
+            stack.addElement( new Integer( this.indent ) );
+            this.indent = indent;
+        }
+
+        int get()
+        {
+            return indent;
+        }
+
+        void restore()
+        {
+            if ( !stack.isEmpty() )
+            {
+                indent = ( (Integer) stack.lastElement() ).intValue();
+                stack.removeElementAt( stack.size() - 1 );
+            }
+        }
+
+        void add( int indent /*twips*/ )
+        {
+            set( this.indent + indent );
+        }
+    }
+
+    static class Table
+    {
+        int numColumns;
+
+        int[] columnWidths;
+
+        int[] justification;
+
+        boolean grid;
+
+        Vector rows;
+
+        Table( int[] justification, boolean grid )
+        {
+            numColumns = justification.length;
+            columnWidths = new int[numColumns];
+            this.justification = justification;
+            this.grid = grid;
+            rows = new Vector();
+        }
+
+        void add( Row row )
+        {
+            rows.addElement( row );
+
+            for ( int i = 0; i < numColumns; ++i )
+            {
+                if ( i >= row.cells.size() )
+                {
+                    break;
+                }
+                Cell cell = (Cell) row.cells.elementAt( i );
+                int width = cell.boundingBox().width;
+                if ( width > columnWidths[i] )
+                {
+                    columnWidths[i] = width;
+                }
+            }
+        }
+
+        int width()
+        {
+            int width = 0;
+            for ( int i = 0; i < numColumns; ++i )
+            {
+                width += columnWidths[i];
+            }
+            if ( grid )
+            {
+                width += ( numColumns + 1 ) * BORDER_WIDTH;
+            }
+            return width;
+        }
+    }
+
+    static class Row
+    {
+        Vector cells = new Vector();
+
+        void add( Cell cell )
+        {
+            cells.addElement( cell );
+        }
+
+        int height()
+        {
+            int height = 0;
+            int numCells = cells.size();
+            for ( int i = 0; i < numCells; ++i )
+            {
+                Cell cell = (Cell) cells.elementAt( i );
+                Box box = cell.boundingBox();
+                if ( box.height > height )
+                {
+                    height = box.height;
+                }
+            }
+            return height;
+        }
+    }
+
+    class Cell
+    {
+        Vector lines = new Vector();
+
+        void add( Line line )
+        {
+            lines.addElement( line );
+        }
+
+        Box boundingBox()
+        {
+            int width = 0;
+            int height = 0;
+
+            for ( int i = 0; i < lines.size(); ++i )
+            {
+                int w = 0;
+                int h = 0;
+                Line line = (Line) lines.elementAt( i );
+
+                for ( int j = 0; j < line.items.size(); ++j )
+                {
+                    Item item = (Item) line.items.elementAt( j );
+                    Font font = getFont( item.style, fontSize );
+                    if ( font == null )
+                    {
+                        continue;
+                    }
+                    Font.TextExtents x = font.textExtents( item.text );
+                    w += x.width;
+                    if ( x.height > h )
+                    {
+                        h = x.height;
+                    }
+                }
+
+                if ( w > width )
+                {
+                    width = w;
+                }
+                height += h;
+            }
+
+            width += ( 2 * CELL_HORIZONTAL_PAD );
+            height += ( 2 * CELL_VERTICAL_PAD );
+
+            // allow one more pixel for grid outline
+            width += toTwips( 1., UNIT_PIXEL );
+
+            return new Box( width, height );
+        }
+    }
+
+    static class Line
+    {
+        Vector items = new Vector();
+
+        void add( Item item )
+        {
+            items.addElement( item );
+        }
+    }
+
+    static class Item
+    {
+        int style;
+
+        String text;
+
+        Item( int style, String text )
+        {
+            this.style = style;
+            this.text = text;
+        }
+    }
+
+    static class Box
+    {
+        int width;
+
+        int height;
+
+        Box( int width, int height )
+        {
+            this.width = width;
+            this.height = height;
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RtfSinkFactory.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RtfSinkFactory.java
new file mode 100644
index 0000000..4a84613
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/RtfSinkFactory.java
@@ -0,0 +1,45 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.maven.doxia.sink.AbstractBinarySinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Rtf implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: RtfSinkFactory.java 712516 2008-11-09 17:04:22Z hboutemy $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="rtf"
+ */
+public class RtfSinkFactory
+    extends AbstractBinarySinkFactory
+{
+    /** {@inheritDoc} */
+    public Sink createSink( OutputStream out, String encoding )
+        throws IOException
+    {
+        return new RtfSink( out, encoding );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerif.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerif.java
new file mode 100644
index 0000000..13b00a3
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerif.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: SansSerif.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class SansSerif
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 90, 0, 187, 718 ), new CharMetrics( 355, 0, 70, 463, 285, 718 ),
+        new CharMetrics( 556, 0, 28, 0, 529, 688 ), new CharMetrics( 556, 0, 32, -115, 520, 775 ),
+        new CharMetrics( 889, 0, 39, -19, 850, 703 ), new CharMetrics( 667, 0, 44, -15, 645, 718 ),
+        new CharMetrics( 222, 0, 53, 463, 157, 718 ), new CharMetrics( 333, 0, 68, -207, 299, 733 ),
+        new CharMetrics( 333, 0, 34, -207, 265, 733 ), new CharMetrics( 389, 0, 39, 431, 349, 718 ),
+        new CharMetrics( 584, 0, 39, 0, 545, 505 ), new CharMetrics( 278, 0, 87, -147, 191, 106 ),
+        new CharMetrics( 333, 0, 44, 232, 289, 322 ), new CharMetrics( 278, 0, 87, 0, 191, 106 ),
+        new CharMetrics( 278, 0, -17, -19, 295, 737 ), new CharMetrics( 556, 0, 37, -19, 519, 703 ),
+        new CharMetrics( 556, 0, 101, 0, 359, 703 ), new CharMetrics( 556, 0, 26, 0, 507, 703 ),
+        new CharMetrics( 556, 0, 34, -19, 522, 703 ), new CharMetrics( 556, 0, 25, 0, 523, 703 ),
+        new CharMetrics( 556, 0, 32, -19, 514, 688 ), new CharMetrics( 556, 0, 38, -19, 518, 703 ),
+        new CharMetrics( 556, 0, 37, 0, 523, 688 ), new CharMetrics( 556, 0, 38, -19, 517, 703 ),
+        new CharMetrics( 556, 0, 42, -19, 514, 703 ), new CharMetrics( 278, 0, 87, 0, 191, 516 ),
+        new CharMetrics( 278, 0, 87, -147, 191, 516 ), new CharMetrics( 584, 0, 48, 11, 536, 495 ),
+        new CharMetrics( 584, 0, 39, 115, 545, 390 ), new CharMetrics( 584, 0, 48, 11, 536, 495 ),
+        new CharMetrics( 556, 0, 56, 0, 492, 727 ), new CharMetrics( 1015, 0, 147, -19, 868, 737 ),
+        new CharMetrics( 667, 0, 14, 0, 654, 718 ), new CharMetrics( 667, 0, 74, 0, 627, 718 ),
+        new CharMetrics( 722, 0, 44, -19, 681, 737 ), new CharMetrics( 722, 0, 81, 0, 674, 718 ),
+        new CharMetrics( 667, 0, 86, 0, 616, 718 ), new CharMetrics( 611, 0, 86, 0, 583, 718 ),
+        new CharMetrics( 778, 0, 48, -19, 704, 737 ), new CharMetrics( 722, 0, 77, 0, 646, 718 ),
+        new CharMetrics( 278, 0, 91, 0, 188, 718 ), new CharMetrics( 500, 0, 17, -19, 428, 718 ),
+        new CharMetrics( 667, 0, 76, 0, 663, 718 ), new CharMetrics( 556, 0, 76, 0, 537, 718 ),
+        new CharMetrics( 833, 0, 73, 0, 761, 718 ), new CharMetrics( 722, 0, 76, 0, 646, 718 ),
+        new CharMetrics( 778, 0, 39, -19, 739, 737 ), new CharMetrics( 667, 0, 86, 0, 622, 718 ),
+        new CharMetrics( 778, 0, 39, -56, 739, 737 ), new CharMetrics( 722, 0, 88, 0, 684, 718 ),
+        new CharMetrics( 667, 0, 49, -19, 620, 737 ), new CharMetrics( 611, 0, 14, 0, 597, 718 ),
+        new CharMetrics( 722, 0, 79, -19, 644, 718 ), new CharMetrics( 667, 0, 20, 0, 647, 718 ),
+        new CharMetrics( 944, 0, 16, 0, 928, 718 ), new CharMetrics( 667, 0, 19, 0, 648, 718 ),
+        new CharMetrics( 667, 0, 14, 0, 653, 718 ), new CharMetrics( 611, 0, 23, 0, 588, 718 ),
+        new CharMetrics( 278, 0, 63, -196, 250, 722 ), new CharMetrics( 278, 0, -17, -19, 295, 737 ),
+        new CharMetrics( 278, 0, 28, -196, 215, 722 ), new CharMetrics( 469, 0, -14, 264, 483, 688 ),
+        new CharMetrics( 556, 0, 0, -125, 556, -75 ), new CharMetrics( 222, 0, 65, 470, 169, 725 ),
+        new CharMetrics( 556, 0, 36, -15, 530, 538 ), new CharMetrics( 556, 0, 58, -15, 517, 718 ),
+        new CharMetrics( 500, 0, 30, -15, 477, 538 ), new CharMetrics( 556, 0, 35, -15, 499, 718 ),
+        new CharMetrics( 556, 0, 40, -15, 516, 538 ), new CharMetrics( 278, 0, 14, 0, 262, 728 ),
+        new CharMetrics( 556, 0, 40, -220, 499, 538 ), new CharMetrics( 556, 0, 65, 0, 491, 718 ),
+        new CharMetrics( 222, 0, 67, 0, 155, 718 ), new CharMetrics( 222, 0, -16, -210, 155, 718 ),
+        new CharMetrics( 500, 0, 67, 0, 501, 718 ), new CharMetrics( 222, 0, 67, 0, 155, 718 ),
+        new CharMetrics( 833, 0, 65, 0, 769, 538 ), new CharMetrics( 556, 0, 65, 0, 491, 538 ),
+        new CharMetrics( 556, 0, 35, -14, 521, 538 ), new CharMetrics( 556, 0, 58, -207, 517, 538 ),
+        new CharMetrics( 556, 0, 35, -207, 494, 538 ), new CharMetrics( 333, 0, 77, 0, 332, 538 ),
+        new CharMetrics( 500, 0, 32, -15, 464, 538 ), new CharMetrics( 278, 0, 14, -7, 257, 669 ),
+        new CharMetrics( 556, 0, 68, -15, 489, 523 ), new CharMetrics( 500, 0, 8, 0, 492, 523 ),
+        new CharMetrics( 722, 0, 14, 0, 709, 523 ), new CharMetrics( 500, 0, 11, 0, 490, 523 ),
+        new CharMetrics( 500, 0, 11, -214, 489, 523 ), new CharMetrics( 500, 0, 31, 0, 469, 523 ),
+        new CharMetrics( 334, 0, 42, -196, 292, 722 ), new CharMetrics( 260, 0, 94, -19, 167, 737 ),
+        new CharMetrics( 334, 0, 42, -196, 292, 722 ), new CharMetrics( 584, 0, 61, 180, 523, 326 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 95, 0, 183, 523 ),
+        new CharMetrics( 333, 0, 14, 593, 211, 734 ), new CharMetrics( 333, 0, 122, 593, 319, 734 ),
+        new CharMetrics( 333, 0, 21, 593, 312, 734 ), new CharMetrics( 333, 0, -4, 606, 337, 722 ),
+        new CharMetrics( 333, 0, 10, 627, 323, 684 ), new CharMetrics( 333, 0, 13, 595, 321, 731 ),
+        new CharMetrics( 333, 0, 121, 604, 212, 706 ), new CharMetrics( 333, 0, 40, 604, 293, 706 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 333, 0, 75, 572, 259, 756 ),
+        new CharMetrics( 333, 0, 45, -225, 259, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 31, 593, 409, 734 ), new CharMetrics( 333, 0, 73, -225, 287, 0 ),
+        new CharMetrics( 333, 0, 21, 593, 312, 734 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 118, -195, 215, 523 ), new CharMetrics( 556, 0, 51, -115, 513, 623 ),
+        new CharMetrics( 556, 0, 33, -16, 539, 718 ), new CharMetrics( 556, 0, 28, 99, 528, 603 ),
+        new CharMetrics( 556, 0, 3, 0, 553, 688 ), new CharMetrics( 260, 0, 94, -19, 167, 737 ),
+        new CharMetrics( 556, 0, 43, -191, 512, 737 ), new CharMetrics( 333, 0, 40, 604, 293, 706 ),
+        new CharMetrics( 737, 0, -14, -19, 752, 737 ), new CharMetrics( 370, 0, 24, 304, 346, 737 ),
+        new CharMetrics( 556, 0, 97, 108, 459, 446 ), new CharMetrics( 584, 0, 39, 108, 545, 390 ),
+        new CharMetrics( 333, 0, 44, 232, 289, 322 ), new CharMetrics( 737, 0, -14, -19, 752, 737 ),
+        new CharMetrics( 333, 0, 10, 627, 323, 684 ), new CharMetrics( 400, 0, 54, 411, 346, 703 ),
+        new CharMetrics( 584, 0, 39, 0, 545, 506 ), new CharMetrics( 333, 0, 4, 281, 323, 703 ),
+        new CharMetrics( 333, 0, 5, 270, 325, 703 ), new CharMetrics( 333, 0, 122, 593, 319, 734 ),
+        new CharMetrics( 556, 0, 68, -207, 489, 523 ), new CharMetrics( 537, 0, 18, -173, 497, 718 ),
+        new CharMetrics( 278, 0, 77, 190, 202, 315 ), new CharMetrics( 333, 0, 45, -225, 259, 0 ),
+        new CharMetrics( 333, 0, 43, 281, 222, 703 ), new CharMetrics( 365, 0, 25, 304, 341, 737 ),
+        new CharMetrics( 556, 0, 97, 108, 459, 446 ), new CharMetrics( 834, 0, 73, -19, 756, 703 ),
+        new CharMetrics( 834, 0, 43, -19, 773, 703 ), new CharMetrics( 834, 0, 45, -19, 810, 703 ),
+        new CharMetrics( 611, 0, 91, -201, 527, 525 ), new CharMetrics( 667, 0, 14, 0, 654, 929 ),
+        new CharMetrics( 667, 0, 14, 0, 654, 929 ), new CharMetrics( 667, 0, 14, 0, 654, 929 ),
+        new CharMetrics( 667, 0, 14, 0, 654, 917 ), new CharMetrics( 667, 0, 14, 0, 654, 901 ),
+        new CharMetrics( 667, 0, 14, 0, 654, 931 ), new CharMetrics( 1000, 0, 8, 0, 951, 718 ),
+        new CharMetrics( 722, 0, 44, -225, 681, 737 ), new CharMetrics( 667, 0, 86, 0, 616, 929 ),
+        new CharMetrics( 667, 0, 86, 0, 616, 929 ), new CharMetrics( 667, 0, 86, 0, 616, 929 ),
+        new CharMetrics( 667, 0, 86, 0, 616, 901 ), new CharMetrics( 278, 0, -13, 0, 188, 929 ),
+        new CharMetrics( 278, 0, 91, 0, 292, 929 ), new CharMetrics( 278, 0, -6, 0, 285, 929 ),
+        new CharMetrics( 278, 0, 13, 0, 266, 901 ), new CharMetrics( 722, 0, 0, 0, 674, 718 ),
+        new CharMetrics( 722, 0, 76, 0, 646, 917 ), new CharMetrics( 778, 0, 39, -19, 739, 929 ),
+        new CharMetrics( 778, 0, 39, -19, 739, 929 ), new CharMetrics( 778, 0, 39, -19, 739, 929 ),
+        new CharMetrics( 778, 0, 39, -19, 739, 917 ), new CharMetrics( 778, 0, 39, -19, 739, 901 ),
+        new CharMetrics( 584, 0, 39, 0, 545, 506 ), new CharMetrics( 778, 0, 39, -19, 740, 737 ),
+        new CharMetrics( 722, 0, 79, -19, 644, 929 ), new CharMetrics( 722, 0, 79, -19, 644, 929 ),
+        new CharMetrics( 722, 0, 79, -19, 644, 929 ), new CharMetrics( 722, 0, 79, -19, 644, 901 ),
+        new CharMetrics( 667, 0, 14, 0, 653, 929 ), new CharMetrics( 667, 0, 86, 0, 622, 718 ),
+        new CharMetrics( 611, 0, 67, -15, 571, 728 ), new CharMetrics( 556, 0, 36, -15, 530, 734 ),
+        new CharMetrics( 556, 0, 36, -15, 530, 734 ), new CharMetrics( 556, 0, 36, -15, 530, 734 ),
+        new CharMetrics( 556, 0, 36, -15, 530, 722 ), new CharMetrics( 556, 0, 36, -15, 530, 706 ),
+        new CharMetrics( 556, 0, 36, -15, 530, 756 ), new CharMetrics( 889, 0, 36, -15, 847, 538 ),
+        new CharMetrics( 500, 0, 30, -225, 477, 538 ), new CharMetrics( 556, 0, 40, -15, 516, 734 ),
+        new CharMetrics( 556, 0, 40, -15, 516, 734 ), new CharMetrics( 556, 0, 40, -15, 516, 734 ),
+        new CharMetrics( 556, 0, 40, -15, 516, 706 ), new CharMetrics( 278, 0, -13, 0, 184, 734 ),
+        new CharMetrics( 278, 0, 95, 0, 292, 734 ), new CharMetrics( 278, 0, -6, 0, 285, 734 ),
+        new CharMetrics( 278, 0, 13, 0, 266, 706 ), new CharMetrics( 556, 0, 35, -15, 522, 737 ),
+        new CharMetrics( 556, 0, 65, 0, 491, 722 ), new CharMetrics( 556, 0, 35, -14, 521, 734 ),
+        new CharMetrics( 556, 0, 35, -14, 521, 734 ), new CharMetrics( 556, 0, 35, -14, 521, 734 ),
+        new CharMetrics( 556, 0, 35, -14, 521, 722 ), new CharMetrics( 556, 0, 35, -14, 521, 706 ),
+        new CharMetrics( 584, 0, 39, -19, 545, 524 ), new CharMetrics( 611, 0, 28, -22, 537, 545 ),
+        new CharMetrics( 556, 0, 68, -15, 489, 734 ), new CharMetrics( 556, 0, 68, -15, 489, 734 ),
+        new CharMetrics( 556, 0, 68, -15, 489, 734 ), new CharMetrics( 556, 0, 68, -15, 489, 706 ),
+        new CharMetrics( 500, 0, 11, -214, 489, 734 ), new CharMetrics( 556, 0, 58, -207, 517, 718 ),
+        new CharMetrics( 500, 0, 11, -214, 489, 706 )};
+
+    SansSerif()
+    {
+        super( false, 718, -207, new CharMetrics( 0, 0, -166, -225, 1000, 931 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifBold.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifBold.java
new file mode 100644
index 0000000..a4799fa
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifBold.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: SansSerifBold.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class SansSerifBold
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 90, 0, 244, 718 ), new CharMetrics( 474, 0, 98, 447, 376, 718 ),
+        new CharMetrics( 556, 0, 18, 0, 538, 698 ), new CharMetrics( 556, 0, 30, -115, 523, 775 ),
+        new CharMetrics( 889, 0, 28, -19, 861, 710 ), new CharMetrics( 722, 0, 54, -19, 701, 718 ),
+        new CharMetrics( 278, 0, 69, 445, 209, 718 ), new CharMetrics( 333, 0, 35, -208, 314, 734 ),
+        new CharMetrics( 333, 0, 19, -208, 298, 734 ), new CharMetrics( 389, 0, 27, 387, 362, 718 ),
+        new CharMetrics( 584, 0, 40, 0, 544, 506 ), new CharMetrics( 278, 0, 64, -168, 214, 146 ),
+        new CharMetrics( 333, 0, 27, 215, 306, 345 ), new CharMetrics( 278, 0, 64, 0, 214, 146 ),
+        new CharMetrics( 278, 0, -33, -19, 311, 737 ), new CharMetrics( 556, 0, 32, -19, 524, 710 ),
+        new CharMetrics( 556, 0, 69, 0, 378, 710 ), new CharMetrics( 556, 0, 26, 0, 511, 710 ),
+        new CharMetrics( 556, 0, 27, -19, 516, 710 ), new CharMetrics( 556, 0, 27, 0, 526, 710 ),
+        new CharMetrics( 556, 0, 27, -19, 516, 698 ), new CharMetrics( 556, 0, 31, -19, 520, 710 ),
+        new CharMetrics( 556, 0, 25, 0, 528, 698 ), new CharMetrics( 556, 0, 32, -19, 524, 710 ),
+        new CharMetrics( 556, 0, 30, -19, 522, 710 ), new CharMetrics( 333, 0, 92, 0, 242, 512 ),
+        new CharMetrics( 333, 0, 92, -168, 242, 512 ), new CharMetrics( 584, 0, 38, -8, 546, 514 ),
+        new CharMetrics( 584, 0, 40, 87, 544, 419 ), new CharMetrics( 584, 0, 38, -8, 546, 514 ),
+        new CharMetrics( 611, 0, 60, 0, 556, 727 ), new CharMetrics( 975, 0, 118, -19, 856, 737 ),
+        new CharMetrics( 722, 0, 20, 0, 702, 718 ), new CharMetrics( 722, 0, 76, 0, 669, 718 ),
+        new CharMetrics( 722, 0, 44, -19, 684, 737 ), new CharMetrics( 722, 0, 76, 0, 685, 718 ),
+        new CharMetrics( 667, 0, 76, 0, 621, 718 ), new CharMetrics( 611, 0, 76, 0, 587, 718 ),
+        new CharMetrics( 778, 0, 44, -19, 713, 737 ), new CharMetrics( 722, 0, 71, 0, 651, 718 ),
+        new CharMetrics( 278, 0, 64, 0, 214, 718 ), new CharMetrics( 556, 0, 22, -18, 484, 718 ),
+        new CharMetrics( 722, 0, 87, 0, 722, 718 ), new CharMetrics( 611, 0, 76, 0, 583, 718 ),
+        new CharMetrics( 833, 0, 69, 0, 765, 718 ), new CharMetrics( 722, 0, 69, 0, 654, 718 ),
+        new CharMetrics( 778, 0, 44, -19, 734, 737 ), new CharMetrics( 667, 0, 76, 0, 627, 718 ),
+        new CharMetrics( 778, 0, 44, -52, 737, 737 ), new CharMetrics( 722, 0, 76, 0, 677, 718 ),
+        new CharMetrics( 667, 0, 39, -19, 629, 737 ), new CharMetrics( 611, 0, 14, 0, 598, 718 ),
+        new CharMetrics( 722, 0, 72, -19, 651, 718 ), new CharMetrics( 667, 0, 19, 0, 648, 718 ),
+        new CharMetrics( 944, 0, 16, 0, 929, 718 ), new CharMetrics( 667, 0, 14, 0, 653, 718 ),
+        new CharMetrics( 667, 0, 15, 0, 653, 718 ), new CharMetrics( 611, 0, 25, 0, 586, 718 ),
+        new CharMetrics( 333, 0, 63, -196, 309, 722 ), new CharMetrics( 278, 0, -33, -19, 311, 737 ),
+        new CharMetrics( 333, 0, 24, -196, 270, 722 ), new CharMetrics( 584, 0, 62, 323, 522, 698 ),
+        new CharMetrics( 556, 0, 0, -125, 556, -75 ), new CharMetrics( 278, 0, 69, 454, 209, 727 ),
+        new CharMetrics( 556, 0, 29, -14, 527, 546 ), new CharMetrics( 611, 0, 61, -14, 578, 718 ),
+        new CharMetrics( 556, 0, 34, -14, 524, 546 ), new CharMetrics( 611, 0, 34, -14, 551, 718 ),
+        new CharMetrics( 556, 0, 23, -14, 528, 546 ), new CharMetrics( 333, 0, 10, 0, 318, 727 ),
+        new CharMetrics( 611, 0, 40, -217, 553, 546 ), new CharMetrics( 611, 0, 65, 0, 546, 718 ),
+        new CharMetrics( 278, 0, 69, 0, 209, 725 ), new CharMetrics( 278, 0, 3, -214, 209, 725 ),
+        new CharMetrics( 556, 0, 69, 0, 562, 718 ), new CharMetrics( 278, 0, 69, 0, 209, 718 ),
+        new CharMetrics( 889, 0, 64, 0, 826, 546 ), new CharMetrics( 611, 0, 65, 0, 546, 546 ),
+        new CharMetrics( 611, 0, 34, -14, 578, 546 ), new CharMetrics( 611, 0, 62, -207, 578, 546 ),
+        new CharMetrics( 611, 0, 34, -207, 552, 546 ), new CharMetrics( 389, 0, 64, 0, 373, 546 ),
+        new CharMetrics( 556, 0, 30, -14, 519, 546 ), new CharMetrics( 333, 0, 10, -6, 309, 676 ),
+        new CharMetrics( 611, 0, 66, -14, 545, 532 ), new CharMetrics( 556, 0, 13, 0, 543, 532 ),
+        new CharMetrics( 778, 0, 10, 0, 769, 532 ), new CharMetrics( 556, 0, 15, 0, 541, 532 ),
+        new CharMetrics( 556, 0, 10, -214, 539, 532 ), new CharMetrics( 500, 0, 20, 0, 480, 532 ),
+        new CharMetrics( 389, 0, 48, -196, 365, 722 ), new CharMetrics( 280, 0, 84, -19, 196, 737 ),
+        new CharMetrics( 389, 0, 24, -196, 341, 722 ), new CharMetrics( 584, 0, 61, 163, 523, 343 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 69, 0, 209, 532 ),
+        new CharMetrics( 333, 0, -23, 604, 225, 750 ), new CharMetrics( 333, 0, 108, 604, 356, 750 ),
+        new CharMetrics( 333, 0, -10, 604, 343, 750 ), new CharMetrics( 333, 0, -17, 610, 350, 737 ),
+        new CharMetrics( 333, 0, -6, 604, 339, 678 ), new CharMetrics( 333, 0, -2, 604, 335, 750 ),
+        new CharMetrics( 333, 0, 104, 614, 230, 729 ), new CharMetrics( 333, 0, 6, 614, 327, 729 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 333, 0, 59, 568, 275, 776 ),
+        new CharMetrics( 333, 0, 6, -228, 245, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 9, 604, 486, 750 ), new CharMetrics( 333, 0, 71, -228, 304, 0 ),
+        new CharMetrics( 333, 0, -10, 604, 343, 750 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 90, -186, 244, 532 ), new CharMetrics( 556, 0, 34, -118, 524, 628 ),
+        new CharMetrics( 556, 0, 28, -16, 541, 718 ), new CharMetrics( 556, 0, -3, 76, 559, 636 ),
+        new CharMetrics( 556, 0, -9, 0, 565, 698 ), new CharMetrics( 280, 0, 84, -19, 196, 737 ),
+        new CharMetrics( 556, 0, 34, -184, 522, 727 ), new CharMetrics( 333, 0, 6, 614, 327, 729 ),
+        new CharMetrics( 737, 0, -11, -19, 749, 737 ), new CharMetrics( 370, 0, 22, 276, 347, 737 ),
+        new CharMetrics( 556, 0, 88, 76, 468, 484 ), new CharMetrics( 584, 0, 40, 108, 544, 419 ),
+        new CharMetrics( 333, 0, 27, 215, 306, 345 ), new CharMetrics( 737, 0, -11, -19, 748, 737 ),
+        new CharMetrics( 333, 0, -6, 604, 339, 678 ), new CharMetrics( 400, 0, 57, 426, 343, 712 ),
+        new CharMetrics( 584, 0, 40, 0, 544, 506 ), new CharMetrics( 333, 0, 9, 283, 324, 710 ),
+        new CharMetrics( 333, 0, 8, 271, 326, 710 ), new CharMetrics( 333, 0, 108, 604, 356, 750 ),
+        new CharMetrics( 611, 0, 66, -207, 545, 532 ), new CharMetrics( 556, 0, -8, -191, 539, 700 ),
+        new CharMetrics( 278, 0, 58, 172, 220, 334 ), new CharMetrics( 333, 0, 6, -228, 245, 0 ),
+        new CharMetrics( 333, 0, 26, 283, 237, 710 ), new CharMetrics( 365, 0, 6, 276, 360, 737 ),
+        new CharMetrics( 556, 0, 88, 76, 468, 484 ), new CharMetrics( 834, 0, 26, -19, 766, 710 ),
+        new CharMetrics( 834, 0, 26, -19, 794, 710 ), new CharMetrics( 834, 0, 16, -19, 799, 710 ),
+        new CharMetrics( 611, 0, 55, -195, 551, 532 ), new CharMetrics( 722, 0, 20, 0, 702, 936 ),
+        new CharMetrics( 722, 0, 20, 0, 702, 936 ), new CharMetrics( 722, 0, 20, 0, 702, 936 ),
+        new CharMetrics( 722, 0, 20, 0, 702, 923 ), new CharMetrics( 722, 0, 20, 0, 702, 915 ),
+        new CharMetrics( 722, 0, 20, 0, 702, 962 ), new CharMetrics( 1000, 0, 5, 0, 954, 718 ),
+        new CharMetrics( 722, 0, 44, -228, 684, 737 ), new CharMetrics( 667, 0, 76, 0, 621, 936 ),
+        new CharMetrics( 667, 0, 76, 0, 621, 936 ), new CharMetrics( 667, 0, 76, 0, 621, 936 ),
+        new CharMetrics( 667, 0, 76, 0, 621, 915 ), new CharMetrics( 278, 0, -50, 0, 214, 936 ),
+        new CharMetrics( 278, 0, 64, 0, 329, 936 ), new CharMetrics( 278, 0, -37, 0, 316, 936 ),
+        new CharMetrics( 278, 0, -21, 0, 300, 915 ), new CharMetrics( 722, 0, -5, 0, 685, 718 ),
+        new CharMetrics( 722, 0, 69, 0, 654, 923 ), new CharMetrics( 778, 0, 44, -19, 734, 936 ),
+        new CharMetrics( 778, 0, 44, -19, 734, 936 ), new CharMetrics( 778, 0, 44, -19, 734, 936 ),
+        new CharMetrics( 778, 0, 44, -19, 734, 923 ), new CharMetrics( 778, 0, 44, -19, 734, 915 ),
+        new CharMetrics( 584, 0, 40, 1, 545, 505 ), new CharMetrics( 778, 0, 33, -27, 744, 745 ),
+        new CharMetrics( 722, 0, 72, -19, 651, 936 ), new CharMetrics( 722, 0, 72, -19, 651, 936 ),
+        new CharMetrics( 722, 0, 72, -19, 651, 936 ), new CharMetrics( 722, 0, 72, -19, 651, 915 ),
+        new CharMetrics( 667, 0, 15, 0, 653, 936 ), new CharMetrics( 667, 0, 76, 0, 627, 718 ),
+        new CharMetrics( 611, 0, 69, -14, 579, 731 ), new CharMetrics( 556, 0, 29, -14, 527, 750 ),
+        new CharMetrics( 556, 0, 29, -14, 527, 750 ), new CharMetrics( 556, 0, 29, -14, 527, 750 ),
+        new CharMetrics( 556, 0, 29, -14, 527, 737 ), new CharMetrics( 556, 0, 29, -14, 527, 729 ),
+        new CharMetrics( 556, 0, 29, -14, 527, 776 ), new CharMetrics( 889, 0, 29, -14, 858, 546 ),
+        new CharMetrics( 556, 0, 34, -228, 524, 546 ), new CharMetrics( 556, 0, 23, -14, 528, 750 ),
+        new CharMetrics( 556, 0, 23, -14, 528, 750 ), new CharMetrics( 556, 0, 23, -14, 528, 750 ),
+        new CharMetrics( 556, 0, 23, -14, 528, 729 ), new CharMetrics( 278, 0, -50, 0, 209, 750 ),
+        new CharMetrics( 278, 0, 69, 0, 329, 750 ), new CharMetrics( 278, 0, -37, 0, 316, 750 ),
+        new CharMetrics( 278, 0, -21, 0, 300, 729 ), new CharMetrics( 611, 0, 34, -14, 578, 737 ),
+        new CharMetrics( 611, 0, 65, 0, 546, 737 ), new CharMetrics( 611, 0, 34, -14, 578, 750 ),
+        new CharMetrics( 611, 0, 34, -14, 578, 750 ), new CharMetrics( 611, 0, 34, -14, 578, 750 ),
+        new CharMetrics( 611, 0, 34, -14, 578, 737 ), new CharMetrics( 611, 0, 34, -14, 578, 729 ),
+        new CharMetrics( 584, 0, 40, -42, 544, 548 ), new CharMetrics( 611, 0, 22, -29, 589, 560 ),
+        new CharMetrics( 611, 0, 66, -14, 545, 750 ), new CharMetrics( 611, 0, 66, -14, 545, 750 ),
+        new CharMetrics( 611, 0, 66, -14, 545, 750 ), new CharMetrics( 611, 0, 66, -14, 545, 729 ),
+        new CharMetrics( 556, 0, 10, -214, 539, 750 ), new CharMetrics( 611, 0, 62, -208, 578, 718 ),
+        new CharMetrics( 556, 0, 10, -214, 539, 729 )};
+
+    SansSerifBold()
+    {
+        super( false, 718, -207, new CharMetrics( 0, 0, -170, -228, 1003, 962 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifBoldItalic.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifBoldItalic.java
new file mode 100644
index 0000000..92de3a1
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifBoldItalic.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: SansSerifBoldItalic.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class SansSerifBoldItalic
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 94, 0, 397, 718 ), new CharMetrics( 474, 0, 193, 447, 529, 718 ),
+        new CharMetrics( 556, 0, 60, 0, 644, 698 ), new CharMetrics( 556, 0, 67, -115, 622, 775 ),
+        new CharMetrics( 889, 0, 136, -19, 901, 710 ), new CharMetrics( 722, 0, 89, -19, 732, 718 ),
+        new CharMetrics( 278, 0, 167, 445, 362, 718 ), new CharMetrics( 333, 0, 76, -208, 470, 734 ),
+        new CharMetrics( 333, 0, -25, -208, 369, 734 ), new CharMetrics( 389, 0, 146, 387, 481, 718 ),
+        new CharMetrics( 584, 0, 82, 0, 610, 506 ), new CharMetrics( 278, 0, 28, -168, 245, 146 ),
+        new CharMetrics( 333, 0, 73, 215, 379, 345 ), new CharMetrics( 278, 0, 64, 0, 245, 146 ),
+        new CharMetrics( 278, 0, -37, -19, 468, 737 ), new CharMetrics( 556, 0, 86, -19, 617, 710 ),
+        new CharMetrics( 556, 0, 173, 0, 529, 710 ), new CharMetrics( 556, 0, 26, 0, 619, 710 ),
+        new CharMetrics( 556, 0, 65, -19, 608, 710 ), new CharMetrics( 556, 0, 60, 0, 598, 710 ),
+        new CharMetrics( 556, 0, 64, -19, 636, 698 ), new CharMetrics( 556, 0, 85, -19, 619, 710 ),
+        new CharMetrics( 556, 0, 125, 0, 676, 698 ), new CharMetrics( 556, 0, 69, -19, 616, 710 ),
+        new CharMetrics( 556, 0, 78, -19, 615, 710 ), new CharMetrics( 333, 0, 92, 0, 351, 512 ),
+        new CharMetrics( 333, 0, 56, -168, 351, 512 ), new CharMetrics( 584, 0, 82, -8, 655, 514 ),
+        new CharMetrics( 584, 0, 58, 87, 633, 419 ), new CharMetrics( 584, 0, 36, -8, 609, 514 ),
+        new CharMetrics( 611, 0, 165, 0, 671, 727 ), new CharMetrics( 975, 0, 186, -19, 954, 737 ),
+        new CharMetrics( 722, 0, 20, 0, 702, 718 ), new CharMetrics( 722, 0, 76, 0, 764, 718 ),
+        new CharMetrics( 722, 0, 107, -19, 789, 737 ), new CharMetrics( 722, 0, 76, 0, 777, 718 ),
+        new CharMetrics( 667, 0, 76, 0, 757, 718 ), new CharMetrics( 611, 0, 76, 0, 740, 718 ),
+        new CharMetrics( 778, 0, 108, -19, 817, 737 ), new CharMetrics( 722, 0, 71, 0, 804, 718 ),
+        new CharMetrics( 278, 0, 64, 0, 367, 718 ), new CharMetrics( 556, 0, 60, -18, 637, 718 ),
+        new CharMetrics( 722, 0, 87, 0, 858, 718 ), new CharMetrics( 611, 0, 76, 0, 611, 718 ),
+        new CharMetrics( 833, 0, 69, 0, 918, 718 ), new CharMetrics( 722, 0, 69, 0, 807, 718 ),
+        new CharMetrics( 778, 0, 107, -19, 823, 737 ), new CharMetrics( 667, 0, 76, 0, 738, 718 ),
+        new CharMetrics( 778, 0, 107, -52, 823, 737 ), new CharMetrics( 722, 0, 76, 0, 778, 718 ),
+        new CharMetrics( 667, 0, 81, -19, 718, 737 ), new CharMetrics( 611, 0, 140, 0, 751, 718 ),
+        new CharMetrics( 722, 0, 116, -19, 804, 718 ), new CharMetrics( 667, 0, 172, 0, 801, 718 ),
+        new CharMetrics( 944, 0, 169, 0, 1082, 718 ), new CharMetrics( 667, 0, 14, 0, 791, 718 ),
+        new CharMetrics( 667, 0, 168, 0, 806, 718 ), new CharMetrics( 611, 0, 25, 0, 737, 718 ),
+        new CharMetrics( 333, 0, 21, -196, 462, 722 ), new CharMetrics( 278, 0, 124, -19, 307, 737 ),
+        new CharMetrics( 333, 0, -18, -196, 423, 722 ), new CharMetrics( 584, 0, 131, 323, 591, 698 ),
+        new CharMetrics( 556, 0, -27, -125, 540, -75 ), new CharMetrics( 278, 0, 165, 454, 361, 727 ),
+        new CharMetrics( 556, 0, 55, -14, 583, 546 ), new CharMetrics( 611, 0, 61, -14, 645, 718 ),
+        new CharMetrics( 556, 0, 79, -14, 599, 546 ), new CharMetrics( 611, 0, 82, -14, 704, 718 ),
+        new CharMetrics( 556, 0, 70, -14, 593, 546 ), new CharMetrics( 333, 0, 87, 0, 469, 727 ),
+        new CharMetrics( 611, 0, 38, -217, 666, 546 ), new CharMetrics( 611, 0, 65, 0, 629, 718 ),
+        new CharMetrics( 278, 0, 69, 0, 363, 725 ), new CharMetrics( 278, 0, -42, -214, 363, 725 ),
+        new CharMetrics( 556, 0, 69, 0, 670, 718 ), new CharMetrics( 278, 0, 69, 0, 362, 718 ),
+        new CharMetrics( 889, 0, 64, 0, 909, 546 ), new CharMetrics( 611, 0, 65, 0, 629, 546 ),
+        new CharMetrics( 611, 0, 82, -14, 643, 546 ), new CharMetrics( 611, 0, 18, -207, 645, 546 ),
+        new CharMetrics( 611, 0, 80, -207, 665, 546 ), new CharMetrics( 389, 0, 64, 0, 489, 546 ),
+        new CharMetrics( 556, 0, 63, -14, 584, 546 ), new CharMetrics( 333, 0, 100, -6, 422, 676 ),
+        new CharMetrics( 611, 0, 98, -14, 658, 532 ), new CharMetrics( 556, 0, 126, 0, 656, 532 ),
+        new CharMetrics( 778, 0, 123, 0, 882, 532 ), new CharMetrics( 556, 0, 15, 0, 648, 532 ),
+        new CharMetrics( 556, 0, 42, -214, 652, 532 ), new CharMetrics( 500, 0, 20, 0, 583, 532 ),
+        new CharMetrics( 389, 0, 94, -196, 518, 722 ), new CharMetrics( 280, 0, 80, -19, 353, 737 ),
+        new CharMetrics( 389, 0, -18, -196, 407, 722 ), new CharMetrics( 584, 0, 115, 163, 577, 343 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 69, 0, 322, 532 ),
+        new CharMetrics( 333, 0, 136, 604, 353, 750 ), new CharMetrics( 333, 0, 236, 604, 515, 750 ),
+        new CharMetrics( 333, 0, 118, 604, 471, 750 ), new CharMetrics( 333, 0, 113, 610, 507, 737 ),
+        new CharMetrics( 333, 0, 122, 604, 483, 678 ), new CharMetrics( 333, 0, 156, 604, 494, 750 ),
+        new CharMetrics( 333, 0, 235, 614, 385, 729 ), new CharMetrics( 333, 0, 137, 614, 482, 729 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 333, 0, 200, 568, 420, 776 ),
+        new CharMetrics( 333, 0, -37, -228, 220, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 137, 604, 645, 750 ), new CharMetrics( 333, 0, 41, -228, 264, 0 ),
+        new CharMetrics( 333, 0, 149, 604, 502, 750 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 50, -186, 353, 532 ), new CharMetrics( 556, 0, 79, -118, 599, 628 ),
+        new CharMetrics( 556, 0, 50, -16, 635, 718 ), new CharMetrics( 556, 0, 27, 76, 680, 636 ),
+        new CharMetrics( 556, 0, 60, 0, 713, 698 ), new CharMetrics( 280, 0, 80, -19, 353, 737 ),
+        new CharMetrics( 556, 0, 61, -184, 598, 727 ), new CharMetrics( 333, 0, 137, 614, 482, 729 ),
+        new CharMetrics( 737, 0, 56, -19, 835, 737 ), new CharMetrics( 370, 0, 92, 276, 465, 737 ),
+        new CharMetrics( 556, 0, 135, 76, 571, 484 ), new CharMetrics( 584, 0, 105, 108, 633, 419 ),
+        new CharMetrics( 333, 0, 73, 215, 379, 345 ), new CharMetrics( 737, 0, 55, -19, 834, 737 ),
+        new CharMetrics( 333, 0, 122, 604, 483, 678 ), new CharMetrics( 400, 0, 175, 426, 467, 712 ),
+        new CharMetrics( 584, 0, 40, 0, 625, 506 ), new CharMetrics( 333, 0, 69, 283, 449, 710 ),
+        new CharMetrics( 333, 0, 91, 271, 441, 710 ), new CharMetrics( 333, 0, 236, 604, 515, 750 ),
+        new CharMetrics( 611, 0, 22, -207, 658, 532 ), new CharMetrics( 556, 0, 98, -191, 688, 700 ),
+        new CharMetrics( 278, 0, 110, 172, 276, 334 ), new CharMetrics( 333, 0, -37, -228, 220, 0 ),
+        new CharMetrics( 333, 0, 148, 283, 388, 710 ), new CharMetrics( 365, 0, 92, 276, 485, 737 ),
+        new CharMetrics( 556, 0, 104, 76, 540, 484 ), new CharMetrics( 834, 0, 132, -19, 806, 710 ),
+        new CharMetrics( 834, 0, 132, -19, 858, 710 ), new CharMetrics( 834, 0, 99, -19, 839, 710 ),
+        new CharMetrics( 611, 0, 53, -195, 559, 532 ), new CharMetrics( 722, 0, 20, 0, 702, 936 ),
+        new CharMetrics( 722, 0, 20, 0, 750, 936 ), new CharMetrics( 722, 0, 20, 0, 706, 936 ),
+        new CharMetrics( 722, 0, 20, 0, 741, 923 ), new CharMetrics( 722, 0, 20, 0, 716, 915 ),
+        new CharMetrics( 722, 0, 20, 0, 702, 962 ), new CharMetrics( 1000, 0, 5, 0, 1100, 718 ),
+        new CharMetrics( 722, 0, 107, -228, 789, 737 ), new CharMetrics( 667, 0, 76, 0, 757, 936 ),
+        new CharMetrics( 667, 0, 76, 0, 757, 936 ), new CharMetrics( 667, 0, 76, 0, 757, 936 ),
+        new CharMetrics( 667, 0, 76, 0, 757, 915 ), new CharMetrics( 278, 0, 64, 0, 367, 936 ),
+        new CharMetrics( 278, 0, 64, 0, 528, 936 ), new CharMetrics( 278, 0, 64, 0, 484, 936 ),
+        new CharMetrics( 278, 0, 64, 0, 494, 915 ), new CharMetrics( 722, 0, 62, 0, 777, 718 ),
+        new CharMetrics( 722, 0, 69, 0, 807, 923 ), new CharMetrics( 778, 0, 107, -19, 823, 936 ),
+        new CharMetrics( 778, 0, 107, -19, 823, 936 ), new CharMetrics( 778, 0, 107, -19, 823, 936 ),
+        new CharMetrics( 778, 0, 107, -19, 823, 923 ), new CharMetrics( 778, 0, 107, -19, 823, 915 ),
+        new CharMetrics( 584, 0, 57, 1, 635, 505 ), new CharMetrics( 778, 0, 35, -27, 894, 745 ),
+        new CharMetrics( 722, 0, 116, -19, 804, 936 ), new CharMetrics( 722, 0, 116, -19, 804, 936 ),
+        new CharMetrics( 722, 0, 116, -19, 804, 936 ), new CharMetrics( 722, 0, 116, -19, 804, 915 ),
+        new CharMetrics( 667, 0, 168, 0, 806, 936 ), new CharMetrics( 667, 0, 76, 0, 716, 718 ),
+        new CharMetrics( 611, 0, 69, -14, 657, 731 ), new CharMetrics( 556, 0, 55, -14, 583, 750 ),
+        new CharMetrics( 556, 0, 55, -14, 627, 750 ), new CharMetrics( 556, 0, 55, -14, 583, 750 ),
+        new CharMetrics( 556, 0, 55, -14, 619, 737 ), new CharMetrics( 556, 0, 55, -14, 594, 729 ),
+        new CharMetrics( 556, 0, 55, -14, 583, 776 ), new CharMetrics( 889, 0, 56, -14, 923, 546 ),
+        new CharMetrics( 556, 0, 79, -228, 599, 546 ), new CharMetrics( 556, 0, 70, -14, 593, 750 ),
+        new CharMetrics( 556, 0, 70, -14, 627, 750 ), new CharMetrics( 556, 0, 70, -14, 593, 750 ),
+        new CharMetrics( 556, 0, 70, -14, 594, 729 ), new CharMetrics( 278, 0, 69, 0, 326, 750 ),
+        new CharMetrics( 278, 0, 69, 0, 488, 750 ), new CharMetrics( 278, 0, 69, 0, 444, 750 ),
+        new CharMetrics( 278, 0, 69, 0, 455, 729 ), new CharMetrics( 611, 0, 82, -14, 670, 737 ),
+        new CharMetrics( 611, 0, 65, 0, 646, 737 ), new CharMetrics( 611, 0, 82, -14, 643, 750 ),
+        new CharMetrics( 611, 0, 82, -14, 654, 750 ), new CharMetrics( 611, 0, 82, -14, 643, 750 ),
+        new CharMetrics( 611, 0, 82, -14, 646, 737 ), new CharMetrics( 611, 0, 82, -14, 643, 729 ),
+        new CharMetrics( 584, 0, 82, -42, 610, 548 ), new CharMetrics( 611, 0, 22, -29, 701, 560 ),
+        new CharMetrics( 611, 0, 98, -14, 658, 750 ), new CharMetrics( 611, 0, 98, -14, 658, 750 ),
+        new CharMetrics( 611, 0, 98, -14, 658, 750 ), new CharMetrics( 611, 0, 98, -14, 658, 729 ),
+        new CharMetrics( 556, 0, 42, -214, 652, 750 ), new CharMetrics( 611, 0, 18, -208, 645, 718 ),
+        new CharMetrics( 556, 0, 42, -214, 652, 729 )};
+
+    SansSerifBoldItalic()
+    {
+        super( false, 718, -207, new CharMetrics( 0, 0, -174, -228, 1114, 962 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifItalic.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifItalic.java
new file mode 100644
index 0000000..3bc1160
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SansSerifItalic.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: SansSerifItalic.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class SansSerifItalic
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 90, 0, 340, 718 ), new CharMetrics( 355, 0, 168, 463, 438, 718 ),
+        new CharMetrics( 556, 0, 73, 0, 631, 688 ), new CharMetrics( 556, 0, 69, -115, 617, 775 ),
+        new CharMetrics( 889, 0, 147, -19, 889, 703 ), new CharMetrics( 667, 0, 77, -15, 647, 718 ),
+        new CharMetrics( 222, 0, 151, 463, 310, 718 ), new CharMetrics( 333, 0, 108, -207, 454, 733 ),
+        new CharMetrics( 333, 0, -9, -207, 337, 733 ), new CharMetrics( 389, 0, 165, 431, 475, 718 ),
+        new CharMetrics( 584, 0, 85, 0, 606, 505 ), new CharMetrics( 278, 0, 56, -147, 214, 106 ),
+        new CharMetrics( 333, 0, 93, 232, 357, 322 ), new CharMetrics( 278, 0, 87, 0, 214, 106 ),
+        new CharMetrics( 278, 0, -21, -19, 452, 737 ), new CharMetrics( 556, 0, 93, -19, 608, 703 ),
+        new CharMetrics( 556, 0, 207, 0, 508, 703 ), new CharMetrics( 556, 0, 26, 0, 617, 703 ),
+        new CharMetrics( 556, 0, 75, -19, 610, 703 ), new CharMetrics( 556, 0, 61, 0, 576, 703 ),
+        new CharMetrics( 556, 0, 68, -19, 621, 688 ), new CharMetrics( 556, 0, 91, -19, 615, 703 ),
+        new CharMetrics( 556, 0, 137, 0, 669, 688 ), new CharMetrics( 556, 0, 74, -19, 607, 703 ),
+        new CharMetrics( 556, 0, 82, -19, 609, 703 ), new CharMetrics( 278, 0, 87, 0, 301, 516 ),
+        new CharMetrics( 278, 0, 56, -147, 301, 516 ), new CharMetrics( 584, 0, 94, 11, 641, 495 ),
+        new CharMetrics( 584, 0, 63, 115, 628, 390 ), new CharMetrics( 584, 0, 50, 11, 597, 495 ),
+        new CharMetrics( 556, 0, 161, 0, 610, 727 ), new CharMetrics( 1015, 0, 215, -19, 965, 737 ),
+        new CharMetrics( 667, 0, 14, 0, 654, 718 ), new CharMetrics( 667, 0, 74, 0, 712, 718 ),
+        new CharMetrics( 722, 0, 108, -19, 782, 737 ), new CharMetrics( 722, 0, 81, 0, 764, 718 ),
+        new CharMetrics( 667, 0, 86, 0, 762, 718 ), new CharMetrics( 611, 0, 86, 0, 736, 718 ),
+        new CharMetrics( 778, 0, 111, -19, 799, 737 ), new CharMetrics( 722, 0, 77, 0, 799, 718 ),
+        new CharMetrics( 278, 0, 91, 0, 341, 718 ), new CharMetrics( 500, 0, 47, -19, 581, 718 ),
+        new CharMetrics( 667, 0, 76, 0, 808, 718 ), new CharMetrics( 556, 0, 76, 0, 555, 718 ),
+        new CharMetrics( 833, 0, 73, 0, 914, 718 ), new CharMetrics( 722, 0, 76, 0, 799, 718 ),
+        new CharMetrics( 778, 0, 105, -19, 826, 737 ), new CharMetrics( 667, 0, 86, 0, 737, 718 ),
+        new CharMetrics( 778, 0, 105, -56, 826, 737 ), new CharMetrics( 722, 0, 88, 0, 773, 718 ),
+        new CharMetrics( 667, 0, 90, -19, 713, 737 ), new CharMetrics( 611, 0, 148, 0, 750, 718 ),
+        new CharMetrics( 722, 0, 123, -19, 797, 718 ), new CharMetrics( 667, 0, 173, 0, 800, 718 ),
+        new CharMetrics( 944, 0, 169, 0, 1081, 718 ), new CharMetrics( 667, 0, 19, 0, 790, 718 ),
+        new CharMetrics( 667, 0, 167, 0, 806, 718 ), new CharMetrics( 611, 0, 23, 0, 741, 718 ),
+        new CharMetrics( 278, 0, 21, -196, 403, 722 ), new CharMetrics( 278, 0, 140, -19, 291, 737 ),
+        new CharMetrics( 278, 0, -14, -196, 368, 722 ), new CharMetrics( 469, 0, 42, 264, 539, 688 ),
+        new CharMetrics( 556, 0, -27, -125, 540, -75 ), new CharMetrics( 222, 0, 165, 470, 323, 725 ),
+        new CharMetrics( 556, 0, 61, -15, 559, 538 ), new CharMetrics( 556, 0, 58, -15, 584, 718 ),
+        new CharMetrics( 500, 0, 74, -15, 553, 538 ), new CharMetrics( 556, 0, 84, -15, 652, 718 ),
+        new CharMetrics( 556, 0, 84, -15, 578, 538 ), new CharMetrics( 278, 0, 86, 0, 416, 728 ),
+        new CharMetrics( 556, 0, 42, -220, 610, 538 ), new CharMetrics( 556, 0, 65, 0, 573, 718 ),
+        new CharMetrics( 222, 0, 67, 0, 308, 718 ), new CharMetrics( 222, 0, -60, -210, 308, 718 ),
+        new CharMetrics( 500, 0, 67, 0, 600, 718 ), new CharMetrics( 222, 0, 67, 0, 308, 718 ),
+        new CharMetrics( 833, 0, 65, 0, 852, 538 ), new CharMetrics( 556, 0, 65, 0, 573, 538 ),
+        new CharMetrics( 556, 0, 83, -14, 585, 538 ), new CharMetrics( 556, 0, 14, -207, 584, 538 ),
+        new CharMetrics( 556, 0, 84, -207, 605, 538 ), new CharMetrics( 333, 0, 77, 0, 446, 538 ),
+        new CharMetrics( 500, 0, 63, -15, 529, 538 ), new CharMetrics( 278, 0, 102, -7, 368, 669 ),
+        new CharMetrics( 556, 0, 94, -15, 600, 523 ), new CharMetrics( 500, 0, 119, 0, 603, 523 ),
+        new CharMetrics( 722, 0, 125, 0, 820, 523 ), new CharMetrics( 500, 0, 11, 0, 594, 523 ),
+        new CharMetrics( 500, 0, 15, -214, 600, 523 ), new CharMetrics( 500, 0, 31, 0, 571, 523 ),
+        new CharMetrics( 334, 0, 92, -196, 445, 722 ), new CharMetrics( 260, 0, 90, -19, 324, 737 ),
+        new CharMetrics( 334, 0, 0, -196, 354, 722 ), new CharMetrics( 584, 0, 111, 180, 580, 326 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 95, 0, 294, 523 ),
+        new CharMetrics( 333, 0, 170, 593, 337, 734 ), new CharMetrics( 333, 0, 248, 593, 475, 734 ),
+        new CharMetrics( 333, 0, 147, 593, 438, 734 ), new CharMetrics( 333, 0, 125, 606, 490, 722 ),
+        new CharMetrics( 333, 0, 143, 627, 468, 684 ), new CharMetrics( 333, 0, 167, 595, 476, 731 ),
+        new CharMetrics( 333, 0, 249, 604, 362, 706 ), new CharMetrics( 333, 0, 168, 604, 443, 706 ),
+        new CharMetrics( 278, 0, 0, 0, 0, 0 ), new CharMetrics( 333, 0, 214, 572, 402, 756 ),
+        new CharMetrics( 333, 0, 2, -225, 232, 0 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 157, 593, 565, 734 ), new CharMetrics( 333, 0, 43, -225, 249, 0 ),
+        new CharMetrics( 333, 0, 177, 593, 468, 734 ), new CharMetrics( 278, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 77, -195, 326, 523 ), new CharMetrics( 556, 0, 95, -115, 584, 623 ),
+        new CharMetrics( 556, 0, 49, -16, 634, 718 ), new CharMetrics( 556, 0, 60, 99, 646, 603 ),
+        new CharMetrics( 556, 0, 81, 0, 699, 688 ), new CharMetrics( 260, 0, 90, -19, 324, 737 ),
+        new CharMetrics( 556, 0, 76, -191, 584, 737 ), new CharMetrics( 333, 0, 168, 604, 443, 706 ),
+        new CharMetrics( 737, 0, 54, -19, 837, 737 ), new CharMetrics( 370, 0, 100, 304, 449, 737 ),
+        new CharMetrics( 556, 0, 146, 108, 554, 446 ), new CharMetrics( 584, 0, 106, 108, 628, 390 ),
+        new CharMetrics( 333, 0, 93, 232, 357, 322 ), new CharMetrics( 737, 0, 54, -19, 837, 737 ),
+        new CharMetrics( 333, 0, 143, 627, 468, 684 ), new CharMetrics( 400, 0, 169, 411, 468, 703 ),
+        new CharMetrics( 584, 0, 39, 0, 618, 506 ), new CharMetrics( 333, 0, 64, 281, 449, 703 ),
+        new CharMetrics( 333, 0, 90, 270, 436, 703 ), new CharMetrics( 333, 0, 248, 593, 475, 734 ),
+        new CharMetrics( 556, 0, 24, -207, 600, 523 ), new CharMetrics( 537, 0, 126, -173, 650, 718 ),
+        new CharMetrics( 278, 0, 129, 190, 257, 315 ), new CharMetrics( 333, 0, 2, -225, 232, 0 ),
+        new CharMetrics( 333, 0, 166, 281, 371, 703 ), new CharMetrics( 365, 0, 100, 304, 468, 737 ),
+        new CharMetrics( 556, 0, 120, 108, 528, 446 ), new CharMetrics( 834, 0, 150, -19, 802, 703 ),
+        new CharMetrics( 834, 0, 114, -19, 839, 703 ), new CharMetrics( 834, 0, 130, -19, 861, 703 ),
+        new CharMetrics( 611, 0, 85, -201, 534, 525 ), new CharMetrics( 667, 0, 14, 0, 654, 929 ),
+        new CharMetrics( 667, 0, 14, 0, 683, 929 ), new CharMetrics( 667, 0, 14, 0, 654, 929 ),
+        new CharMetrics( 667, 0, 14, 0, 699, 917 ), new CharMetrics( 667, 0, 14, 0, 654, 901 ),
+        new CharMetrics( 667, 0, 14, 0, 654, 931 ), new CharMetrics( 1000, 0, 8, 0, 1097, 718 ),
+        new CharMetrics( 722, 0, 108, -225, 782, 737 ), new CharMetrics( 667, 0, 86, 0, 762, 929 ),
+        new CharMetrics( 667, 0, 86, 0, 762, 929 ), new CharMetrics( 667, 0, 86, 0, 762, 929 ),
+        new CharMetrics( 667, 0, 86, 0, 762, 901 ), new CharMetrics( 278, 0, 91, 0, 351, 929 ),
+        new CharMetrics( 278, 0, 91, 0, 489, 929 ), new CharMetrics( 278, 0, 91, 0, 452, 929 ),
+        new CharMetrics( 278, 0, 91, 0, 458, 901 ), new CharMetrics( 722, 0, 69, 0, 764, 718 ),
+        new CharMetrics( 722, 0, 76, 0, 799, 917 ), new CharMetrics( 778, 0, 105, -19, 826, 929 ),
+        new CharMetrics( 778, 0, 105, -19, 826, 929 ), new CharMetrics( 778, 0, 105, -19, 826, 929 ),
+        new CharMetrics( 778, 0, 105, -19, 826, 917 ), new CharMetrics( 778, 0, 105, -19, 826, 901 ),
+        new CharMetrics( 584, 0, 50, 0, 642, 506 ), new CharMetrics( 778, 0, 43, -19, 890, 737 ),
+        new CharMetrics( 722, 0, 123, -19, 797, 929 ), new CharMetrics( 722, 0, 123, -19, 797, 929 ),
+        new CharMetrics( 722, 0, 123, -19, 797, 929 ), new CharMetrics( 722, 0, 123, -19, 797, 901 ),
+        new CharMetrics( 667, 0, 167, 0, 806, 929 ), new CharMetrics( 667, 0, 86, 0, 712, 718 ),
+        new CharMetrics( 611, 0, 67, -15, 658, 728 ), new CharMetrics( 556, 0, 61, -15, 559, 734 ),
+        new CharMetrics( 556, 0, 61, -15, 587, 734 ), new CharMetrics( 556, 0, 61, -15, 559, 734 ),
+        new CharMetrics( 556, 0, 61, -15, 592, 722 ), new CharMetrics( 556, 0, 61, -15, 559, 706 ),
+        new CharMetrics( 556, 0, 61, -15, 559, 756 ), new CharMetrics( 889, 0, 61, -15, 909, 538 ),
+        new CharMetrics( 500, 0, 74, -225, 553, 538 ), new CharMetrics( 556, 0, 84, -15, 578, 734 ),
+        new CharMetrics( 556, 0, 84, -15, 587, 734 ), new CharMetrics( 556, 0, 84, -15, 578, 734 ),
+        new CharMetrics( 556, 0, 84, -15, 578, 706 ), new CharMetrics( 278, 0, 95, 0, 310, 734 ),
+        new CharMetrics( 278, 0, 95, 0, 448, 734 ), new CharMetrics( 278, 0, 95, 0, 411, 734 ),
+        new CharMetrics( 278, 0, 95, 0, 416, 706 ), new CharMetrics( 556, 0, 81, -15, 617, 737 ),
+        new CharMetrics( 556, 0, 65, 0, 592, 722 ), new CharMetrics( 556, 0, 83, -14, 585, 734 ),
+        new CharMetrics( 556, 0, 83, -14, 587, 734 ), new CharMetrics( 556, 0, 83, -14, 585, 734 ),
+        new CharMetrics( 556, 0, 83, -14, 602, 722 ), new CharMetrics( 556, 0, 83, -14, 585, 706 ),
+        new CharMetrics( 584, 0, 85, -19, 606, 524 ), new CharMetrics( 611, 0, 29, -22, 647, 545 ),
+        new CharMetrics( 556, 0, 94, -15, 600, 734 ), new CharMetrics( 556, 0, 94, -15, 600, 734 ),
+        new CharMetrics( 556, 0, 94, -15, 600, 734 ), new CharMetrics( 556, 0, 94, -15, 600, 706 ),
+        new CharMetrics( 500, 0, 15, -214, 600, 734 ), new CharMetrics( 556, 0, 14, -207, 584, 718 ),
+        new CharMetrics( 500, 0, 15, -214, 600, 706 )};
+
+    SansSerifItalic()
+    {
+        super( false, 718, -207, new CharMetrics( 0, 0, -170, -225, 1116, 931 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Serif.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Serif.java
new file mode 100644
index 0000000..8ecccb6
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/Serif.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: Serif.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class Serif
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 130, -9, 238, 676 ), new CharMetrics( 408, 0, 77, 431, 331, 676 ),
+        new CharMetrics( 500, 0, 5, 0, 496, 662 ), new CharMetrics( 500, 0, 44, -87, 457, 727 ),
+        new CharMetrics( 833, 0, 61, -13, 772, 676 ), new CharMetrics( 778, 0, 42, -13, 750, 676 ),
+        new CharMetrics( 333, 0, 79, 433, 218, 676 ), new CharMetrics( 333, 0, 48, -177, 304, 676 ),
+        new CharMetrics( 333, 0, 29, -177, 285, 676 ), new CharMetrics( 500, 0, 69, 265, 432, 676 ),
+        new CharMetrics( 564, 0, 30, 0, 534, 506 ), new CharMetrics( 250, 0, 56, -141, 195, 102 ),
+        new CharMetrics( 333, 0, 39, 194, 285, 257 ), new CharMetrics( 250, 0, 70, -11, 181, 100 ),
+        new CharMetrics( 278, 0, -9, -14, 287, 676 ), new CharMetrics( 500, 0, 24, -14, 476, 676 ),
+        new CharMetrics( 500, 0, 111, 0, 394, 676 ), new CharMetrics( 500, 0, 30, 0, 475, 676 ),
+        new CharMetrics( 500, 0, 43, -14, 431, 676 ), new CharMetrics( 500, 0, 12, 0, 472, 676 ),
+        new CharMetrics( 500, 0, 32, -14, 438, 688 ), new CharMetrics( 500, 0, 34, -14, 468, 684 ),
+        new CharMetrics( 500, 0, 20, -8, 449, 662 ), new CharMetrics( 500, 0, 56, -14, 445, 676 ),
+        new CharMetrics( 500, 0, 30, -22, 459, 676 ), new CharMetrics( 278, 0, 81, -11, 192, 459 ),
+        new CharMetrics( 278, 0, 80, -141, 219, 459 ), new CharMetrics( 564, 0, 28, -8, 536, 514 ),
+        new CharMetrics( 564, 0, 30, 120, 534, 386 ), new CharMetrics( 564, 0, 28, -8, 536, 514 ),
+        new CharMetrics( 444, 0, 68, -8, 414, 676 ), new CharMetrics( 921, 0, 116, -14, 809, 676 ),
+        new CharMetrics( 722, 0, 15, 0, 706, 674 ), new CharMetrics( 667, 0, 17, 0, 593, 662 ),
+        new CharMetrics( 667, 0, 28, -14, 633, 676 ), new CharMetrics( 722, 0, 16, 0, 685, 662 ),
+        new CharMetrics( 611, 0, 12, 0, 597, 662 ), new CharMetrics( 556, 0, 12, 0, 546, 662 ),
+        new CharMetrics( 722, 0, 32, -14, 709, 676 ), new CharMetrics( 722, 0, 19, 0, 702, 662 ),
+        new CharMetrics( 333, 0, 18, 0, 315, 662 ), new CharMetrics( 389, 0, 10, -14, 370, 662 ),
+        new CharMetrics( 722, 0, 34, 0, 723, 662 ), new CharMetrics( 611, 0, 12, 0, 598, 662 ),
+        new CharMetrics( 889, 0, 12, 0, 863, 662 ), new CharMetrics( 722, 0, 12, -11, 707, 662 ),
+        new CharMetrics( 722, 0, 34, -14, 688, 676 ), new CharMetrics( 556, 0, 16, 0, 542, 662 ),
+        new CharMetrics( 722, 0, 34, -178, 701, 676 ), new CharMetrics( 667, 0, 17, 0, 659, 662 ),
+        new CharMetrics( 556, 0, 42, -14, 491, 676 ), new CharMetrics( 611, 0, 17, 0, 593, 662 ),
+        new CharMetrics( 722, 0, 14, -14, 705, 662 ), new CharMetrics( 722, 0, 16, -11, 697, 662 ),
+        new CharMetrics( 944, 0, 5, -11, 932, 662 ), new CharMetrics( 722, 0, 10, 0, 704, 662 ),
+        new CharMetrics( 722, 0, 22, 0, 703, 662 ), new CharMetrics( 611, 0, 9, 0, 597, 662 ),
+        new CharMetrics( 333, 0, 88, -156, 299, 662 ), new CharMetrics( 278, 0, -9, -14, 287, 676 ),
+        new CharMetrics( 333, 0, 34, -156, 245, 662 ), new CharMetrics( 469, 0, 24, 297, 446, 662 ),
+        new CharMetrics( 500, 0, 0, -125, 500, -75 ), new CharMetrics( 333, 0, 115, 433, 254, 676 ),
+        new CharMetrics( 444, 0, 37, -10, 442, 460 ), new CharMetrics( 500, 0, 3, -10, 468, 683 ),
+        new CharMetrics( 444, 0, 25, -10, 412, 460 ), new CharMetrics( 500, 0, 27, -10, 491, 683 ),
+        new CharMetrics( 444, 0, 25, -10, 424, 460 ), new CharMetrics( 333, 0, 20, 0, 383, 683 ),
+        new CharMetrics( 500, 0, 28, -218, 470, 460 ), new CharMetrics( 500, 0, 9, 0, 487, 683 ),
+        new CharMetrics( 278, 0, 16, 0, 253, 683 ), new CharMetrics( 278, 0, -70, -218, 194, 683 ),
+        new CharMetrics( 500, 0, 7, 0, 505, 683 ), new CharMetrics( 278, 0, 19, 0, 257, 683 ),
+        new CharMetrics( 778, 0, 16, 0, 775, 460 ), new CharMetrics( 500, 0, 16, 0, 485, 460 ),
+        new CharMetrics( 500, 0, 29, -10, 470, 460 ), new CharMetrics( 500, 0, 5, -217, 470, 460 ),
+        new CharMetrics( 500, 0, 24, -217, 488, 460 ), new CharMetrics( 333, 0, 5, 0, 335, 460 ),
+        new CharMetrics( 389, 0, 51, -10, 348, 460 ), new CharMetrics( 278, 0, 13, -10, 279, 579 ),
+        new CharMetrics( 500, 0, 9, -10, 479, 450 ), new CharMetrics( 500, 0, 19, -14, 477, 450 ),
+        new CharMetrics( 722, 0, 21, -14, 694, 450 ), new CharMetrics( 500, 0, 17, 0, 479, 450 ),
+        new CharMetrics( 500, 0, 14, -218, 475, 450 ), new CharMetrics( 444, 0, 27, 0, 418, 450 ),
+        new CharMetrics( 480, 0, 100, -181, 350, 680 ), new CharMetrics( 200, 0, 67, -14, 133, 676 ),
+        new CharMetrics( 480, 0, 130, -181, 380, 680 ), new CharMetrics( 541, 0, 40, 183, 502, 323 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 16, 0, 253, 460 ),
+        new CharMetrics( 333, 0, 19, 507, 242, 678 ), new CharMetrics( 333, 0, 93, 507, 317, 678 ),
+        new CharMetrics( 333, 0, 11, 507, 322, 674 ), new CharMetrics( 333, 0, 1, 532, 331, 638 ),
+        new CharMetrics( 333, 0, 11, 547, 322, 601 ), new CharMetrics( 333, 0, 26, 507, 307, 664 ),
+        new CharMetrics( 333, 0, 118, 523, 216, 623 ), new CharMetrics( 333, 0, 18, 523, 315, 623 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 333, 0, 67, 512, 266, 711 ),
+        new CharMetrics( 333, 0, 52, -215, 261, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, -3, 507, 377, 678 ), new CharMetrics( 333, 0, 64, -165, 249, 0 ),
+        new CharMetrics( 333, 0, 11, 507, 322, 674 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 97, -218, 205, 467 ), new CharMetrics( 500, 0, 53, -138, 448, 579 ),
+        new CharMetrics( 500, 0, 12, -8, 490, 676 ), new CharMetrics( 500, 0, -22, 58, 522, 602 ),
+        new CharMetrics( 500, 0, -53, 0, 512, 662 ), new CharMetrics( 200, 0, 67, -14, 133, 676 ),
+        new CharMetrics( 500, 0, 70, -148, 426, 676 ), new CharMetrics( 333, 0, 18, 523, 315, 623 ),
+        new CharMetrics( 760, 0, 38, -14, 722, 676 ), new CharMetrics( 276, 0, 4, 394, 270, 676 ),
+        new CharMetrics( 500, 0, 42, 33, 456, 416 ), new CharMetrics( 564, 0, 30, 108, 534, 386 ),
+        new CharMetrics( 333, 0, 39, 194, 285, 257 ), new CharMetrics( 760, 0, 38, -14, 722, 676 ),
+        new CharMetrics( 333, 0, 11, 547, 322, 601 ), new CharMetrics( 400, 0, 57, 390, 343, 676 ),
+        new CharMetrics( 564, 0, 30, 0, 534, 506 ), new CharMetrics( 300, 0, 1, 270, 296, 676 ),
+        new CharMetrics( 300, 0, 15, 262, 291, 676 ), new CharMetrics( 333, 0, 93, 507, 317, 678 ),
+        new CharMetrics( 500, 0, 36, -218, 512, 450 ), new CharMetrics( 453, 0, -22, -154, 450, 662 ),
+        new CharMetrics( 250, 0, 70, 199, 181, 310 ), new CharMetrics( 333, 0, 52, -215, 261, 0 ),
+        new CharMetrics( 300, 0, 57, 270, 248, 676 ), new CharMetrics( 310, 0, 6, 394, 304, 676 ),
+        new CharMetrics( 500, 0, 44, 33, 458, 416 ), new CharMetrics( 750, 0, 37, -14, 718, 676 ),
+        new CharMetrics( 750, 0, 31, -14, 746, 676 ), new CharMetrics( 750, 0, 15, -14, 718, 676 ),
+        new CharMetrics( 444, 0, 30, -218, 376, 466 ), new CharMetrics( 722, 0, 15, 0, 706, 890 ),
+        new CharMetrics( 722, 0, 15, 0, 706, 890 ), new CharMetrics( 722, 0, 15, 0, 706, 886 ),
+        new CharMetrics( 722, 0, 15, 0, 706, 850 ), new CharMetrics( 722, 0, 15, 0, 706, 835 ),
+        new CharMetrics( 722, 0, 15, 0, 706, 898 ), new CharMetrics( 889, 0, 0, 0, 863, 662 ),
+        new CharMetrics( 667, 0, 28, -215, 633, 676 ), new CharMetrics( 611, 0, 12, 0, 597, 890 ),
+        new CharMetrics( 611, 0, 12, 0, 597, 890 ), new CharMetrics( 611, 0, 12, 0, 597, 886 ),
+        new CharMetrics( 611, 0, 12, 0, 597, 835 ), new CharMetrics( 333, 0, 18, 0, 315, 890 ),
+        new CharMetrics( 333, 0, 18, 0, 317, 890 ), new CharMetrics( 333, 0, 11, 0, 322, 886 ),
+        new CharMetrics( 333, 0, 18, 0, 315, 835 ), new CharMetrics( 722, 0, 16, 0, 685, 662 ),
+        new CharMetrics( 722, 0, 12, -11, 707, 850 ), new CharMetrics( 722, 0, 34, -14, 688, 890 ),
+        new CharMetrics( 722, 0, 34, -14, 688, 890 ), new CharMetrics( 722, 0, 34, -14, 688, 886 ),
+        new CharMetrics( 722, 0, 34, -14, 688, 850 ), new CharMetrics( 722, 0, 34, -14, 688, 835 ),
+        new CharMetrics( 564, 0, 38, 8, 527, 497 ), new CharMetrics( 722, 0, 34, -80, 688, 734 ),
+        new CharMetrics( 722, 0, 14, -14, 705, 890 ), new CharMetrics( 722, 0, 14, -14, 705, 890 ),
+        new CharMetrics( 722, 0, 14, -14, 705, 886 ), new CharMetrics( 722, 0, 14, -14, 705, 835 ),
+        new CharMetrics( 722, 0, 22, 0, 703, 890 ), new CharMetrics( 556, 0, 16, 0, 542, 662 ),
+        new CharMetrics( 500, 0, 12, -9, 468, 683 ), new CharMetrics( 444, 0, 37, -10, 442, 678 ),
+        new CharMetrics( 444, 0, 37, -10, 442, 678 ), new CharMetrics( 444, 0, 37, -10, 442, 674 ),
+        new CharMetrics( 444, 0, 37, -10, 442, 638 ), new CharMetrics( 444, 0, 37, -10, 442, 623 ),
+        new CharMetrics( 444, 0, 37, -10, 442, 711 ), new CharMetrics( 667, 0, 38, -10, 632, 460 ),
+        new CharMetrics( 444, 0, 25, -215, 412, 460 ), new CharMetrics( 444, 0, 25, -10, 424, 678 ),
+        new CharMetrics( 444, 0, 25, -10, 424, 678 ), new CharMetrics( 444, 0, 25, -10, 424, 674 ),
+        new CharMetrics( 444, 0, 25, -10, 424, 623 ), new CharMetrics( 278, 0, -8, 0, 253, 678 ),
+        new CharMetrics( 278, 0, 16, 0, 290, 678 ), new CharMetrics( 278, 0, -16, 0, 295, 674 ),
+        new CharMetrics( 278, 0, -9, 0, 288, 623 ), new CharMetrics( 500, 0, 29, -10, 471, 686 ),
+        new CharMetrics( 500, 0, 16, 0, 485, 638 ), new CharMetrics( 500, 0, 29, -10, 470, 678 ),
+        new CharMetrics( 500, 0, 29, -10, 470, 678 ), new CharMetrics( 500, 0, 29, -10, 470, 674 ),
+        new CharMetrics( 500, 0, 29, -10, 470, 638 ), new CharMetrics( 500, 0, 29, -10, 470, 623 ),
+        new CharMetrics( 564, 0, 30, -10, 534, 516 ), new CharMetrics( 500, 0, 29, -112, 470, 551 ),
+        new CharMetrics( 500, 0, 9, -10, 479, 678 ), new CharMetrics( 500, 0, 9, -10, 479, 678 ),
+        new CharMetrics( 500, 0, 9, -10, 479, 674 ), new CharMetrics( 500, 0, 9, -10, 479, 623 ),
+        new CharMetrics( 500, 0, 14, -218, 475, 678 ), new CharMetrics( 500, 0, 5, -217, 470, 683 ),
+        new CharMetrics( 500, 0, 14, -218, 475, 623 )};
+
+    Serif()
+    {
+        super( false, 683, -217, new CharMetrics( 0, 0, -168, -218, 1000, 898 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifBold.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifBold.java
new file mode 100644
index 0000000..c7782e7
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifBold.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: SerifBold.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class SerifBold
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 81, -13, 251, 691 ), new CharMetrics( 555, 0, 83, 404, 472, 691 ),
+        new CharMetrics( 500, 0, 4, 0, 496, 700 ), new CharMetrics( 500, 0, 29, -99, 472, 750 ),
+        new CharMetrics( 1000, 0, 124, -14, 877, 692 ), new CharMetrics( 833, 0, 62, -16, 787, 691 ),
+        new CharMetrics( 333, 0, 79, 356, 263, 691 ), new CharMetrics( 333, 0, 46, -168, 306, 694 ),
+        new CharMetrics( 333, 0, 27, -168, 287, 694 ), new CharMetrics( 500, 0, 56, 255, 447, 691 ),
+        new CharMetrics( 570, 0, 33, 0, 537, 506 ), new CharMetrics( 250, 0, 39, -180, 223, 155 ),
+        new CharMetrics( 333, 0, 44, 171, 287, 287 ), new CharMetrics( 250, 0, 41, -13, 210, 156 ),
+        new CharMetrics( 278, 0, -24, -19, 302, 691 ), new CharMetrics( 500, 0, 24, -13, 476, 688 ),
+        new CharMetrics( 500, 0, 65, 0, 442, 688 ), new CharMetrics( 500, 0, 17, 0, 478, 688 ),
+        new CharMetrics( 500, 0, 16, -14, 468, 688 ), new CharMetrics( 500, 0, 19, 0, 475, 688 ),
+        new CharMetrics( 500, 0, 22, -8, 470, 676 ), new CharMetrics( 500, 0, 28, -13, 475, 688 ),
+        new CharMetrics( 500, 0, 17, 0, 477, 676 ), new CharMetrics( 500, 0, 28, -13, 472, 688 ),
+        new CharMetrics( 500, 0, 26, -13, 473, 688 ), new CharMetrics( 333, 0, 82, -13, 251, 472 ),
+        new CharMetrics( 333, 0, 82, -180, 266, 472 ), new CharMetrics( 570, 0, 31, -8, 539, 514 ),
+        new CharMetrics( 570, 0, 33, 107, 537, 399 ), new CharMetrics( 570, 0, 31, -8, 539, 514 ),
+        new CharMetrics( 500, 0, 57, -13, 445, 689 ), new CharMetrics( 930, 0, 108, -19, 822, 691 ),
+        new CharMetrics( 722, 0, 9, 0, 689, 690 ), new CharMetrics( 667, 0, 16, 0, 619, 676 ),
+        new CharMetrics( 722, 0, 49, -19, 687, 691 ), new CharMetrics( 722, 0, 14, 0, 690, 676 ),
+        new CharMetrics( 667, 0, 16, 0, 641, 676 ), new CharMetrics( 611, 0, 16, 0, 583, 676 ),
+        new CharMetrics( 778, 0, 37, -19, 755, 691 ), new CharMetrics( 778, 0, 21, 0, 759, 676 ),
+        new CharMetrics( 389, 0, 20, 0, 370, 676 ), new CharMetrics( 500, 0, 3, -96, 479, 676 ),
+        new CharMetrics( 778, 0, 30, 0, 769, 676 ), new CharMetrics( 667, 0, 19, 0, 638, 676 ),
+        new CharMetrics( 944, 0, 14, 0, 921, 676 ), new CharMetrics( 722, 0, 16, -18, 701, 676 ),
+        new CharMetrics( 778, 0, 35, -19, 743, 691 ), new CharMetrics( 611, 0, 16, 0, 600, 676 ),
+        new CharMetrics( 778, 0, 35, -176, 743, 691 ), new CharMetrics( 722, 0, 26, 0, 715, 676 ),
+        new CharMetrics( 556, 0, 35, -19, 513, 692 ), new CharMetrics( 667, 0, 31, 0, 636, 676 ),
+        new CharMetrics( 722, 0, 16, -19, 701, 676 ), new CharMetrics( 722, 0, 16, -18, 701, 676 ),
+        new CharMetrics( 1000, 0, 19, -15, 981, 676 ), new CharMetrics( 722, 0, 16, 0, 699, 676 ),
+        new CharMetrics( 722, 0, 15, 0, 699, 676 ), new CharMetrics( 667, 0, 28, 0, 634, 676 ),
+        new CharMetrics( 333, 0, 67, -149, 301, 678 ), new CharMetrics( 278, 0, -25, -19, 303, 691 ),
+        new CharMetrics( 333, 0, 32, -149, 266, 678 ), new CharMetrics( 581, 0, 73, 311, 509, 676 ),
+        new CharMetrics( 500, 0, 0, -125, 500, -75 ), new CharMetrics( 333, 0, 70, 356, 254, 691 ),
+        new CharMetrics( 500, 0, 25, -14, 488, 473 ), new CharMetrics( 556, 0, 17, -14, 521, 676 ),
+        new CharMetrics( 444, 0, 25, -14, 430, 473 ), new CharMetrics( 556, 0, 25, -14, 534, 676 ),
+        new CharMetrics( 444, 0, 25, -14, 426, 473 ), new CharMetrics( 333, 0, 14, 0, 389, 691 ),
+        new CharMetrics( 500, 0, 28, -206, 483, 473 ), new CharMetrics( 556, 0, 16, 0, 534, 676 ),
+        new CharMetrics( 278, 0, 16, 0, 255, 691 ), new CharMetrics( 333, 0, -57, -203, 263, 691 ),
+        new CharMetrics( 556, 0, 22, 0, 543, 676 ), new CharMetrics( 278, 0, 16, 0, 255, 676 ),
+        new CharMetrics( 833, 0, 16, 0, 814, 473 ), new CharMetrics( 556, 0, 21, 0, 539, 473 ),
+        new CharMetrics( 500, 0, 25, -14, 476, 473 ), new CharMetrics( 556, 0, 19, -205, 524, 473 ),
+        new CharMetrics( 556, 0, 34, -205, 536, 473 ), new CharMetrics( 444, 0, 29, 0, 434, 473 ),
+        new CharMetrics( 389, 0, 25, -14, 361, 473 ), new CharMetrics( 333, 0, 20, -12, 332, 630 ),
+        new CharMetrics( 556, 0, 16, -14, 537, 461 ), new CharMetrics( 500, 0, 21, -14, 485, 461 ),
+        new CharMetrics( 722, 0, 23, -14, 707, 461 ), new CharMetrics( 500, 0, 12, 0, 484, 461 ),
+        new CharMetrics( 500, 0, 16, -205, 480, 461 ), new CharMetrics( 444, 0, 21, 0, 420, 461 ),
+        new CharMetrics( 394, 0, 22, -175, 340, 698 ), new CharMetrics( 220, 0, 66, -19, 154, 691 ),
+        new CharMetrics( 394, 0, 54, -175, 372, 698 ), new CharMetrics( 520, 0, 29, 173, 491, 333 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 16, 0, 255, 461 ),
+        new CharMetrics( 333, 0, 8, 528, 246, 713 ), new CharMetrics( 333, 0, 86, 528, 324, 713 ),
+        new CharMetrics( 333, 0, -2, 528, 335, 704 ), new CharMetrics( 333, 0, -16, 547, 349, 674 ),
+        new CharMetrics( 333, 0, 1, 565, 331, 637 ), new CharMetrics( 333, 0, 15, 528, 318, 691 ),
+        new CharMetrics( 333, 0, 103, 537, 230, 667 ), new CharMetrics( 333, 0, -2, 537, 335, 667 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 333, 0, 60, 527, 273, 740 ),
+        new CharMetrics( 333, 0, 68, -218, 294, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, -13, 528, 425, 713 ), new CharMetrics( 333, 0, 90, -173, 319, 44 ),
+        new CharMetrics( 333, 0, -2, 528, 335, 704 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 82, -203, 252, 501 ), new CharMetrics( 500, 0, 53, -140, 458, 588 ),
+        new CharMetrics( 500, 0, 21, -14, 477, 684 ), new CharMetrics( 500, 0, -26, 61, 526, 613 ),
+        new CharMetrics( 500, 0, -64, 0, 547, 676 ), new CharMetrics( 220, 0, 66, -19, 154, 691 ),
+        new CharMetrics( 500, 0, 57, -132, 443, 691 ), new CharMetrics( 333, 0, -2, 537, 335, 667 ),
+        new CharMetrics( 747, 0, 26, -19, 721, 691 ), new CharMetrics( 300, 0, -1, 397, 301, 688 ),
+        new CharMetrics( 500, 0, 23, 36, 473, 415 ), new CharMetrics( 570, 0, 33, 108, 537, 399 ),
+        new CharMetrics( 333, 0, 44, 171, 287, 287 ), new CharMetrics( 747, 0, 26, -19, 721, 691 ),
+        new CharMetrics( 333, 0, 1, 565, 331, 637 ), new CharMetrics( 400, 0, 57, 402, 343, 688 ),
+        new CharMetrics( 570, 0, 33, 0, 537, 506 ), new CharMetrics( 300, 0, 0, 275, 300, 688 ),
+        new CharMetrics( 300, 0, 3, 268, 297, 688 ), new CharMetrics( 333, 0, 86, 528, 324, 713 ),
+        new CharMetrics( 556, 0, 33, -206, 536, 461 ), new CharMetrics( 540, 0, 0, -186, 519, 676 ),
+        new CharMetrics( 250, 0, 41, 248, 210, 417 ), new CharMetrics( 333, 0, 68, -218, 294, 0 ),
+        new CharMetrics( 300, 0, 28, 275, 273, 688 ), new CharMetrics( 330, 0, 18, 397, 312, 688 ),
+        new CharMetrics( 500, 0, 27, 36, 477, 415 ), new CharMetrics( 750, 0, 28, -12, 743, 688 ),
+        new CharMetrics( 750, 0, -7, -12, 775, 688 ), new CharMetrics( 750, 0, 23, -12, 733, 688 ),
+        new CharMetrics( 500, 0, 55, -201, 443, 501 ), new CharMetrics( 722, 0, 9, 0, 689, 923 ),
+        new CharMetrics( 722, 0, 9, 0, 689, 923 ), new CharMetrics( 722, 0, 9, 0, 689, 914 ),
+        new CharMetrics( 722, 0, 9, 0, 689, 884 ), new CharMetrics( 722, 0, 9, 0, 689, 877 ),
+        new CharMetrics( 722, 0, 9, 0, 689, 935 ), new CharMetrics( 1000, 0, 4, 0, 951, 676 ),
+        new CharMetrics( 722, 0, 49, -218, 687, 691 ), new CharMetrics( 667, 0, 16, 0, 641, 923 ),
+        new CharMetrics( 667, 0, 16, 0, 641, 923 ), new CharMetrics( 667, 0, 16, 0, 641, 914 ),
+        new CharMetrics( 667, 0, 16, 0, 641, 877 ), new CharMetrics( 389, 0, 20, 0, 370, 923 ),
+        new CharMetrics( 389, 0, 20, 0, 370, 923 ), new CharMetrics( 389, 0, 20, 0, 370, 914 ),
+        new CharMetrics( 389, 0, 20, 0, 370, 877 ), new CharMetrics( 722, 0, 6, 0, 690, 676 ),
+        new CharMetrics( 722, 0, 16, -18, 701, 884 ), new CharMetrics( 778, 0, 35, -19, 743, 923 ),
+        new CharMetrics( 778, 0, 35, -19, 743, 923 ), new CharMetrics( 778, 0, 35, -19, 743, 914 ),
+        new CharMetrics( 778, 0, 35, -19, 743, 884 ), new CharMetrics( 778, 0, 35, -19, 743, 877 ),
+        new CharMetrics( 570, 0, 48, 16, 522, 490 ), new CharMetrics( 778, 0, 35, -74, 743, 737 ),
+        new CharMetrics( 722, 0, 16, -19, 701, 923 ), new CharMetrics( 722, 0, 16, -19, 701, 923 ),
+        new CharMetrics( 722, 0, 16, -19, 701, 914 ), new CharMetrics( 722, 0, 16, -19, 701, 877 ),
+        new CharMetrics( 722, 0, 15, 0, 699, 928 ), new CharMetrics( 611, 0, 16, 0, 600, 676 ),
+        new CharMetrics( 556, 0, 19, -12, 517, 691 ), new CharMetrics( 500, 0, 25, -14, 488, 713 ),
+        new CharMetrics( 500, 0, 25, -14, 488, 713 ), new CharMetrics( 500, 0, 25, -14, 488, 704 ),
+        new CharMetrics( 500, 0, 25, -14, 488, 674 ), new CharMetrics( 500, 0, 25, -14, 488, 667 ),
+        new CharMetrics( 500, 0, 25, -14, 488, 740 ), new CharMetrics( 722, 0, 33, -14, 693, 473 ),
+        new CharMetrics( 444, 0, 25, -218, 430, 473 ), new CharMetrics( 444, 0, 25, -14, 426, 713 ),
+        new CharMetrics( 444, 0, 25, -14, 426, 713 ), new CharMetrics( 444, 0, 25, -14, 426, 704 ),
+        new CharMetrics( 444, 0, 25, -14, 426, 667 ), new CharMetrics( 278, 0, -26, 0, 255, 713 ),
+        new CharMetrics( 278, 0, 16, 0, 290, 713 ), new CharMetrics( 278, 0, -36, 0, 301, 704 ),
+        new CharMetrics( 278, 0, -36, 0, 301, 667 ), new CharMetrics( 500, 0, 25, -14, 476, 691 ),
+        new CharMetrics( 556, 0, 21, 0, 539, 674 ), new CharMetrics( 500, 0, 25, -14, 476, 713 ),
+        new CharMetrics( 500, 0, 25, -14, 476, 713 ), new CharMetrics( 500, 0, 25, -14, 476, 704 ),
+        new CharMetrics( 500, 0, 25, -14, 476, 674 ), new CharMetrics( 500, 0, 25, -14, 476, 667 ),
+        new CharMetrics( 570, 0, 33, -31, 537, 537 ), new CharMetrics( 500, 0, 25, -92, 476, 549 ),
+        new CharMetrics( 556, 0, 16, -14, 537, 713 ), new CharMetrics( 556, 0, 16, -14, 537, 713 ),
+        new CharMetrics( 556, 0, 16, -14, 537, 704 ), new CharMetrics( 556, 0, 16, -14, 537, 667 ),
+        new CharMetrics( 500, 0, 16, -205, 480, 713 ), new CharMetrics( 556, 0, 19, -205, 524, 676 ),
+        new CharMetrics( 500, 0, 16, -205, 480, 667 )};
+
+    SerifBold()
+    {
+        super( false, 676, -205, new CharMetrics( 0, 0, -168, -218, 1000, 935 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifBoldItalic.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifBoldItalic.java
new file mode 100644
index 0000000..50122ea
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifBoldItalic.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: SerifBoldItalic.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class SerifBoldItalic
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 389, 0, 67, -13, 370, 684 ), new CharMetrics( 555, 0, 136, 398, 536, 685 ),
+        new CharMetrics( 500, 0, -33, 0, 533, 700 ), new CharMetrics( 500, 0, -20, -100, 497, 733 ),
+        new CharMetrics( 833, 0, 39, -10, 793, 692 ), new CharMetrics( 778, 0, 5, -19, 699, 682 ),
+        new CharMetrics( 333, 0, 98, 369, 302, 685 ), new CharMetrics( 333, 0, 28, -179, 344, 685 ),
+        new CharMetrics( 333, 0, -44, -179, 271, 685 ), new CharMetrics( 500, 0, 65, 249, 456, 685 ),
+        new CharMetrics( 570, 0, 33, 0, 537, 506 ), new CharMetrics( 250, 0, -60, -182, 144, 134 ),
+        new CharMetrics( 333, 0, 2, 166, 271, 282 ), new CharMetrics( 250, 0, -9, -13, 139, 135 ),
+        new CharMetrics( 278, 0, -64, -18, 342, 685 ), new CharMetrics( 500, 0, 17, -14, 477, 683 ),
+        new CharMetrics( 500, 0, 5, 0, 419, 683 ), new CharMetrics( 500, 0, -27, 0, 446, 683 ),
+        new CharMetrics( 500, 0, -15, -13, 450, 683 ), new CharMetrics( 500, 0, -15, 0, 503, 683 ),
+        new CharMetrics( 500, 0, -11, -13, 487, 669 ), new CharMetrics( 500, 0, 23, -15, 509, 679 ),
+        new CharMetrics( 500, 0, 52, 0, 525, 669 ), new CharMetrics( 500, 0, 3, -13, 476, 683 ),
+        new CharMetrics( 500, 0, -12, -10, 475, 683 ), new CharMetrics( 333, 0, 23, -13, 264, 459 ),
+        new CharMetrics( 333, 0, -25, -183, 264, 459 ), new CharMetrics( 570, 0, 31, -8, 539, 514 ),
+        new CharMetrics( 570, 0, 33, 107, 537, 399 ), new CharMetrics( 570, 0, 31, -8, 539, 514 ),
+        new CharMetrics( 500, 0, 79, -13, 470, 684 ), new CharMetrics( 832, 0, 63, -18, 770, 685 ),
+        new CharMetrics( 667, 0, -67, 0, 593, 683 ), new CharMetrics( 667, 0, -24, 0, 624, 669 ),
+        new CharMetrics( 667, 0, 32, -18, 677, 685 ), new CharMetrics( 722, 0, -46, 0, 685, 669 ),
+        new CharMetrics( 667, 0, -27, 0, 653, 669 ), new CharMetrics( 667, 0, -13, 0, 660, 669 ),
+        new CharMetrics( 722, 0, 21, -18, 706, 685 ), new CharMetrics( 778, 0, -24, 0, 799, 669 ),
+        new CharMetrics( 389, 0, -32, 0, 406, 669 ), new CharMetrics( 500, 0, -46, -99, 524, 669 ),
+        new CharMetrics( 667, 0, -21, 0, 702, 669 ), new CharMetrics( 611, 0, -22, 0, 590, 669 ),
+        new CharMetrics( 889, 0, -29, -12, 917, 669 ), new CharMetrics( 722, 0, -27, -15, 748, 669 ),
+        new CharMetrics( 722, 0, 27, -18, 691, 685 ), new CharMetrics( 611, 0, -27, 0, 613, 669 ),
+        new CharMetrics( 722, 0, 27, -208, 691, 685 ), new CharMetrics( 667, 0, -29, 0, 623, 669 ),
+        new CharMetrics( 556, 0, 2, -18, 526, 685 ), new CharMetrics( 611, 0, 50, 0, 650, 669 ),
+        new CharMetrics( 722, 0, 67, -18, 744, 669 ), new CharMetrics( 667, 0, 65, -18, 715, 669 ),
+        new CharMetrics( 889, 0, 65, -18, 940, 669 ), new CharMetrics( 667, 0, -24, 0, 694, 669 ),
+        new CharMetrics( 611, 0, 73, 0, 659, 669 ), new CharMetrics( 611, 0, -11, 0, 590, 669 ),
+        new CharMetrics( 333, 0, -37, -159, 362, 674 ), new CharMetrics( 278, 0, -1, -18, 279, 685 ),
+        new CharMetrics( 333, 0, -56, -157, 343, 674 ), new CharMetrics( 570, 0, 67, 304, 503, 669 ),
+        new CharMetrics( 500, 0, 0, -125, 500, -75 ), new CharMetrics( 333, 0, 128, 369, 332, 685 ),
+        new CharMetrics( 500, 0, -21, -14, 455, 462 ), new CharMetrics( 500, 0, -14, -13, 444, 699 ),
+        new CharMetrics( 444, 0, -5, -13, 392, 462 ), new CharMetrics( 500, 0, -21, -13, 517, 699 ),
+        new CharMetrics( 444, 0, 5, -13, 398, 462 ), new CharMetrics( 333, 0, -169, -205, 446, 698 ),
+        new CharMetrics( 500, 0, -52, -203, 478, 462 ), new CharMetrics( 556, 0, -13, -9, 498, 699 ),
+        new CharMetrics( 278, 0, 2, -9, 263, 684 ), new CharMetrics( 278, 0, -189, -207, 279, 684 ),
+        new CharMetrics( 500, 0, -23, -8, 483, 699 ), new CharMetrics( 278, 0, 2, -9, 290, 699 ),
+        new CharMetrics( 778, 0, -14, -9, 722, 462 ), new CharMetrics( 556, 0, -6, -9, 493, 462 ),
+        new CharMetrics( 500, 0, -3, -13, 441, 462 ), new CharMetrics( 500, 0, -120, -205, 446, 462 ),
+        new CharMetrics( 500, 0, 1, -205, 471, 462 ), new CharMetrics( 389, 0, -21, 0, 389, 462 ),
+        new CharMetrics( 389, 0, -19, -13, 333, 462 ), new CharMetrics( 278, 0, -11, -9, 281, 594 ),
+        new CharMetrics( 556, 0, 15, -9, 492, 462 ), new CharMetrics( 444, 0, 16, -13, 401, 462 ),
+        new CharMetrics( 667, 0, 16, -13, 614, 462 ), new CharMetrics( 500, 0, -46, -13, 469, 462 ),
+        new CharMetrics( 444, 0, -94, -205, 392, 462 ), new CharMetrics( 389, 0, -43, -78, 368, 449 ),
+        new CharMetrics( 348, 0, 5, -187, 436, 686 ), new CharMetrics( 220, 0, 66, -18, 154, 685 ),
+        new CharMetrics( 348, 0, -129, -187, 302, 686 ), new CharMetrics( 570, 0, 54, 173, 516, 333 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 2, -9, 238, 462 ),
+        new CharMetrics( 333, 0, 85, 516, 297, 697 ), new CharMetrics( 333, 0, 139, 516, 379, 697 ),
+        new CharMetrics( 333, 0, 40, 516, 367, 690 ), new CharMetrics( 333, 0, 48, 536, 407, 655 ),
+        new CharMetrics( 333, 0, 51, 553, 393, 623 ), new CharMetrics( 333, 0, 71, 516, 387, 678 ),
+        new CharMetrics( 333, 0, 163, 525, 293, 655 ), new CharMetrics( 333, 0, 55, 525, 397, 655 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 333, 0, 127, 516, 340, 729 ),
+        new CharMetrics( 333, 0, -80, -218, 156, 5 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 69, 516, 498, 697 ), new CharMetrics( 333, 0, -40, -173, 189, 44 ),
+        new CharMetrics( 333, 0, 79, 516, 411, 690 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 389, 0, 19, -205, 322, 492 ), new CharMetrics( 500, 0, 42, -143, 439, 576 ),
+        new CharMetrics( 500, 0, -32, -12, 510, 683 ), new CharMetrics( 500, 0, -26, 34, 526, 586 ),
+        new CharMetrics( 500, 0, 33, 0, 628, 669 ), new CharMetrics( 220, 0, 66, -18, 154, 685 ),
+        new CharMetrics( 500, 0, 36, -143, 459, 685 ), new CharMetrics( 333, 0, 55, 525, 397, 655 ),
+        new CharMetrics( 747, 0, 30, -18, 718, 685 ), new CharMetrics( 266, 0, 16, 399, 330, 685 ),
+        new CharMetrics( 500, 0, 12, 32, 468, 415 ), new CharMetrics( 606, 0, 51, 108, 555, 399 ),
+        new CharMetrics( 333, 0, 2, 166, 271, 282 ), new CharMetrics( 747, 0, 30, -18, 718, 685 ),
+        new CharMetrics( 333, 0, 51, 553, 393, 623 ), new CharMetrics( 400, 0, 83, 397, 369, 683 ),
+        new CharMetrics( 570, 0, 33, 0, 537, 506 ), new CharMetrics( 300, 0, 2, 274, 313, 683 ),
+        new CharMetrics( 300, 0, 17, 265, 321, 683 ), new CharMetrics( 333, 0, 139, 516, 379, 697 ),
+        new CharMetrics( 576, 0, -60, -207, 516, 449 ), new CharMetrics( 500, 0, -57, -193, 562, 669 ),
+        new CharMetrics( 250, 0, 51, 257, 199, 405 ), new CharMetrics( 333, 0, -80, -218, 156, 5 ),
+        new CharMetrics( 300, 0, 30, 274, 301, 683 ), new CharMetrics( 300, 0, 56, 400, 347, 685 ),
+        new CharMetrics( 500, 0, 12, 32, 468, 415 ), new CharMetrics( 750, 0, 7, -14, 721, 683 ),
+        new CharMetrics( 750, 0, -9, -14, 723, 683 ), new CharMetrics( 750, 0, 7, -14, 726, 683 ),
+        new CharMetrics( 500, 0, 30, -205, 421, 492 ), new CharMetrics( 667, 0, -67, 0, 593, 904 ),
+        new CharMetrics( 667, 0, -67, 0, 593, 904 ), new CharMetrics( 667, 0, -67, 0, 593, 897 ),
+        new CharMetrics( 667, 0, -67, 0, 593, 862 ), new CharMetrics( 667, 0, -67, 0, 593, 862 ),
+        new CharMetrics( 667, 0, -67, 0, 593, 921 ), new CharMetrics( 944, 0, -64, 0, 918, 669 ),
+        new CharMetrics( 667, 0, 32, -218, 677, 685 ), new CharMetrics( 667, 0, -27, 0, 653, 904 ),
+        new CharMetrics( 667, 0, -27, 0, 653, 904 ), new CharMetrics( 667, 0, -27, 0, 653, 897 ),
+        new CharMetrics( 667, 0, -27, 0, 653, 862 ), new CharMetrics( 389, 0, -32, 0, 406, 904 ),
+        new CharMetrics( 389, 0, -32, 0, 412, 904 ), new CharMetrics( 389, 0, -32, 0, 420, 897 ),
+        new CharMetrics( 389, 0, -32, 0, 445, 862 ), new CharMetrics( 722, 0, -31, 0, 700, 669 ),
+        new CharMetrics( 722, 0, -27, -15, 748, 862 ), new CharMetrics( 722, 0, 27, -18, 691, 904 ),
+        new CharMetrics( 722, 0, 27, -18, 691, 904 ), new CharMetrics( 722, 0, 27, -18, 691, 897 ),
+        new CharMetrics( 722, 0, 27, -18, 691, 862 ), new CharMetrics( 722, 0, 27, -18, 691, 862 ),
+        new CharMetrics( 570, 0, 48, 16, 522, 490 ), new CharMetrics( 722, 0, 27, -125, 691, 764 ),
+        new CharMetrics( 722, 0, 67, -18, 744, 904 ), new CharMetrics( 722, 0, 67, -18, 744, 904 ),
+        new CharMetrics( 722, 0, 67, -18, 744, 897 ), new CharMetrics( 722, 0, 67, -18, 744, 862 ),
+        new CharMetrics( 611, 0, 73, 0, 659, 904 ), new CharMetrics( 611, 0, -27, 0, 573, 669 ),
+        new CharMetrics( 500, 0, -200, -200, 473, 705 ), new CharMetrics( 500, 0, -21, -14, 455, 697 ),
+        new CharMetrics( 500, 0, -21, -14, 463, 697 ), new CharMetrics( 500, 0, -21, -14, 455, 690 ),
+        new CharMetrics( 500, 0, -21, -14, 491, 655 ), new CharMetrics( 500, 0, -21, -14, 471, 655 ),
+        new CharMetrics( 500, 0, -21, -14, 455, 729 ), new CharMetrics( 722, 0, -5, -13, 673, 462 ),
+        new CharMetrics( 444, 0, -24, -218, 392, 462 ), new CharMetrics( 444, 0, 5, -13, 398, 697 ),
+        new CharMetrics( 444, 0, 5, -13, 435, 697 ), new CharMetrics( 444, 0, 5, -13, 423, 690 ),
+        new CharMetrics( 444, 0, 5, -13, 443, 655 ), new CharMetrics( 278, 0, 2, -9, 260, 697 ),
+        new CharMetrics( 278, 0, 2, -9, 352, 697 ), new CharMetrics( 278, 0, -2, -9, 325, 690 ),
+        new CharMetrics( 278, 0, 2, -9, 360, 655 ), new CharMetrics( 500, 0, -3, -13, 454, 699 ),
+        new CharMetrics( 556, 0, -6, -9, 504, 655 ), new CharMetrics( 500, 0, -3, -13, 441, 697 ),
+        new CharMetrics( 500, 0, -3, -13, 463, 697 ), new CharMetrics( 500, 0, -3, -13, 451, 690 ),
+        new CharMetrics( 500, 0, -3, -13, 491, 655 ), new CharMetrics( 500, 0, -3, -13, 466, 655 ),
+        new CharMetrics( 570, 0, 33, -29, 537, 535 ), new CharMetrics( 500, 0, -3, -119, 441, 560 ),
+        new CharMetrics( 556, 0, 15, -9, 492, 697 ), new CharMetrics( 556, 0, 15, -9, 492, 697 ),
+        new CharMetrics( 556, 0, 15, -9, 492, 690 ), new CharMetrics( 556, 0, 15, -9, 494, 655 ),
+        new CharMetrics( 444, 0, -94, -205, 435, 697 ), new CharMetrics( 500, 0, -120, -205, 446, 699 ),
+        new CharMetrics( 444, 0, -94, -205, 438, 655 )};
+
+    SerifBoldItalic()
+    {
+        super( false, 699, -205, new CharMetrics( 0, 0, -200, -218, 996, 921 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifItalic.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifItalic.java
new file mode 100644
index 0000000..7b661c5
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/SerifItalic.java
@@ -0,0 +1,162 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @version $Id: SerifItalic.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class SerifItalic
+    extends FontMetrics
+{
+    static final CharMetrics[] metrics = {new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 39, -11, 302, 667 ), new CharMetrics( 420, 0, 144, 421, 432, 666 ),
+        new CharMetrics( 500, 0, 2, 0, 540, 676 ), new CharMetrics( 500, 0, 31, -89, 497, 731 ),
+        new CharMetrics( 833, 0, 79, -13, 790, 676 ), new CharMetrics( 778, 0, 76, -18, 723, 666 ),
+        new CharMetrics( 333, 0, 151, 436, 290, 666 ), new CharMetrics( 333, 0, 42, -181, 315, 669 ),
+        new CharMetrics( 333, 0, 16, -180, 289, 669 ), new CharMetrics( 500, 0, 128, 255, 492, 666 ),
+        new CharMetrics( 675, 0, 86, 0, 590, 506 ), new CharMetrics( 250, 0, -4, -129, 135, 101 ),
+        new CharMetrics( 333, 0, 49, 192, 282, 255 ), new CharMetrics( 250, 0, 27, -11, 138, 100 ),
+        new CharMetrics( 278, 0, -65, -18, 386, 666 ), new CharMetrics( 500, 0, 32, -7, 497, 676 ),
+        new CharMetrics( 500, 0, 49, 0, 409, 676 ), new CharMetrics( 500, 0, 12, 0, 452, 676 ),
+        new CharMetrics( 500, 0, 15, -7, 465, 676 ), new CharMetrics( 500, 0, 1, 0, 479, 676 ),
+        new CharMetrics( 500, 0, 15, -7, 491, 666 ), new CharMetrics( 500, 0, 30, -7, 521, 686 ),
+        new CharMetrics( 500, 0, 75, -8, 537, 666 ), new CharMetrics( 500, 0, 30, -7, 493, 676 ),
+        new CharMetrics( 500, 0, 23, -17, 492, 676 ), new CharMetrics( 333, 0, 50, -11, 261, 441 ),
+        new CharMetrics( 333, 0, 27, -129, 261, 441 ), new CharMetrics( 675, 0, 84, -8, 592, 514 ),
+        new CharMetrics( 675, 0, 86, 120, 590, 386 ), new CharMetrics( 675, 0, 84, -8, 592, 514 ),
+        new CharMetrics( 500, 0, 132, -12, 472, 664 ), new CharMetrics( 920, 0, 118, -18, 806, 666 ),
+        new CharMetrics( 611, 0, -51, 0, 564, 668 ), new CharMetrics( 611, 0, -8, 0, 588, 653 ),
+        new CharMetrics( 667, 0, 66, -18, 689, 666 ), new CharMetrics( 722, 0, -8, 0, 700, 653 ),
+        new CharMetrics( 611, 0, -1, 0, 634, 653 ), new CharMetrics( 611, 0, 8, 0, 645, 653 ),
+        new CharMetrics( 722, 0, 52, -18, 722, 666 ), new CharMetrics( 722, 0, -8, 0, 767, 653 ),
+        new CharMetrics( 333, 0, -8, 0, 384, 653 ), new CharMetrics( 444, 0, -6, -18, 491, 653 ),
+        new CharMetrics( 667, 0, 7, 0, 722, 653 ), new CharMetrics( 556, 0, -8, 0, 559, 653 ),
+        new CharMetrics( 833, 0, -18, 0, 873, 653 ), new CharMetrics( 667, 0, -20, -15, 727, 653 ),
+        new CharMetrics( 722, 0, 60, -18, 699, 666 ), new CharMetrics( 611, 0, 0, 0, 605, 653 ),
+        new CharMetrics( 722, 0, 59, -182, 699, 666 ), new CharMetrics( 611, 0, -13, 0, 588, 653 ),
+        new CharMetrics( 500, 0, 17, -18, 508, 667 ), new CharMetrics( 556, 0, 59, 0, 633, 653 ),
+        new CharMetrics( 722, 0, 102, -18, 765, 653 ), new CharMetrics( 611, 0, 76, -18, 688, 653 ),
+        new CharMetrics( 833, 0, 71, -18, 906, 653 ), new CharMetrics( 611, 0, -29, 0, 655, 653 ),
+        new CharMetrics( 556, 0, 78, 0, 633, 653 ), new CharMetrics( 556, 0, -6, 0, 606, 653 ),
+        new CharMetrics( 389, 0, 21, -153, 391, 663 ), new CharMetrics( 278, 0, -41, -18, 319, 666 ),
+        new CharMetrics( 389, 0, 12, -153, 382, 663 ), new CharMetrics( 422, 0, 0, 301, 422, 666 ),
+        new CharMetrics( 500, 0, 0, -125, 500, -75 ), new CharMetrics( 333, 0, 171, 436, 310, 666 ),
+        new CharMetrics( 500, 0, 17, -11, 476, 441 ), new CharMetrics( 500, 0, 23, -11, 473, 683 ),
+        new CharMetrics( 444, 0, 30, -11, 425, 441 ), new CharMetrics( 500, 0, 15, -13, 527, 683 ),
+        new CharMetrics( 444, 0, 31, -11, 412, 441 ), new CharMetrics( 278, 0, -147, -207, 424, 678 ),
+        new CharMetrics( 500, 0, 8, -206, 472, 441 ), new CharMetrics( 500, 0, 19, -9, 478, 683 ),
+        new CharMetrics( 278, 0, 49, -11, 264, 654 ), new CharMetrics( 278, 0, -124, -207, 276, 654 ),
+        new CharMetrics( 444, 0, 14, -11, 461, 683 ), new CharMetrics( 278, 0, 41, -11, 279, 683 ),
+        new CharMetrics( 722, 0, 12, -9, 704, 441 ), new CharMetrics( 500, 0, 14, -9, 474, 441 ),
+        new CharMetrics( 500, 0, 27, -11, 468, 441 ), new CharMetrics( 500, 0, -75, -205, 469, 441 ),
+        new CharMetrics( 500, 0, 25, -209, 483, 441 ), new CharMetrics( 389, 0, 45, 0, 412, 441 ),
+        new CharMetrics( 389, 0, 16, -13, 366, 442 ), new CharMetrics( 278, 0, 37, -11, 296, 546 ),
+        new CharMetrics( 500, 0, 42, -11, 475, 441 ), new CharMetrics( 444, 0, 21, -18, 426, 441 ),
+        new CharMetrics( 667, 0, 16, -18, 648, 441 ), new CharMetrics( 444, 0, -27, -11, 447, 441 ),
+        new CharMetrics( 444, 0, -24, -206, 426, 441 ), new CharMetrics( 389, 0, -2, -81, 380, 428 ),
+        new CharMetrics( 400, 0, 51, -177, 407, 687 ), new CharMetrics( 275, 0, 105, -18, 171, 666 ),
+        new CharMetrics( 400, 0, -7, -177, 349, 687 ), new CharMetrics( 541, 0, 40, 183, 502, 323 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 278, 0, 49, -11, 235, 441 ),
+        new CharMetrics( 333, 0, 121, 492, 311, 664 ), new CharMetrics( 333, 0, 180, 494, 403, 664 ),
+        new CharMetrics( 333, 0, 91, 492, 385, 661 ), new CharMetrics( 333, 0, 100, 517, 427, 624 ),
+        new CharMetrics( 333, 0, 99, 532, 411, 583 ), new CharMetrics( 333, 0, 117, 492, 418, 650 ),
+        new CharMetrics( 333, 0, 207, 508, 305, 606 ), new CharMetrics( 333, 0, 107, 508, 405, 606 ),
+        new CharMetrics( 250, 0, 0, 0, 0, 0 ), new CharMetrics( 333, 0, 155, 492, 355, 691 ),
+        new CharMetrics( 333, 0, -30, -217, 182, 0 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 333, 0, 93, 494, 486, 664 ), new CharMetrics( 333, 0, -20, -169, 200, 40 ),
+        new CharMetrics( 333, 0, 121, 492, 426, 661 ), new CharMetrics( 250, 0, 0, 0, 0, 0 ),
+        new CharMetrics( 389, 0, 59, -205, 322, 473 ), new CharMetrics( 500, 0, 77, -143, 472, 560 ),
+        new CharMetrics( 500, 0, 10, -6, 517, 670 ), new CharMetrics( 500, 0, -22, 53, 522, 597 ),
+        new CharMetrics( 500, 0, 27, 0, 603, 653 ), new CharMetrics( 275, 0, 105, -18, 171, 666 ),
+        new CharMetrics( 500, 0, 53, -162, 461, 666 ), new CharMetrics( 333, 0, 107, 508, 405, 606 ),
+        new CharMetrics( 760, 0, 41, -18, 719, 666 ), new CharMetrics( 276, 0, 42, 406, 352, 676 ),
+        new CharMetrics( 500, 0, 53, 37, 445, 403 ), new CharMetrics( 675, 0, 86, 108, 590, 386 ),
+        new CharMetrics( 333, 0, 49, 192, 282, 255 ), new CharMetrics( 760, 0, 41, -18, 719, 666 ),
+        new CharMetrics( 333, 0, 99, 532, 411, 583 ), new CharMetrics( 400, 0, 101, 390, 387, 676 ),
+        new CharMetrics( 675, 0, 86, 0, 590, 506 ), new CharMetrics( 300, 0, 33, 271, 324, 676 ),
+        new CharMetrics( 300, 0, 43, 268, 339, 676 ), new CharMetrics( 333, 0, 180, 494, 403, 664 ),
+        new CharMetrics( 500, 0, -30, -209, 497, 428 ), new CharMetrics( 523, 0, 55, -123, 616, 653 ),
+        new CharMetrics( 250, 0, 70, 199, 181, 310 ), new CharMetrics( 333, 0, -30, -217, 182, 0 ),
+        new CharMetrics( 300, 0, 43, 271, 284, 676 ), new CharMetrics( 310, 0, 67, 406, 362, 676 ),
+        new CharMetrics( 500, 0, 55, 37, 447, 403 ), new CharMetrics( 750, 0, 33, -10, 736, 676 ),
+        new CharMetrics( 750, 0, 34, -10, 749, 676 ), new CharMetrics( 750, 0, 23, -10, 736, 676 ),
+        new CharMetrics( 500, 0, 28, -205, 368, 471 ), new CharMetrics( 611, 0, -51, 0, 564, 876 ),
+        new CharMetrics( 611, 0, -51, 0, 564, 876 ), new CharMetrics( 611, 0, -51, 0, 564, 873 ),
+        new CharMetrics( 611, 0, -51, 0, 566, 836 ), new CharMetrics( 611, 0, -51, 0, 564, 818 ),
+        new CharMetrics( 611, 0, -51, 0, 564, 883 ), new CharMetrics( 889, 0, -27, 0, 911, 653 ),
+        new CharMetrics( 667, 0, 66, -217, 689, 666 ), new CharMetrics( 611, 0, -1, 0, 634, 876 ),
+        new CharMetrics( 611, 0, -1, 0, 634, 876 ), new CharMetrics( 611, 0, -1, 0, 634, 873 ),
+        new CharMetrics( 611, 0, -1, 0, 634, 818 ), new CharMetrics( 333, 0, -8, 0, 384, 876 ),
+        new CharMetrics( 333, 0, -8, 0, 413, 876 ), new CharMetrics( 333, 0, -8, 0, 425, 873 ),
+        new CharMetrics( 333, 0, -8, 0, 435, 818 ), new CharMetrics( 722, 0, -8, 0, 700, 653 ),
+        new CharMetrics( 667, 0, -20, -15, 727, 836 ), new CharMetrics( 722, 0, 60, -18, 699, 876 ),
+        new CharMetrics( 722, 0, 60, -18, 699, 876 ), new CharMetrics( 722, 0, 60, -18, 699, 873 ),
+        new CharMetrics( 722, 0, 60, -18, 699, 836 ), new CharMetrics( 722, 0, 60, -18, 699, 818 ),
+        new CharMetrics( 675, 0, 93, 8, 582, 497 ), new CharMetrics( 722, 0, 60, -105, 699, 722 ),
+        new CharMetrics( 722, 0, 102, -18, 765, 876 ), new CharMetrics( 722, 0, 102, -18, 765, 876 ),
+        new CharMetrics( 722, 0, 102, -18, 765, 873 ), new CharMetrics( 722, 0, 102, -18, 765, 818 ),
+        new CharMetrics( 556, 0, 78, 0, 633, 876 ), new CharMetrics( 611, 0, 0, 0, 569, 653 ),
+        new CharMetrics( 500, 0, -168, -207, 493, 679 ), new CharMetrics( 500, 0, 17, -11, 476, 664 ),
+        new CharMetrics( 500, 0, 17, -11, 487, 664 ), new CharMetrics( 500, 0, 17, -11, 476, 661 ),
+        new CharMetrics( 500, 0, 17, -11, 511, 624 ), new CharMetrics( 500, 0, 17, -11, 489, 606 ),
+        new CharMetrics( 500, 0, 17, -11, 476, 691 ), new CharMetrics( 667, 0, 23, -11, 640, 441 ),
+        new CharMetrics( 444, 0, 26, -217, 425, 441 ), new CharMetrics( 444, 0, 31, -11, 412, 664 ),
+        new CharMetrics( 444, 0, 31, -11, 459, 664 ), new CharMetrics( 444, 0, 31, -11, 441, 661 ),
+        new CharMetrics( 444, 0, 31, -11, 451, 606 ), new CharMetrics( 278, 0, 49, -11, 284, 664 ),
+        new CharMetrics( 278, 0, 49, -11, 356, 664 ), new CharMetrics( 278, 0, 34, -11, 328, 661 ),
+        new CharMetrics( 278, 0, 49, -11, 353, 606 ), new CharMetrics( 500, 0, 27, -11, 482, 683 ),
+        new CharMetrics( 500, 0, 14, -9, 476, 624 ), new CharMetrics( 500, 0, 27, -11, 468, 664 ),
+        new CharMetrics( 500, 0, 27, -11, 487, 664 ), new CharMetrics( 500, 0, 27, -11, 468, 661 ),
+        new CharMetrics( 500, 0, 27, -11, 496, 624 ), new CharMetrics( 500, 0, 27, -11, 489, 606 ),
+        new CharMetrics( 675, 0, 86, -11, 590, 517 ), new CharMetrics( 500, 0, 28, -135, 469, 554 ),
+        new CharMetrics( 500, 0, 42, -11, 475, 664 ), new CharMetrics( 500, 0, 42, -11, 477, 664 ),
+        new CharMetrics( 500, 0, 42, -11, 475, 661 ), new CharMetrics( 500, 0, 42, -11, 479, 606 ),
+        new CharMetrics( 444, 0, -24, -206, 459, 664 ), new CharMetrics( 500, 0, -75, -205, 469, 683 ),
+        new CharMetrics( 444, 0, -24, -206, 441, 606 )};
+
+    SerifItalic()
+    {
+        super( false, 683, -205, new CharMetrics( 0, 0, -169, -217, 1010, 883 ), metrics );
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/WMFWriter.java b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/WMFWriter.java
new file mode 100644
index 0000000..f78bda2
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/main/java/org/apache/maven/doxia/module/rtf/WMFWriter.java
@@ -0,0 +1,549 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Vector;
+
+/**
+ * A Windows MetaFile writer.
+ *
+ * @version $Id: WMFWriter.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+class WMFWriter
+{
+    /**
+     * See the libwmf library documentation
+     * (http://www.wvware.com/wmf_doc_index.html)
+     * for a description of WMF format.
+     */
+    private static Record trailer = new Record( 0, null );
+
+    /**
+     * standard header fields
+     */
+    private short fileType;
+
+    private short headerSize;
+
+    private short version;
+
+    private int fileSize;
+
+    private short numOfObjects;
+
+    private int maxRecordSize;
+
+    private short numOfParams;
+
+    private Vector records;
+
+    WMFWriter()
+    {
+        fileType = 2;
+        headerSize = 9;
+        version = 0x0300;
+        fileSize = headerSize + trailer.size();
+        numOfObjects = 0;
+        maxRecordSize = trailer.size();
+        numOfParams = 0;
+
+        records = new Vector();
+    }
+
+    void add( Record record )
+    {
+        records.addElement( record );
+
+        int size = record.size();
+        fileSize += size;
+        if ( size > maxRecordSize )
+        {
+            maxRecordSize = size;
+        }
+    }
+
+    int size()
+    {
+        return fileSize;
+    }
+
+    void write( String fileName )
+        throws IOException
+    {
+        BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( fileName ) );
+        write( out );
+        out.flush();
+        out.close();
+    }
+
+    void write( OutputStream out )
+        throws IOException
+    {
+        write16( fileType, out );
+        write16( headerSize, out );
+        write16( version, out );
+        write32( fileSize, out );
+        write16( numOfObjects, out );
+        write32( maxRecordSize, out );
+        write16( numOfParams, out );
+
+        for ( int i = 0, n = records.size(); i < n; ++i )
+        {
+            Record record = (Record) records.elementAt( i );
+            record.write( out );
+        }
+
+        trailer.write( out );
+    }
+
+    /**
+     * Writes a 16-bit integer in little-endian format.
+     */
+    static void write16( int word, OutputStream out )
+        throws IOException
+    {
+        out.write( word );
+        out.write( word >> 8 );
+    }
+
+    /**
+     * Writes a 32-bit integer in little-endian format.
+     */
+    static void write32( int dword, OutputStream out )
+        throws IOException
+    {
+        out.write( dword );
+        out.write( dword >> 8 );
+        out.write( dword >> 16 );
+        out.write( dword >> 24 );
+    }
+
+    void print( Writer out )
+        throws IOException
+    {
+        print16( fileType, out );
+        print16( headerSize, out );
+        print16( version, out );
+        print32( fileSize, out );
+        print16( numOfObjects, out );
+        print32( maxRecordSize, out );
+        print16( numOfParams, out );
+        out.write( System.getProperty( "line.separator" ) );
+
+        for ( int i = 0, n = records.size(); i < n; ++i )
+        {
+            Record record = (Record) records.elementAt( i );
+            record.print( out );
+        }
+
+        trailer.print( out );
+    }
+
+    static void print16( int word, Writer out )
+        throws IOException
+    {
+        byte[] buf = new byte[2];
+        buf[0] = (byte) word;
+        buf[1] = (byte) ( word >> 8 );
+        print( buf, 0, 2, out );
+    }
+
+    static void print32( int dword, Writer out )
+        throws IOException
+    {
+        byte[] buf = new byte[4];
+        buf[0] = (byte) dword;
+        buf[1] = (byte) ( dword >> 8 );
+        buf[2] = (byte) ( dword >> 16 );
+        buf[3] = (byte) ( dword >> 24 );
+        print( buf, 0, 4, out );
+    }
+
+    static void print( byte[] buf, int off, int len, Writer out )
+        throws IOException
+    {
+        char[] cbuf = new char[2 * len];
+
+        for ( int i = off, j = 0, n = off + len; i < n; ++i )
+        {
+            int d = ( buf[i] >> 4 ) & 0x0f;
+            if ( d < 10 )
+            {
+                cbuf[j++] = (char) ( '0' + d );
+            }
+            else
+            {
+                cbuf[j++] = (char) ( 'a' + ( d - 10 ) );
+            }
+            d = buf[i] & 0x0f;
+            if ( d < 10 )
+            {
+                cbuf[j++] = (char) ( '0' + d );
+            }
+            else
+            {
+                cbuf[j++] = (char) ( 'a' + ( d - 10 ) );
+            }
+        }
+
+        out.write( cbuf );
+    }
+
+    static void print( byte[] buf, int off, int len, Writer out, int lw )
+        throws IOException
+    {
+        String ls = System.getProperty( "line.separator" );
+        for ( int i = off; len > 0; )
+        {
+            int n = Math.min( len, lw / 2 );
+            print( buf, i, n, out );
+            out.write( ls );
+            len -= n;
+            i += n;
+        }
+    }
+
+    /**
+     * Standard data record.
+     */
+    static class Record
+    {
+        protected int size;
+
+        private short function;
+
+        private short[] parameters;
+
+        Record( int function, int[] parameters )
+        {
+            this.function = (short) function;
+            if ( parameters != null )
+            {
+                this.parameters = new short[parameters.length];
+                for ( int i = 0; i < parameters.length; ++i )
+                {
+                    this.parameters[i] = (short) parameters[i];
+                }
+            }
+            size = 3 + ( parameters == null ? 0 : parameters.length );
+        }
+
+        int size()
+        {
+            return size;
+        }
+
+        void write( OutputStream out )
+            throws IOException
+        {
+            write32( size, out );
+            write16( function, out );
+            if ( parameters != null )
+            {
+                for ( int i = 0; i < parameters.length; ++i )
+                {
+                    write16( parameters[i], out );
+                }
+            }
+        }
+
+        void print( Writer out )
+            throws IOException
+        {
+            print32( size, out );
+            print16( function, out );
+            if ( parameters != null )
+            {
+                for ( int i = 0; i < parameters.length; ++i )
+                {
+                    print16( parameters[i], out );
+                }
+            }
+        }
+
+    }
+
+    /**
+     * DIB data structure.
+     */
+    static class Dib
+    {
+        /**
+         * compression types
+         */
+        static final int BI_RGB = 0;
+
+        static final int BI_RLE8 = 1;
+
+        static final int BI_RLE4 = 2;
+
+        static final int BI_BITFIELDS = 3;
+
+        /*
+         * information header fields
+         */
+        final int biSize = 40;        // header size
+
+        int biWidth;            // image width
+
+        int biHeight;            // image height
+
+        final short biPlanes = 1;    // number of planes
+
+        short biBitCount;        // number of bits per pixel
+
+        int biCompression;        // compression type
+
+        int biSizeImage;            // image data size
+
+        int biXPelsPerMeter;        // horizontal resolution
+
+        int biYPelsPerMeter;        // vertical resolution
+
+        int biClrUsed;            // number of colors
+
+        int biClrImportant;        // number of required colors
+
+        byte[] palette;            // color palette
+
+        byte[] bitmap;            // bitmap data
+
+        int size()
+        {
+            int size = biSize;
+            if ( palette != null )
+            {
+                size += palette.length;
+            }
+            if ( bitmap != null )
+            {
+                if ( biSizeImage != 0 )
+                {
+                    size += biSizeImage;
+                }
+                else
+                {
+                    size += bitmap.length;
+                }
+            }
+            return size / 2;
+        }
+
+        void write( OutputStream out )
+            throws IOException
+        {
+            write32( biSize, out );
+            write32( biWidth, out );
+            write32( biHeight, out );
+            write16( biPlanes, out );
+            write16( biBitCount, out );
+            write32( biCompression, out );
+            write32( biSizeImage, out );
+            write32( biXPelsPerMeter, out );
+            write32( biYPelsPerMeter, out );
+            write32( biClrUsed, out );
+            write32( biClrImportant, out );
+            if ( palette != null )
+            {
+                out.write( palette );
+            }
+            if ( bitmap != null )
+            {
+                if ( biSizeImage != 0 )
+                {
+                    out.write( bitmap, 0, biSizeImage );
+                }
+                else
+                {
+                    out.write( bitmap );
+                }
+            }
+        }
+
+        void print( Writer out )
+            throws IOException
+        {
+            String ls = System.getProperty( "line.separator" );
+
+            print32( biSize, out );
+            print32( biWidth, out );
+            print32( biHeight, out );
+            print16( biPlanes, out );
+            print16( biBitCount, out );
+            out.write( ls );
+
+            print32( biCompression, out );
+            print32( biSizeImage, out );
+            print32( biXPelsPerMeter, out );
+            print32( biYPelsPerMeter, out );
+            print32( biClrUsed, out );
+            print32( biClrImportant, out );
+            out.write( ls );
+
+            if ( palette != null )
+            {
+                WMFWriter.print( palette, 0, palette.length, out, 64 );
+            }
+
+            if ( bitmap != null )
+            {
+                int len = ( biSizeImage != 0 ) ? biSizeImage : bitmap.length;
+                WMFWriter.print( bitmap, 0, len, out, 76 );
+            }
+        }
+
+        static int rlEncode8( byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff )
+        {
+            int i1, i2, j, k, n;
+            int len;
+
+            for ( i1 = inOff, j = outOff, n = ( inOff + inLen ); i1 < n; )
+            {
+                for ( i2 = ( i1 + 1 ), len = 1; i2 < n; ++i2, ++len )
+                {
+                    if ( inBuf[i2] != inBuf[i2 - 1] )
+                    {
+                        break;
+                    }
+                }
+
+                if ( len > 1 )
+                {
+                    while ( len > 255 )
+                    {
+                        outBuf[j++] = (byte) 255;
+                        outBuf[j++] = inBuf[i1];
+                        len -= 255;
+                    }
+                    if ( len > 0 )
+                    {
+                        outBuf[j++] = (byte) len;
+                        outBuf[j++] = inBuf[i1];
+                    }
+                    i1 = i2;
+                    continue;
+                }
+
+                for ( ++i2; i2 < n; ++i2, ++len )
+                {
+                    if ( inBuf[i2] == inBuf[i2 - 1] )
+                    {
+                        break;
+                    }
+                }
+
+                while ( len > 255 )
+                {
+                    outBuf[j++] = 0;
+                    outBuf[j++] = (byte) 255;
+                    for ( k = 0; k < 255; ++k )
+                    {
+                        outBuf[j++] = inBuf[i1++];
+                    }
+                    outBuf[j++] = (byte) 0;
+                    len -= 255;
+                }
+
+                if ( len > 2 )
+                {
+                    outBuf[j++] = 0;
+                    outBuf[j++] = (byte) len;
+                    for ( k = 0; k < len; ++k )
+                    {
+                        outBuf[j++] = inBuf[i1++];
+                    }
+                    if ( len % 2 != 0 )
+                    {
+                        outBuf[j++] = 0;
+                    }
+                }
+                else
+                {
+                    while ( len > 0 )
+                    {
+                        outBuf[j++] = 1;
+                        outBuf[j++] = inBuf[i1++];
+                        len -= 1;
+                    }
+                }
+            }
+
+            return j - outOff;
+        }
+    }
+
+    static class DibBitBltRecord
+        extends Record
+    {
+        /**
+         * parameter count
+         */
+        static final int P_COUNT = 8;
+
+        /**
+         * parameter indexes
+         */
+        static final int P_ROP_L = 0;
+
+        static final int P_ROP_H = 1;
+
+        static final int P_YSRC = 2;
+
+        static final int P_XSRC = 3;
+
+        static final int P_HEIGHT = 4;
+
+        static final int P_WIDTH = 5;
+
+        static final int P_YDST = 6;
+
+        static final int P_XDST = 7;
+
+        private Dib dib;
+
+        DibBitBltRecord( int[] parameters, Dib dib )
+        {
+            super( 0x0940, parameters );
+            size += dib.size();
+            this.dib = dib;
+        }
+
+        /** {@inheritDoc} */
+        void write( OutputStream out )
+            throws IOException
+        {
+            super.write( out );
+            dib.write( out );
+        }
+
+        /** {@inheritDoc} */
+        void print( Writer out )
+            throws IOException
+        {
+            super.print( out );
+            dib.print( out );
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/site/site.xml b/doxia-modules/doxia-module-rtf/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-rtf/src/test/java/org/apache/maven/doxia/module/rtf/RtfSinkTest.java b/doxia-modules/doxia-module-rtf/src/test/java/org/apache/maven/doxia/module/rtf/RtfSinkTest.java
new file mode 100644
index 0000000..f7d52b7
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/test/java/org/apache/maven/doxia/module/rtf/RtfSinkTest.java
@@ -0,0 +1,93 @@
+package org.apache.maven.doxia.module.rtf;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.AbstractSinkTestCase;
+import org.apache.maven.doxia.sink.SinkTestDocument;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.module.apt.AptParser;
+import org.codehaus.plexus.util.IOUtil;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: RtfSinkTest.java 735472 2009-01-18 15:30:54Z vsiveton $
+ */
+public class RtfSinkTest
+    extends AbstractSinkTestCase
+{
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "rtf";
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return new AptParser();
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink()
+        throws Exception
+    {
+        File outputFile = new File( getBasedirFile(), "target/output/test.rtf" );
+        outputFile.getParentFile().mkdirs();
+        return new RtfSink( new FileOutputStream( outputFile ) );
+    }
+
+    /** {@inheritDoc} */
+    protected Reader getTestReader()
+        throws Exception
+    {
+        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( "test.apt" );
+
+        InputStreamReader reader = new InputStreamReader( is );
+
+        return reader;
+    }
+
+    public void testDocument()
+        throws Exception
+    {
+        File outputFile = new File( getBasedirFile(), "target/test-output/sink/testDocument.rtf" );
+        outputFile.getParentFile().mkdirs();
+        OutputStream out = new FileOutputStream( outputFile );
+
+        Sink sink = new RtfSink( out );
+        try
+        {
+            SinkTestDocument.generate( sink );
+        }
+        finally
+        {
+            sink.close();
+            IOUtil.close( out );
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-rtf/src/test/resources/test.apt b/doxia-modules/doxia-module-rtf/src/test/resources/test.apt
new file mode 100644
index 0000000..715ec17
--- /dev/null
+++ b/doxia-modules/doxia-module-rtf/src/test/resources/test.apt
@@ -0,0 +1,16 @@
+ -----
+ Maven: the cute and fluffy build tool.
+ -----
+ Jason van Zyl
+ -----
+
+Maven: the cute and fluffy build tool that's good for the whole family
+
+ Maven will make you laugh; Maven will make you jump for joy; Maven will
+ make you put that dirty little tool called Ant in the litter box with
+ the cat treats where it belongs!
+
+* Say Goodbye the build time stains!
+
+ Maven helps you get rid of those <hard to get rid of> build stains. Wash with Maven
+ and go!
diff --git a/doxia-modules/doxia-module-twiki/pom.xml b/doxia-modules/doxia-module-twiki/pom.xml
new file mode 100644
index 0000000..097f0f6
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-twiki</artifactId>
+
+  <name>Doxia :: TWiki Module</name>
+  <description>A Doxia module for Twiki source documents.</description>
+
+   <developers>
+    <developer>
+      <name>Juan F. Codagnone</name>
+      <email>juan *at* zauber dot. com dot. ar</email>
+      <roles>
+        <role>Developer</role>
+      </roles>
+      <timezone>-3</timezone>
+    </developer>
+  </developers>
+
+  <contributors>
+    <contributor>
+      <name>Christian Nardi</name>
+      <roles>
+        <role>Developer</role>
+      </roles>
+    </contributor>
+  </contributors>
+</project>
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiMarkup.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiMarkup.java
new file mode 100644
index 0000000..230f7ca
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiMarkup.java
@@ -0,0 +1,138 @@
+package org.apache.maven.doxia.module.twiki;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.markup.TextMarkup;
+
+/**
+ * This interface defines all markups and syntaxes used by the <b>TWiki</b> format.
+ *
+ * @see <a href="http://twiki.org/cgi-bin/view/TWiki/TextFormattingRules">http://twiki.org/cgi-bin/view/TWiki/TextFormattingRules</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: TWikiMarkup.java 746991 2009-02-23 12:35:46Z vsiveton $
+ * @since 1.0
+ */
+public interface TWikiMarkup
+    extends TextMarkup
+{
+    // ----------------------------------------------------------------------
+    // Twiki markups
+    // ----------------------------------------------------------------------
+
+    /** Syntax for the anchor : '#' */
+    char ANCHOR_MARKUP = '#';
+
+    /** Syntax for the start line separator: "   " */
+    String THREE_SPACES_MARKUP = "   ";
+
+    /** Syntax for the bold markup: "*" */
+    String BOLD_END_MARKUP = "*";
+
+    /** Syntax for the bold markup: "*" */
+    String BOLD_START_MARKUP = "*";
+
+    /** Syntax for the bold italic markup: "__" */
+    String BOLD_ITALIC_END_MARKUP = "__";
+
+    /** Syntax for the bold italic markup: "__" */
+    String BOLD_ITALIC_START_MARKUP = "__";
+
+    /** Syntax for the bold monospaced markup: "==" */
+    String BOLD_MONOSPACED_END_MARKUP = "==";
+
+    /** Syntax for the bold monospaced markup: "==" */
+    String BOLD_MONOSPACED_START_MARKUP = "==";
+
+    /** Syntax for the definition list item: "   $ " */
+    String DEFINITION_LIST_ITEM_MARKUP = THREE_SPACES_MARKUP + "$ ";
+
+    /** Syntax for the definition list definition: ": " */
+    String DEFINITION_LIST_DEFINITION_MARKUP = ": ";
+
+    /** Syntax for the horizontal rule markup: "---" */
+    String HORIZONTAL_RULE_MARKUP = "---";
+
+    /** Syntax for the italic markup: "_" */
+    String ITALIC_END_MARKUP = "_";
+
+    /** Syntax for the italic markup: "_" */
+    String ITALIC_START_MARKUP = "_";
+
+    /** Syntax for the link end markup: "]]" */
+    String LINK_END_MARKUP = "]]";
+
+    /** Syntax for the link middle markup: "][" */
+    String LINK_MIDDLE_MARKUP = "][";
+
+    /** Syntax for the link start markup: "[[" */
+    String LINK_START_MARKUP = "[[";
+
+    /** Syntax for the list item markup: "* */
+    String LIST_ITEM_MARKUP = "* ";
+
+    /** Syntax for the mono-spaced style end: "=" */
+    String MONOSPACED_END_MARKUP = "=";
+
+    /** Syntax for the mono-spaced style start: "=" */
+    String MONOSPACED_START_MARKUP = "=";
+
+    /** Syntax for the numbering decimal markup char: "1." */
+    String NUMBERING_MARKUP = "1.";
+
+    /** Syntax for the numbering lower alpha markup char: "a." */
+    String NUMBERING_LOWER_ALPHA_MARKUP = "a.";
+
+    /** Syntax for the numbering lower roman markup char: "i." */
+    String NUMBERING_LOWER_ROMAN_MARKUP = "i.";
+
+    /** Syntax for the numbering upper alpha markup char: "A." */
+    String NUMBERING_UPPER_ALPHA_MARKUP = "A.";
+
+    /** Syntax for the numbering upper roman markup char: "I." */
+    String NUMBERING_UPPER_ROMAN_MARKUP = "I.";
+
+    /** Syntax for the table cell header end markup: "* |" */
+    String TABLE_CELL_HEADER_END_MARKUP = "* |";
+
+    /** Syntax for the table cell header start markup: " *" */
+    String TABLE_CELL_HEADER_START_MARKUP = " *";
+
+    /** Syntax for the table cell markup: "| " */
+    String TABLE_CELL_MARKUP = " |";
+
+    /** Syntax for the table row markup: "|" */
+    String TABLE_ROW_MARKUP = "|";
+
+    // ----------------------------------------------------------------------
+    // Specific Twiki tags
+    // ----------------------------------------------------------------------
+
+    /** TWiki tag for <code>verbatim</code> */
+    Tag VERBATIM_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "verbatim";
+        }
+    };
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiParser.java
new file mode 100644
index 0000000..a70915e
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiParser.java
@@ -0,0 +1,249 @@
+package org.apache.maven.doxia.module.twiki;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.twiki.parser.Block;
+import org.apache.maven.doxia.module.twiki.parser.BlockParser;
+import org.apache.maven.doxia.module.twiki.parser.FormatedTextParser;
+import org.apache.maven.doxia.module.twiki.parser.GenericListBlockParser;
+import org.apache.maven.doxia.module.twiki.parser.HRuleBlockParser;
+import org.apache.maven.doxia.module.twiki.parser.ParagraphBlockParser;
+import org.apache.maven.doxia.module.twiki.parser.SectionBlock;
+import org.apache.maven.doxia.module.twiki.parser.SectionBlockParser;
+import org.apache.maven.doxia.module.twiki.parser.TableBlockParser;
+import org.apache.maven.doxia.module.twiki.parser.TextParser;
+import org.apache.maven.doxia.module.twiki.parser.VerbatimBlockParser;
+import org.apache.maven.doxia.module.twiki.parser.XHTMLWikiWordLinkResolver;
+import org.apache.maven.doxia.parser.AbstractTextParser;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.util.ByLineSource;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Parse the <a href="http://twiki.org/cgi-bin/view/TWiki/TextFormattingRules">
+ * twiki file format</a>
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TWikiParser.java 807179 2009-08-24 12:21:26Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="twiki"
+ */
+public class TWikiParser
+    extends AbstractTextParser
+{
+    private static final int EXTENSION_LENGTH = 6;
+
+    /** paragraph parser. */
+    private final ParagraphBlockParser paraParser = new ParagraphBlockParser();
+
+    /** section parser. */
+    private final SectionBlockParser sectionParser = new SectionBlockParser();
+
+    /** enumeration parser. */
+    private final GenericListBlockParser listParser = new GenericListBlockParser();
+
+    /** Text parser. */
+    private final FormatedTextParser formatTextParser = new FormatedTextParser();
+
+    /**
+     * text parser.
+     * This only works for xhtml output, but there is no way
+     * of transforming a wikiWord in another context.
+     */
+    private final TextParser textParser = new TextParser( new XHTMLWikiWordLinkResolver() );
+
+    /** hruler parser. */
+    private final HRuleBlockParser hrulerParser = new HRuleBlockParser();
+
+    /** table parser. */
+    private final TableBlockParser tableParser = new TableBlockParser();
+
+    /** verbatim parser. */
+    private final VerbatimBlockParser verbatimParser = new VerbatimBlockParser();
+
+    /** list of parsers to try to apply to the toplevel */
+    private BlockParser[] parsers;
+
+    /**
+     * Creates the TWikiParser.
+     */
+    public TWikiParser()
+    {
+        init();
+    }
+
+    /**
+     * <p>parse.</p>
+     *
+     * @param source source to parse.
+     * @return the blocks that represent source.
+     * @throws org.apache.maven.doxia.parser.ParseException on error.
+     */
+    public final List parse( final ByLineSource source )
+        throws ParseException
+    {
+        final List ret = new ArrayList();
+
+        String line;
+        while ( ( line = source.getNextLine() ) != null )
+        {
+            boolean accepted = false;
+
+            for ( int i = 0; i < parsers.length; i++ )
+            {
+                final BlockParser parser = parsers[i];
+
+                if ( parser.accept( line ) )
+                {
+                    accepted = true;
+                    ret.add( parser.visit( line, source ) );
+                    break;
+                }
+            }
+            if ( !accepted )
+            {
+                throw new ParseException( "Line number not handle : " + source.getLineNumber() + ": " + line );
+            }
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    public final synchronized void parse( final Reader source, final Sink sink )
+        throws ParseException
+    {
+        init();
+
+        List blocks;
+        final ByLineSource src = new ByLineReaderSource( source );
+
+        try
+        {
+            blocks = parse( src );
+        }
+        catch ( final Exception e )
+        {
+            // TODO handle column number
+            throw new ParseException( e, src.getName(), src.getLineNumber(), -1 );
+        }
+
+        sink.head();
+
+        final String title = getTitle( blocks, src );
+        if ( title != null )
+        {
+            sink.title();
+            sink.text( title );
+            sink.title_();
+        }
+
+        sink.head_();
+        sink.body();
+        for ( Iterator it = blocks.iterator(); it.hasNext(); )
+        {
+            final Block block = (Block) it.next();
+
+            block.traverse( sink );
+        }
+        sink.body_();
+        sink.flush();
+        sink.close();
+
+        setSecondParsing( false );
+        init();
+    }
+
+    /**
+     * Guess a title for the page. It uses the first section that it finds.
+     * If it doesn't find any section tries to get it from
+     * {@link ByLineReaderSource#getName()}
+     *
+     * @param blocks blocks to parse
+     * @param source source to parse
+     * @return a title for a page
+     * @since 1.1
+     */
+    public final String getTitle( final List blocks, final ByLineSource source )
+    {
+        String title = null;
+
+        for ( Iterator it = blocks.iterator(); title == null && it.hasNext(); )
+        {
+            final Block block = (Block) it.next();
+
+            if ( block instanceof SectionBlock )
+            {
+                final SectionBlock sectionBlock = (SectionBlock) block;
+                title = sectionBlock.getTitle();
+            }
+        }
+
+        if ( title == null )
+        {
+            String name = source.getName();
+            if ( name != null )
+            {
+                name = name.trim();
+
+                if ( name.length() != 0 )
+                {
+                    if ( name.endsWith( ".twiki" ) )
+                    {
+                        title = name.substring( 0, name.length() - EXTENSION_LENGTH );
+                    }
+                    else
+                    {
+                        title = name;
+                    }
+                }
+            }
+        }
+
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        paraParser.setSectionParser( sectionParser );
+        paraParser.setListParser( listParser );
+        paraParser.setTextParser( formatTextParser );
+        paraParser.setHrulerParser( hrulerParser );
+        paraParser.setTableBlockParser( tableParser );
+        paraParser.setVerbatimParser( verbatimParser );
+        sectionParser.setParaParser( paraParser );
+        sectionParser.setHrulerParser( hrulerParser );
+        sectionParser.setVerbatimBlockParser( verbatimParser );
+        listParser.setTextParser( formatTextParser );
+        formatTextParser.setTextParser( textParser );
+        tableParser.setTextParser( formatTextParser );
+
+        this.parsers = new BlockParser[] { sectionParser, hrulerParser, verbatimParser, paraParser };
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSink.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSink.java
new file mode 100644
index 0000000..dd6dd62
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSink.java
@@ -0,0 +1,1294 @@
+package org.apache.maven.doxia.module.twiki;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Stack;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML.Attribute;
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.sink.AbstractTextSink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.sink.SinkUtils;
+import org.apache.maven.doxia.util.HtmlTools;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * TWiki Sink implementation.
+ * <br/>
+ * <b>Note</b>: The encoding used is UTF-8.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: TWikiSink.java 807179 2009-08-24 12:21:26Z vsiveton $
+ * @since 1.0
+ */
+public class TWikiSink
+    extends AbstractTextSink
+    implements TWikiMarkup
+{
+    /**  The writer to use. */
+    private final PrintWriter out;
+
+    /**  The writer to use. */
+    private StringWriter writer;
+
+    /** An indication on if we're in bold mode. */
+    private boolean boldFlag;
+
+    /** An indication on if we're in bold italic or monospaced mode. */
+    private boolean boldItalicOrMonodpacedFlag;
+
+    /** An indication on if we're in head mode. */
+    private boolean headFlag;
+
+    private int levelList = 0;
+
+    /**  listStyles. */
+    private final Stack listStyles;
+
+    /**
+     * Constructor, initialize the Writer and the variables.
+     *
+     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     */
+    protected TWikiSink( Writer writer )
+    {
+        this.out = new PrintWriter( writer );
+        this.listStyles = new Stack();
+
+        init();
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name )
+    {
+        write( EOL );
+        write( ANCHOR_MARKUP + name );
+    }
+
+    /** {@inheritDoc} */
+    public void anchor( String name, SinkEventAttributes attributes )
+    {
+        anchor( name );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void anchor_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void author()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void author( SinkEventAttributes attributes )
+    {
+        author();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void author_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void body()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void body( SinkEventAttributes attributes )
+    {
+        body();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void body_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void bold()
+    {
+        boldFlag = true;
+        write( BOLD_START_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void bold_()
+    {
+        boldFlag = false;
+        if ( !boldItalicOrMonodpacedFlag )
+        {
+            write( BOLD_END_MARKUP );
+        }
+        boldItalicOrMonodpacedFlag = false;
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void comment( String comment )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        out.write( writer.toString() );
+        out.close();
+
+        init();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void date()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void date( SinkEventAttributes attributes )
+    {
+        date();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void date_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definedTerm()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definedTerm( SinkEventAttributes attributes )
+    {
+        definedTerm();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definedTerm_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definition()
+    {
+        write( DEFINITION_LIST_DEFINITION_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void definition( SinkEventAttributes attributes )
+    {
+        definition();
+    }
+
+    /** {@inheritDoc} */
+    public void definition_()
+    {
+        writeEOL();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionList()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definitionList( SinkEventAttributes attributes )
+    {
+        definitionList();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionList_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem()
+    {
+        write( DEFINITION_LIST_ITEM_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void definitionListItem( SinkEventAttributes attributes )
+    {
+        definitionListItem();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void definitionListItem_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void figure()
+    {
+        write( String.valueOf( LESS_THAN ) + Tag.IMG.toString() + SPACE );
+    }
+
+    /** {@inheritDoc} */
+    public void figure( SinkEventAttributes attributes )
+    {
+        figure();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void figure_()
+    {
+        write( SLASH + String.valueOf( GREATER_THAN ) );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void figureCaption()
+    {
+        write( Attribute.ALT.toString() + EQUAL + QUOTE );
+    }
+
+    /** {@inheritDoc} */
+    public void figureCaption( SinkEventAttributes attributes )
+    {
+        figureCaption();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void figureCaption_()
+    {
+        write( QUOTE + String.valueOf( SPACE ) );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String name )
+    {
+        write( Attribute.SRC.toString() + EQUAL + QUOTE + name + QUOTE + String.valueOf( SPACE ) );
+    }
+
+    /** {@inheritDoc} */
+    public void figureGraphics( String src, SinkEventAttributes attributes )
+    {
+        figureGraphics( src );
+    }
+
+    /** {@inheritDoc} */
+    public void flush()
+    {
+        close();
+        writer.flush();
+    }
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        init();
+
+        headFlag = true;
+    }
+
+    /** {@inheritDoc} */
+    public void head( SinkEventAttributes attributes )
+    {
+        head();
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        headFlag = false;
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule()
+    {
+        writeEOL( true );
+        write( HORIZONTAL_RULE_MARKUP );
+        writeEOL( true );
+    }
+
+    /** {@inheritDoc} */
+    public void horizontalRule( SinkEventAttributes attributes )
+    {
+        horizontalRule();
+    }
+
+    /** {@inheritDoc} */
+    public void italic()
+    {
+        if ( boldFlag )
+        {
+            boldItalicOrMonodpacedFlag = true;
+
+            String tmp = writer.toString();
+            writer = new StringWriter();
+            writer.write( tmp.substring( 0, tmp.length() - 1 ) );
+            write( BOLD_ITALIC_START_MARKUP );
+        }
+        else
+        {
+            write( ITALIC_START_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void italic_()
+    {
+        if ( boldFlag )
+        {
+            write( BOLD_ITALIC_END_MARKUP );
+        }
+        else
+        {
+            write( ITALIC_END_MARKUP );
+        }
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void lineBreak()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void lineBreak( SinkEventAttributes attributes )
+    {
+        lineBreak();
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name )
+    {
+        write( LINK_START_MARKUP + name + LINK_MIDDLE_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void link( String name, SinkEventAttributes attributes )
+    {
+        link( name );
+    }
+
+    /** {@inheritDoc} */
+    public void link_()
+    {
+        write( LINK_END_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void list()
+    {
+        if ( !writer.toString().endsWith( EOL + EOL ) )
+        {
+            writeEOL( true );
+        }
+
+        levelList++;
+    }
+
+    /** {@inheritDoc} */
+    public void list( SinkEventAttributes attributes )
+    {
+        list();
+    }
+
+    /** {@inheritDoc} */
+    public void list_()
+    {
+        levelList--;
+    }
+
+    /** {@inheritDoc} */
+    public void listItem()
+    {
+        String indent = StringUtils.repeat( THREE_SPACES_MARKUP, levelList );
+        write( indent + LIST_ITEM_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void listItem( SinkEventAttributes attributes )
+    {
+        listItem();
+    }
+
+    /** {@inheritDoc} */
+    public void listItem_()
+    {
+        writeEOL( true );
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced()
+    {
+        if ( boldFlag )
+        {
+            boldItalicOrMonodpacedFlag = true;
+
+            String tmp = writer.toString();
+            writer = new StringWriter();
+            writer.write( tmp.substring( 0, tmp.length() - 1 ) );
+            write( BOLD_MONOSPACED_START_MARKUP );
+        }
+        else
+        {
+            write( MONOSPACED_START_MARKUP );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void monospaced_()
+    {
+        if ( boldFlag )
+        {
+            write( BOLD_MONOSPACED_END_MARKUP );
+        }
+        else
+        {
+            write( MONOSPACED_END_MARKUP );
+        }
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void nonBreakingSpace()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering )
+    {
+        levelList++;
+
+        String style;
+        switch ( numbering )
+        {
+            case NUMBERING_UPPER_ALPHA:
+                style = NUMBERING_UPPER_ALPHA_MARKUP;
+                break;
+            case NUMBERING_LOWER_ALPHA:
+                style = NUMBERING_LOWER_ALPHA_MARKUP;
+                break;
+            case NUMBERING_UPPER_ROMAN:
+                style = NUMBERING_UPPER_ROMAN_MARKUP;
+                break;
+            case NUMBERING_LOWER_ROMAN:
+                style = NUMBERING_LOWER_ROMAN_MARKUP;
+                break;
+            case NUMBERING_DECIMAL:
+            default:
+                style = NUMBERING_MARKUP;
+        }
+
+        listStyles.push( style );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList( int numbering, SinkEventAttributes attributes )
+    {
+        numberedList( numbering );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedList_()
+    {
+        levelList--;
+        listStyles.pop();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem()
+    {
+        writeEOL( true );
+        String style = (String) listStyles.peek();
+        String indent = StringUtils.repeat( THREE_SPACES_MARKUP, levelList );
+        write( indent + style + SPACE );
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem( SinkEventAttributes attributes )
+    {
+        numberedListItem();
+    }
+
+    /** {@inheritDoc} */
+    public void numberedListItem_()
+    {
+        writeEOL( true );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void pageBreak()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void paragraph()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph( SinkEventAttributes attributes )
+    {
+        paragraph();
+    }
+
+    /** {@inheritDoc} */
+    public void paragraph_()
+    {
+        writeEOL( true );
+        writeEOL();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void rawText( String text )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section( int level, SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section1()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section1_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section2()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section2_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section3()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section3_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section4()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section4_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section5()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section5_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void section_( int level )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void sectionTitle()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle( int level, SinkEventAttributes attributes )
+    {
+        if ( level > 0 && level < 6 )
+        {
+            write( StringUtils.repeat( "-", 3 ) + StringUtils.repeat( "+", level ) );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1()
+    {
+        sectionTitle( 1, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle1_()
+    {
+        sectionTitle_( 1 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2()
+    {
+        sectionTitle( 2, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle2_()
+    {
+        sectionTitle_( 2 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3()
+    {
+        sectionTitle( 3, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle3_()
+    {
+        sectionTitle_( 3 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4()
+    {
+        sectionTitle( 4, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle4_()
+    {
+        sectionTitle_( 4 );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5()
+    {
+        sectionTitle( 5, null );
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle5_()
+    {
+        sectionTitle_( 5 );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void sectionTitle_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void sectionTitle_( int level )
+    {
+        writeEOL( true );
+        writeEOL();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void table()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void table( SinkEventAttributes attributes )
+    {
+        table();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void table_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void tableCaption()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCaption( SinkEventAttributes attributes )
+    {
+        tableCaption();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void tableCaption_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell()
+    {
+        write( " " );
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( SinkEventAttributes attributes )
+    {
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell( String width )
+    {
+        tableCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableCell_()
+    {
+        write( TABLE_CELL_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell()
+    {
+        write( TABLE_CELL_HEADER_START_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( SinkEventAttributes attributes )
+    {
+        tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell( String width )
+    {
+        tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    public void tableHeaderCell_()
+    {
+        write( TABLE_CELL_HEADER_END_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow()
+    {
+        write( TABLE_ROW_MARKUP );
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow( SinkEventAttributes attributes )
+    {
+        tableRow();
+    }
+
+    /** {@inheritDoc} */
+    public void tableRow_()
+    {
+        writeEOL( true );
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void tableRows_()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text )
+    {
+        if ( headFlag )
+        {
+            return;
+        }
+
+        content( text );
+    }
+
+    /** {@inheritDoc} */
+    public void text( String text, SinkEventAttributes attributes )
+    {
+        if ( attributes == null )
+        {
+            text( text );
+        }
+        else
+        {
+            if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "underline" ) )
+            {
+                writeStartTag( Tag.U );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "line-through" ) )
+            {
+                writeStartTag( Tag.S );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sub" ) )
+            {
+                writeStartTag( Tag.SUB );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sup" ) )
+            {
+                writeStartTag( Tag.SUP );
+            }
+
+            text( text );
+
+            if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sup" ) )
+            {
+                writeEndTag( Tag.SUP );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sub" ) )
+            {
+                writeEndTag( Tag.SUB );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "line-through" ) )
+            {
+                writeEndTag( Tag.S );
+            }
+            if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "underline" ) )
+            {
+                writeEndTag( Tag.U );
+            }
+        }
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void title()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void title( SinkEventAttributes attributes )
+    {
+        title();
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void title_()
+    {
+        // nop
+    }
+
+    /**
+     * Not used.
+     * {@inheritDoc}
+     */
+    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( boolean boxed )
+    {
+        SinkEventAttributeSet att = new SinkEventAttributeSet();
+
+        if ( boxed )
+        {
+            att.addAttribute( SinkEventAttributes.DECORATION, "boxed" );
+        }
+
+        verbatim( att );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes( attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES );
+
+        if ( atts == null )
+        {
+            atts = new SinkEventAttributeSet();
+        }
+
+        boolean boxed = false;
+
+        if ( atts.isDefined( SinkEventAttributes.DECORATION ) )
+        {
+            boxed = "boxed".equals( atts.getAttribute( SinkEventAttributes.DECORATION ).toString() );
+        }
+
+        if ( boxed )
+        {
+            atts.addAttribute( Attribute.CLASS, "source" );
+        }
+
+        atts.removeAttribute( SinkEventAttributes.DECORATION );
+
+        String width = (String) atts.getAttribute( Attribute.WIDTH.toString() );
+        atts.removeAttribute( Attribute.WIDTH.toString() );
+
+        writeStartTag( Tag.DIV, atts );
+        writeEOL( true );
+
+        if ( width != null )
+        {
+            atts.addAttribute( Attribute.WIDTH.toString(), width );
+        }
+
+        atts.removeAttribute( Attribute.ALIGN.toString() );
+        atts.removeAttribute( Attribute.CLASS.toString() );
+
+        writeStartTag( VERBATIM_TAG, atts );
+    }
+
+    /** {@inheritDoc} */
+    public void verbatim_()
+    {
+        writeEndTag( VERBATIM_TAG );
+        writeEOL( true );
+        writeEndTag( Tag.DIV );
+        writeEOL( true );
+    }
+
+    // ----------------------------------------------------------------------
+    // Private methods
+    // ----------------------------------------------------------------------
+
+    private void write( String text )
+    {
+        writer.write( unifyEOLs( text ) );
+    }
+
+    /**
+     * Starts a Tag. For instance:
+     * <pre>
+     * <tag>
+     * </pre>
+     * <br/>
+     * <b>Note</b>: Copy from {@link AbstractXmlSink#writeStartTag(javax.swing.text.html.HTML.Tag)}
+     *
+     * @param t a non null tag
+     * @see #writeStartTag(javax.swing.text.html.HTML.Tag)
+     */
+    private void writeStartTag( Tag t )
+    {
+        writeStartTag( t, null );
+    }
+
+    /**
+     * Starts a Tag with attributes. For instance:
+     * <pre>
+     * <tag attName="attValue">
+     * </pre>
+     * <br/>
+     * <b>Note</b>: Copy from {@link AbstractXmlSink#writeStartTag(javax.swing.text.html.HTML.Tag,
+     *      javax.swing.text.MutableAttributeSet)}
+     *
+     * @param t a non null tag
+     * @param att a set of attributes
+     * @see #writeStartTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet, boolean)
+     */
+    private void writeStartTag( Tag t, MutableAttributeSet att )
+    {
+        writeStartTag( t, att, false );
+    }
+
+    /**
+     * Starts a Tag with attributes. For instance:
+     * <pre>
+     * <tag attName="attValue">
+     * </pre>
+     * <br/>
+     * <b>Note</b>: Copy from {@link AbstractXmlSink#writeStartTag(javax.swing.text.html.HTML.Tag,
+     *      javax.swing.text.MutableAttributeSet, boolean)}
+     *
+     * @param t a non null tag
+     * @param att a set of attributes
+     * @param isSimpleTag boolean to write as a simple tag
+     */
+    private void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
+    {
+        if ( t == null )
+        {
+            throw new IllegalArgumentException( "A tag is required" );
+        }
+
+        StringBuffer sb = new StringBuffer();
+        sb.append( LESS_THAN );
+
+        sb.append( t.toString() );
+
+        sb.append( SinkUtils.getAttributeString( att ) );
+
+        if ( isSimpleTag )
+        {
+            sb.append( SPACE ).append( SLASH );
+        }
+
+        sb.append( GREATER_THAN );
+
+        write( sb.toString() );
+    }
+
+    /**
+     * Writes a system EOL.
+     */
+    private void writeEOL()
+    {
+        write( EOL );
+    }
+
+    /**
+     * Writes a system EOL, with or without trim.
+     */
+    private void writeEOL( boolean trim )
+    {
+        if ( !trim )
+        {
+            writeEOL();
+            return;
+        }
+
+        String tmp = writer.toString().trim();
+        writer = new StringWriter();
+        writer.write( tmp );
+        write( EOL );
+    }
+
+    /**
+     * Ends a Tag without writing an EOL. For instance: <pre></tag></pre>.
+     * <br/>
+     * <b>Note</b>: Copy from {@link AbstractXmlSink#writeEndTag(javax.swing.text.html.HTML.Tag)}
+     *
+     * @param t a tag.
+     */
+    private void writeEndTag( Tag t )
+    {
+        StringBuffer sb = new StringBuffer();
+        sb.append( LESS_THAN );
+        sb.append( SLASH );
+
+        sb.append( t.toString() );
+        sb.append( GREATER_THAN );
+
+        write( sb.toString() );
+    }
+
+    /**
+     * Write HTML escaped text to output.
+     *
+     * @param text The text to write.
+     */
+    protected void content( String text )
+    {
+        write( escapeHTML( text ) );
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.writer = new StringWriter();
+        this.headFlag = false;
+        this.levelList = 0;
+        this.listStyles.clear();
+        this.boldFlag = false;
+        this.boldItalicOrMonodpacedFlag = false;
+    }
+
+    /**
+     * Forward to HtmlTools.escapeHTML( text ).
+     *
+     * @param text the String to escape, may be null
+     * @return the text escaped, "" if null String input
+     * @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String)
+     */
+    protected static String escapeHTML( String text )
+    {
+        return HtmlTools.escapeHTML( text );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSinkFactory.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSinkFactory.java
new file mode 100644
index 0000000..084b4e1
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSinkFactory.java
@@ -0,0 +1,44 @@
+package org.apache.maven.doxia.module.twiki;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractTextSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * TWiki implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: TWikiSinkFactory.java 712574 2008-11-09 22:16:42Z hboutemy $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="twiki"
+ */
+public class TWikiSinkFactory
+    extends AbstractTextSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        // encoding can safely be ignored since it isn't written into the generated TWiki source
+        return new TWikiSink( writer );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSiteModule.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSiteModule.java
new file mode 100644
index 0000000..b67e943
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/TWikiSiteModule.java
@@ -0,0 +1,42 @@
+package org.apache.maven.doxia.module.twiki;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.site.AbstractSiteModule;
+
+/**
+ * <p>TWikiSiteModule class.</p>
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TWikiSiteModule.java 782392 2009-06-07 14:14:16Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.module.site.SiteModule" role-hint="twiki"
+ */
+public class TWikiSiteModule
+    extends AbstractSiteModule
+{
+    /**
+     * Default constructor.
+     */
+    public TWikiSiteModule()
+    {
+        super( "twiki", "twiki", "twiki" );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/AbstractFatherBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/AbstractFatherBlock.java
new file mode 100644
index 0000000..3f4ea70
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/AbstractFatherBlock.java
@@ -0,0 +1,138 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Arrays;
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Generic Block for the Block that have child blocks.
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: AbstractFatherBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+abstract class AbstractFatherBlock
+    implements Block
+{
+    /**
+     * @see AbstractFatherBlock#AbstractFatherBlock(Block[])
+     */
+    private final Block[] childBlocks;
+
+    /**
+     * method called before traversing the childs
+     *
+     * @param sink a sink to fill
+     */
+    abstract void before( Sink sink );
+
+    /**
+     * method called after traversing the childs
+     *
+     * @param sink a sink to fill
+     */
+    abstract void after( Sink sink );
+
+    /**
+     * Creates the AbstractFatherBlock.
+     *
+     * @param childBlocks child blocks
+     */
+    AbstractFatherBlock( final Block[] childBlocks )
+    {
+        if ( childBlocks == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+
+        for ( int i = 0; i < childBlocks.length; i++ )
+        {
+            if ( childBlocks[i] == null )
+            {
+                throw new IllegalArgumentException( "bucket " + i + " can't be null" );
+            }
+        }
+        this.childBlocks = childBlocks;
+    }
+
+    /** {@inheritDoc} */
+    public final void traverse( final Sink sink )
+    {
+        before( sink );
+        for ( int i = 0; i < childBlocks.length; i++ )
+        {
+            Block block = childBlocks[i];
+
+            block.traverse( sink );
+        }
+        after( sink );
+    }
+
+    /**
+     * Returns the childBlocks.
+     *
+     * @return <code>Block[]</code> with the childBlocks.
+     */
+    public final Block[] getBlocks()
+    {
+        return childBlocks;
+    }
+
+    /** {@inheritDoc}*/
+    public boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( obj == this )
+        {
+            ret = true;
+        }
+        else if ( obj == null )
+        {
+            ret = false;
+        }
+        else if ( obj.getClass().equals( this.getClass() ) )
+        {
+            if ( obj instanceof AbstractFatherBlock )
+            {
+                final AbstractFatherBlock a = (AbstractFatherBlock) obj;
+                ret = Arrays.equals( a.childBlocks, this.childBlocks );
+            }
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc}*/
+    public int hashCode()
+    {
+        int result = 1;
+        if ( childBlocks != null )
+        {
+            for ( int i = 0; i < childBlocks.length; i++ )
+            {
+                result += childBlocks[i].hashCode();
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/AnchorBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/AnchorBlock.java
new file mode 100644
index 0000000..3ad5fe7
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/AnchorBlock.java
@@ -0,0 +1,89 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Represents an anchor
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: AnchorBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class AnchorBlock
+    implements Block
+{
+    /**
+     * anchor name
+     */
+    private final String name;
+
+    /**
+     * Creates the AnchorBlock.
+     *
+     * @param name name of the anchor, not null.
+     */
+    AnchorBlock( final String name )
+    {
+        if ( name == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+        this.name = name;
+    }
+
+    /** {@inheritDoc}*/
+    public final void traverse( final Sink sink )
+    {
+        sink.anchor( name );
+        sink.anchor_();
+    }
+
+    /** {@inheritDoc}*/
+    public final boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( obj == this )
+        {
+            ret = true;
+        }
+        else if ( obj instanceof AnchorBlock )
+        {
+            final AnchorBlock a = (AnchorBlock) obj;
+
+            ret = name.equals( a.name );
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc}*/
+    public final int hashCode()
+    {
+        return name.hashCode();
+    }
+
+    /** {@inheritDoc}*/
+    public String toString()
+    {
+        return name;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/Block.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/Block.java
new file mode 100644
index 0000000..e634311
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/Block.java
@@ -0,0 +1,40 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Document objet model: we parse the document to a bunch of these.
+ * <p/>
+ * Implementators should implement equals() and hashCode() to ease testing
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: Block.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+public interface Block
+{
+    /**
+     * Traverse the block
+     *
+     * @param sink the sink that travers
+     */
+    void traverse( final Sink sink );
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/BlockParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/BlockParser.java
new file mode 100644
index 0000000..18b1d34
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/BlockParser.java
@@ -0,0 +1,51 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Parse a twiki syntax block
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: BlockParser.java 785531 2009-06-17 09:47:59Z ltheussl $
+ */
+public interface BlockParser
+{
+    /**
+     * <p>accept.</p>
+     *
+     * @param line text line
+     * @return <code>true</code> if this class can handle this line
+     */
+    boolean accept( String line );
+
+    /**
+     * <p>visit.</p>
+     *
+     * @param line   a line of text
+     * @param source the source of lines
+     * @return a block
+     * @throws org.apache.maven.doxia.parser.ParseException on error
+     */
+    Block visit( String line, ByLineSource source )
+        throws ParseException;
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/BoldBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/BoldBlock.java
new file mode 100644
index 0000000..4564679
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/BoldBlock.java
@@ -0,0 +1,54 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that represents the bold text format
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: BoldBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class BoldBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the BoldBlock.
+     *
+     * @param childBlocks child blocks
+     */
+    public BoldBlock( final Block[] childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.bold();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.bold_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/FormatedTextParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/FormatedTextParser.java
new file mode 100644
index 0000000..f68d6f1
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/FormatedTextParser.java
@@ -0,0 +1,311 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Parse looking for formated text (bold, italic, ...)
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: FormatedTextParser.java 709605 2008-10-31 23:57:03Z hboutemy $
+ */
+public class FormatedTextParser
+{
+    /**
+     * parser used to parse text...
+     */
+    private TextParser textParser;
+
+    /**
+     * map used to create blocks dependening on the text format
+     */
+    private static final Map FACTORY_MAP = new HashMap();
+
+    /**
+     * creates bold blocks
+     */
+    private static final FormatBlockFactory BOLD_FACTORY = new FormatBlockFactory()
+    {
+        /** {@inheritDoc} */
+        public Block createBlock( final Block[] childrens )
+        {
+            return new BoldBlock( childrens );
+        }
+    };
+
+    /**
+     * creates italic blocks
+     */
+    private static final FormatBlockFactory ITALIC_FACTORY = new FormatBlockFactory()
+    {
+        /** {@inheritDoc} */
+        public Block createBlock( final Block[] childrens )
+        {
+            return new ItalicBlock( childrens );
+        }
+    };
+
+    /**
+     * creates monospaced blocks
+     */
+    private static final FormatBlockFactory MONOSPACED_FACTORY = new FormatBlockFactory()
+    {
+        /** {@inheritDoc} */
+        public Block createBlock( final Block[] childrens )
+        {
+            return new MonospaceBlock( childrens );
+        }
+    };
+
+    /**
+     * creates bold italic blocks
+     */
+    private static final FormatBlockFactory BOLDITALIC_FACTORY = new FormatBlockFactory()
+    {
+        /** {@inheritDoc} */
+        public Block createBlock( final Block[] childrens )
+        {
+            return new BoldBlock( new Block[] { new ItalicBlock( childrens ) } );
+        }
+    };
+
+    /**
+     * creates bold monospace blocks
+     */
+    private static final FormatBlockFactory BOLDMONO_FACTORY = new FormatBlockFactory()
+    {
+        /** {@inheritDoc} */
+        public Block createBlock( final Block[] childrens )
+        {
+            return new BoldBlock( new Block[] { new MonospaceBlock( childrens ) } );
+        }
+    };
+
+    /**
+     * format characters
+     */
+    private static final String[] SPECIAL_CHAR = new String[] { "__", "==", "*", "_", "=" };
+
+    static
+    {
+        FACTORY_MAP.put( "*", BOLD_FACTORY );
+        FACTORY_MAP.put( "_", ITALIC_FACTORY );
+        FACTORY_MAP.put( "=", MONOSPACED_FACTORY );
+        FACTORY_MAP.put( "__", BOLDITALIC_FACTORY );
+        FACTORY_MAP.put( "==", BOLDMONO_FACTORY );
+    }
+
+    /**
+     * @param line line to parse
+     * @return TextBlock, ItalicBlock, BoldBlock, MonospacedBlock, ...
+     */
+    final Block[] parse( final String line )
+    {
+        return (Block[]) parseFormat( line ).toArray( new Block[] {} );
+    }
+
+    /**
+     * @param c character to test
+     * @return <code>true</code> if c is a space character
+     */
+    static boolean isSpace( final char c )
+    {
+        return c == ' ' || c == '\t';
+    }
+
+    /**
+     * @param c character to test
+     * @return <code>true</code> if c is a character that limits the formats
+     */
+    static boolean isSpecial( final char c )
+    {
+        boolean ret = false;
+
+        for ( int i = 0; !ret && i < SPECIAL_CHAR.length; i++ )
+        {
+            if ( SPECIAL_CHAR[i].charAt( 0 ) == c )
+            {
+                ret = true;
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Parse text format (bold, italic...)
+     * <p/>
+     * TODO too many lines!!
+     *
+     * @param line line to parse
+     * @return list of blocks
+     */
+    private List parseFormat( final String line )
+    {
+        final List ret = new ArrayList();
+        final int[] lhOffsets = new int[SPECIAL_CHAR.length];
+        final int[] rhOffsets = new int[SPECIAL_CHAR.length];
+
+        // for each text format markers...
+        for ( int i = 0; i < SPECIAL_CHAR.length; i++ )
+        {
+            final int specialLen = SPECIAL_CHAR[i].length();
+            int t = 0;
+            // search the nearset instance of this marker...
+            while ( t != -1 && ( t = line.indexOf( SPECIAL_CHAR[i], t ) ) != -1 )
+            {
+                // and check if it at the begining of a word.
+                if ( t == 0 || isSpace( line.charAt( t - 1 ) ) || isParenthesis( line.charAt( t - 1 ) ) )
+                {
+                    // if it is, and if, check to avoid going beyond the string
+                    if ( t + specialLen < line.length() )
+                    {
+                        // and if character after the format marker is another
+                        // marker, is an error, and should be ignored
+                        if ( isSpecial( line.charAt( t + specialLen ) ) )
+                        {
+                            t += specialLen;
+                        }
+                        else
+                        {
+                            // else we find a starter!
+                            break;
+                        }
+                    }
+                    else
+                    {
+                        t = -1;
+                    }
+                }
+                else
+                {
+                    t += specialLen;
+                }
+            }
+            lhOffsets[i] = t;
+        }
+
+        // for each text format markers...
+        for ( int i = 0; i < lhOffsets.length; i++ )
+        {
+            final int specialLen = SPECIAL_CHAR[i].length();
+            // if we found a text format beginning
+            if ( lhOffsets[i] != -1 )
+            {
+                int t = lhOffsets[i] + specialLen;
+                // search for a text format ending
+                while ( ( t = line.indexOf( SPECIAL_CHAR[i], t ) ) != -1 )
+                {
+                    // must be side by side to a word
+                    final char c = line.charAt( t - 1 );
+                    if ( t > 0 && !isSpace( c ) && !isSpecial( c ) )
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        t += specialLen;
+                    }
+                }
+                rhOffsets[i] = t;
+            }
+        }
+
+        // find the nearest index
+        int minIndex = -1;
+        int charType = 0;
+        for ( int i = 0; i < lhOffsets.length; i++ )
+        {
+            if ( lhOffsets[i] != -1 && rhOffsets[i] != 1 )
+            {
+                if ( minIndex == -1 || lhOffsets[i] < minIndex )
+                {
+                    if ( rhOffsets[i] > lhOffsets[i] )
+                    {
+                        // ej: "mary *has a little lamb"
+                        minIndex = lhOffsets[i];
+                        charType = i;
+                    }
+                }
+            }
+        }
+
+        if ( minIndex == -1 )
+        {
+            ret.addAll( textParser.parse( line ) );
+        }
+        else
+        {
+            int len = SPECIAL_CHAR[charType].length();
+            ret.addAll( parseFormat( line.substring( 0, minIndex ) ) );
+            ret.add( ( (FormatBlockFactory) FACTORY_MAP.get( SPECIAL_CHAR[charType] ) )
+                     .createBlock( (Block[]) parseFormat( line.substring( minIndex + len, rhOffsets[charType] ) )
+                                   .toArray( new Block[] {} ) ) );
+            ret.addAll( parseFormat( line.substring( rhOffsets[charType] + len ) ) );
+        }
+
+        // profit
+        return ret;
+    }
+
+    /**
+     * @param c character to test
+     * @return <code>true</code> if c is a parenthesis
+     */
+    private boolean isParenthesis( final char c )
+    {
+        return c == '(' || c == ')';
+    }
+
+    /**
+     * Sets the formatTextParser.
+     *
+     * @param textParser text parser to use
+     *                   <code>TextParser</code> with the formatTextParser.
+     */
+    public final void setTextParser( final TextParser textParser )
+    {
+        if ( textParser == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+
+        this.textParser = textParser;
+    }
+}
+
+/**
+ * @author Juan F. Codagnone
+ * @version $Id: FormatedTextParser.java 709605 2008-10-31 23:57:03Z hboutemy $
+ */
+interface FormatBlockFactory
+{
+    /**
+     * factory method of format <code>Block</code>
+     *
+     * @param childrens children of the format block
+     * @return a format block
+     */
+    Block createBlock( final Block[] childrens );
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/GenericListBlockParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/GenericListBlockParser.java
new file mode 100644
index 0000000..eb4a716
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/GenericListBlockParser.java
@@ -0,0 +1,544 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.sink.Sink;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Generic list parser
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: GenericListBlockParser.java 746991 2009-02-23 12:35:46Z vsiveton $
+ */
+public class GenericListBlockParser
+    implements BlockParser
+{
+    static final String EOL = System.getProperty( "line.separator" );
+
+    /**
+     * parser used to create text blocks
+     */
+    private FormatedTextParser formatedTextParser;
+
+    /**
+     * supported patterns
+     */
+    private final Pattern[] patterns = new Pattern[TYPES.length];
+
+    /**
+     * Creates the GenericListBlockParser.
+     */
+    public GenericListBlockParser()
+    {
+        for ( int i = 0; i < TYPES.length; i++ )
+        {
+            patterns[i] = Pattern.compile( "^((   )+)" + TYPES[i].getItemPattern() + "(.*)$" );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final boolean accept( final String line )
+    {
+        boolean ret = false;
+
+        for ( int i = 0; !ret && i < patterns.length; i++ )
+        {
+            ret |= patterns[i].matcher( line ).lookingAt();
+        }
+
+        return ret;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final Block visit( final String line, final ByLineSource source )
+        throws ParseException
+    {
+        final TreeListBuilder treeListBuilder = new TreeListBuilder( formatedTextParser );
+        // new TreeListBuilder(formatedTextParser);
+        String l = line;
+        do
+        {
+            if ( !accept( l ) )
+            {
+                break;
+            }
+
+            for ( int i = 0; i < patterns.length; i++ )
+            {
+                final Matcher m = patterns[i].matcher( l );
+                if ( m.lookingAt() )
+                {
+                    final int numberOfSpaces = 3;
+                    final int textGroup = 3;
+                    assert m.group( 1 ).length() % numberOfSpaces == 0;
+                    final int level = m.group( 1 ).length() / numberOfSpaces;
+                    treeListBuilder.feedEntry( TYPES[i], level, m.group( textGroup ).trim() );
+                    break;
+                }
+            }
+        }
+        while ( ( l = source.getNextLine() ) != null );
+
+        if ( l != null )
+        {
+            source.ungetLine();
+        }
+
+        return treeListBuilder.getBlock();
+    }
+
+    /**
+     * Sets the formatTextParser.
+     *
+     * @param textParser <code>FormatedTextParser</code> with the formatTextParser.
+     */
+    public final void setTextParser( final FormatedTextParser textParser )
+    {
+        if ( textParser == null )
+        {
+            throw new IllegalArgumentException( "formatTextParser can't be null" );
+        }
+        this.formatedTextParser = textParser;
+    }
+
+    interface Type
+    {
+        /**
+         * @return the pattern of the item part of the list regex
+         */
+        String getItemPattern();
+
+        /**
+         * @param items children of the new listblock
+         * @return a new ListBlock
+         */
+        ListBlock createList( final ListItemBlock[] items );
+
+    }
+
+    /**
+     * unordered list
+     */
+    private static final Type LIST = new Type()
+    {
+        /** {@inheritDoc} */
+        public String getItemPattern()
+        {
+            return "[*]";
+        }
+
+        /** {@inheritDoc} */
+        public ListBlock createList( final ListItemBlock[] items )
+        {
+            return new UnorderedListBlock( items );
+        }
+    };
+
+    /**
+     * a.
+     */
+    private static final Type ORDERED_LOWER_ALPHA = new Type()
+    {
+        /** {@inheritDoc} */
+        public String getItemPattern()
+        {
+            return "[a-hj-z][.]";
+        }
+
+        /** {@inheritDoc} */
+        public ListBlock createList( final ListItemBlock[] items )
+        {
+            return new NumeratedListBlock( Sink.NUMBERING_LOWER_ALPHA, items );
+        }
+    };
+
+    /**
+     * A.
+     */
+    private static final Type ORDERED_UPPER_ALPHA = new Type()
+    {
+        /** {@inheritDoc} */
+        public String getItemPattern()
+        {
+            return "[A-HJ-Z][.]";
+        }
+
+        /** {@inheritDoc} */
+        public ListBlock createList( final ListItemBlock[] items )
+        {
+            return new NumeratedListBlock( Sink.NUMBERING_UPPER_ALPHA, items );
+        }
+    };
+
+    /**
+     * 1.
+     */
+    private static final Type ORDERERED_DECIMAL = new Type()
+    {
+        /** {@inheritDoc} */
+        public String getItemPattern()
+        {
+            return "[0123456789][.]";
+        }
+
+        /** {@inheritDoc} */
+        public ListBlock createList( final ListItemBlock[] items )
+        {
+            return new NumeratedListBlock( Sink.NUMBERING_DECIMAL, items );
+        }
+    };
+
+    /**
+     * i.
+     */
+    private static final Type ORDERERED_LOWER_ROMAN = new Type()
+    {
+        /** {@inheritDoc} */
+        public String getItemPattern()
+        {
+            return "[i][.]";
+        }
+
+        /** {@inheritDoc} */
+        public ListBlock createList( final ListItemBlock[] items )
+        {
+            return new NumeratedListBlock( Sink.NUMBERING_LOWER_ROMAN, items );
+        }
+    };
+
+    /**
+     * I.
+     */
+    private static final Type ORDERERED_UPPER_ROMAN = new Type()
+    {
+        /** {@inheritDoc} */
+        public String getItemPattern()
+        {
+            return "[I][.]";
+        }
+
+        /** {@inheritDoc} */
+        public ListBlock createList( final ListItemBlock[] items )
+        {
+            return new NumeratedListBlock( Sink.NUMBERING_UPPER_ROMAN, items );
+        }
+    };
+
+    private static final Type[] TYPES =
+        { LIST, ORDERED_LOWER_ALPHA, ORDERED_UPPER_ALPHA, ORDERERED_DECIMAL, ORDERERED_LOWER_ROMAN,
+            ORDERERED_UPPER_ROMAN };
+
+}
+
+/**
+ * It helps to build
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: GenericListBlockParser.java 746991 2009-02-23 12:35:46Z vsiveton $
+ */
+class TreeListBuilder
+{
+    /**
+     * parser that create text blocks
+     */
+    private final FormatedTextParser textParser;
+
+    /**
+     * tree root
+     */
+    private final TreeComponent root;
+
+    /**
+     * the current element of the tree
+     */
+    private TreeComponent current;
+
+    /**
+     * Creates the TreeListBuilder.
+     *
+     * @param formatTextParser parser that create text blocks
+     * @throws IllegalArgumentException if <code>formatTextParser</code> is null
+     */
+    TreeListBuilder( final FormatedTextParser formatTextParser )
+        throws IllegalArgumentException
+    {
+        if ( formatTextParser == null )
+        {
+            throw new IllegalArgumentException( "argument is null" );
+        }
+        this.textParser = formatTextParser;
+        root = new TreeComponent( null, "root", null );
+        current = root;
+    }
+
+    /**
+     * recibe un nivel y un texto y armar magicamente (manteniendo estado)
+     * el �rbol
+     *
+     * @param type  type of list
+     * @param level indentation level of the item
+     * @param text  text of the item
+     */
+    void feedEntry( final GenericListBlockParser.Type type, final int level, final String text )
+    {
+        final int currentDepth = current.getDepth();
+        final int incomingLevel = level - 1;
+
+        if ( incomingLevel == currentDepth )
+        {
+            // nothing to move
+        }
+        else if ( incomingLevel > currentDepth )
+        {
+            // el actual ahora es el �ltimo que insert�
+            final TreeComponent[] components = current.getChildren();
+            if ( components.length == 0 )
+            {
+                /* for example:
+                 *        * item1
+                 *     * item2
+                 */
+                for ( int i = 0, n = incomingLevel - currentDepth; i < n; i++ )
+                {
+                    current = current.addChildren( "", type );
+                }
+            }
+            else
+            {
+                current = components[components.length - 1];
+            }
+
+        }
+        else
+        {
+            for ( int i = 0, n = currentDepth - incomingLevel; i < n; i++ )
+            {
+                current = current.getFather();
+                if ( current == null )
+                {
+                    throw new IllegalStateException();
+                }
+            }
+        }
+        current.addChildren( text, type );
+    }
+
+    /**
+     * @return a Block for the list that we received
+     */
+    ListBlock getBlock()
+    {
+        return getList( root );
+    }
+
+    /**
+     * Wrapper
+     *
+     * @param tc tree
+     * @return list Block for this tree
+     */
+    private ListBlock getList( final TreeComponent tc )
+    {
+        ListItemBlock[] li = (ListItemBlock[]) getListItems( tc ).toArray( new ListItemBlock[] {} );
+        return tc.getChildren()[0].getType().createList( li );
+    }
+
+    /**
+     * @param tc tree
+     * @return list Block for this tree
+     */
+    private List getListItems( final TreeComponent tc )
+    {
+        final List blocks = new ArrayList();
+
+        for ( int i = 0; i < tc.getChildren().length; i++ )
+        {
+            final TreeComponent child = tc.getChildren()[i];
+
+            Block[] text = new Block[] {};
+            if ( child.getFather() != null )
+            {
+                text = textParser.parse( child.getText() );
+            }
+
+            if ( child.getChildren().length != 0 )
+            {
+                blocks.add( new ListItemBlock( text, getList( child ) ) );
+            }
+            else
+            {
+                blocks.add( new ListItemBlock( text ) );
+            }
+        }
+
+        return blocks;
+    }
+
+    /**
+     * A bidirectional tree node
+     *
+     * @author Juan F. Codagnone
+     * @version $Id: GenericListBlockParser.java 746991 2009-02-23 12:35:46Z vsiveton $
+     */
+    class TreeComponent
+    {
+        /**
+         * childrens
+         */
+        private List children = new ArrayList();
+
+        /**
+         * node text
+         */
+        private String text;
+
+        /**
+         * the father
+         */
+        private TreeComponent father;
+
+        /**
+         * type of the list
+         */
+        private GenericListBlockParser.Type type;
+
+        /**
+         * Creates the TreeComponent.
+         *
+         * @param father Component father
+         * @param text   component text
+         * @param type   component type
+         */
+        TreeComponent( final TreeComponent father, final String text, final GenericListBlockParser.Type type )
+        {
+            this.text = text;
+            this.father = father;
+            this.type = type;
+        }
+
+        /**
+         * @return my childrens
+         */
+        TreeComponent[] getChildren()
+        {
+            return (TreeComponent[]) children.toArray( new TreeComponent[] {} );
+        }
+
+        /**
+         * adds a children node
+         *
+         * @param t     text of the children
+         * @param ttype component type
+         * @return the new node created
+         */
+        TreeComponent addChildren( final String t, final GenericListBlockParser.Type ttype )
+        {
+            if ( t == null || ttype == null )
+            {
+                throw new IllegalArgumentException( "argument is null" );
+            }
+            final TreeComponent ret = new TreeComponent( this, t, ttype );
+            children.add( ret );
+
+            return ret;
+        }
+
+        /**
+         * @return the father
+         */
+        TreeComponent getFather()
+        {
+            return father;
+        }
+
+        /**
+         * @return the node depth in the tree
+         */
+        int getDepth()
+        {
+            int ret = 0;
+
+            TreeComponent c = this;
+
+            while ( ( c = c.getFather() ) != null )
+            {
+                ret++;
+            }
+
+            return ret;
+        }
+
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return toString( "" );
+        }
+
+        /** {@inheritDoc} */
+        public String toString( final String indent )
+        {
+            final StringBuffer sb = new StringBuffer();
+
+            if ( father != null )
+            {
+                sb.append( indent );
+                sb.append( "- " );
+                sb.append( text );
+                sb.append( GenericListBlockParser.EOL );
+            }
+            for ( Iterator it = children.iterator(); it.hasNext(); )
+            {
+                TreeComponent lc = (TreeComponent) it.next();
+                sb.append( lc.toString( indent + "   " ) );
+            }
+            return sb.toString();
+        }
+
+        /**
+         * Returns the text.
+         *
+         * @return <code>String</code> with the text.
+         */
+        String getText()
+        {
+            return text;
+        }
+
+        /**
+         * Returns the type.
+         *
+         * @return <code>Type</code> with the text.
+         */
+        GenericListBlockParser.Type getType()
+        {
+            return type;
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/HRuleBlockParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/HRuleBlockParser.java
new file mode 100644
index 0000000..28be27a
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/HRuleBlockParser.java
@@ -0,0 +1,81 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Block that represents an horizontal rule
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: HRuleBlockParser.java 746991 2009-02-23 12:35:46Z vsiveton $
+ */
+public class HRuleBlockParser
+    implements BlockParser
+{
+    /**
+     * pattern used to detect horizontal rulers
+     */
+    private static final Pattern HRULE_PATTERN = Pattern.compile( "^(---)(-*)(.*)$" );
+
+    /** {@inheritDoc} */
+    public final boolean accept( final String line )
+    {
+        final Matcher m = HRULE_PATTERN.matcher( line );
+        boolean ret = false;
+
+        if ( m.lookingAt() )
+        {
+            final int textGroup = 3;
+            String s = m.group( textGroup );
+            if ( s != null && !s.startsWith( "+" ) )
+            {
+                ret = true;
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final Block visit( final String line, final ByLineSource source )
+        throws ParseException
+    {
+        Block ret = new HorizontalRuleBlock();
+        final Matcher matcher = HRULE_PATTERN.matcher( line );
+        if ( matcher.lookingAt() )
+        {
+            final int textGroup = 3;
+            source.unget( matcher.group( textGroup ) );
+        }
+        else
+        {
+            throw new ParseException( "i was expecting a hruler!" );
+        }
+
+        return ret;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/HorizontalRuleBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/HorizontalRuleBlock.java
new file mode 100644
index 0000000..8e44c15
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/HorizontalRuleBlock.java
@@ -0,0 +1,51 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Represents an horizontal block
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: HorizontalRuleBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class HorizontalRuleBlock
+    implements Block
+{
+    /** {@inheritDoc} */
+    public final void traverse( final Sink sink )
+    {
+        sink.horizontalRule();
+    }
+
+    /** {@inheritDoc} */
+    public final boolean equals( final Object obj )
+    {
+        return obj == this || ( obj != null && getClass().equals( obj.getClass() ) );
+    }
+
+    /** {@inheritDoc} */
+    public final int hashCode()
+    {
+        final int hashCode = 214905655;
+        return hashCode;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ImageBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ImageBlock.java
new file mode 100644
index 0000000..4b4c637
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ImageBlock.java
@@ -0,0 +1,87 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that represents an image.
+ *
+ * @author Christian Nardi
+ * @version $Id: ImageBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class ImageBlock
+    implements Block
+{
+    /**
+     * img reference
+     */
+    private final String reference;
+
+    /**
+     * Creates the ImageBlock.
+     *
+     * @param imgReference img reference
+     * @throws IllegalArgumentException if the argument is <code>null</code>
+     */
+    ImageBlock( final String imgReference )
+    {
+        if ( imgReference == null )
+        {
+            throw new IllegalArgumentException( "arguments can't be null" );
+        }
+        this.reference = imgReference;
+    }
+
+    /** {@inheritDoc} */
+    public final void traverse( final Sink sink )
+    {
+        sink.figure();
+        sink.figureGraphics( reference );
+        sink.figure_();
+    }
+
+    /** {@inheritDoc} */
+    public final boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( obj == this )
+        {
+            ret = true;
+        }
+        else if ( obj instanceof ImageBlock )
+        {
+            final ImageBlock l = (ImageBlock) obj;
+            ret = reference.equals( l.reference );
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    public final int hashCode()
+    {
+        final int magic1 = 17;
+        final int magic2 = 37;
+
+        return magic1 + magic2 * reference.hashCode();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ItalicBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ItalicBlock.java
new file mode 100644
index 0000000..a1e1d3a
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ItalicBlock.java
@@ -0,0 +1,54 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that represents the italic text format
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: ItalicBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class ItalicBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the BoldBlock.
+     *
+     * @param childBlocks child blocks
+     */
+    ItalicBlock( final Block[] childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.italic();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.italic_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/LinkBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/LinkBlock.java
new file mode 100644
index 0000000..a135ec5
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/LinkBlock.java
@@ -0,0 +1,106 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that represents a link.
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: LinkBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class LinkBlock
+    implements Block
+{
+    /**
+     * link reference
+     */
+    private final String reference;
+
+    /**
+     * link text
+     */
+    private final Block content;
+
+    /**
+     * Creates the LinkBlock.
+     *
+     * @param reference reference anchor
+     * @param text text to display
+     * @deprecated
+     */
+    LinkBlock( final String reference, final String text )
+    {
+        this( reference, new TextBlock( text ) );
+    }
+
+    /**
+     * Creates the LinkBlock.
+     *
+     * @param reference reference anchor, not null.
+     * @param content block with the displayed content, not null.
+     */
+    LinkBlock( final String reference, final Block content )
+    {
+        if ( reference == null || content == null )
+        {
+            throw new IllegalArgumentException( "arguments can't be null" );
+        }
+        this.reference = reference;
+        this.content = content;
+    }
+
+    /** {@inheritDoc} */
+    public final void traverse( final Sink sink )
+    {
+        sink.link( reference );
+        content.traverse( sink );
+        sink.link_();
+
+    }
+
+    /** {@inheritDoc} */
+    public final boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( obj == this )
+        {
+            ret = true;
+        }
+        else if ( obj instanceof LinkBlock )
+        {
+            final LinkBlock l = (LinkBlock) obj;
+            ret = reference.equals( l.reference ) && content.equals( l.content );
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    public final int hashCode()
+    {
+        final int magic1 = 17;
+        final int magic2 = 37;
+
+        return magic1 + magic2 * reference.hashCode() + magic2 * content.hashCode();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ListBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ListBlock.java
new file mode 100644
index 0000000..3b34279
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ListBlock.java
@@ -0,0 +1,45 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * <pre>
+ *    - item1
+ *    - item2
+ *        - item2.1
+ *        ...
+ * </pre>
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: ListBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+abstract class ListBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the ListBlock.
+     *
+     * @param blocks list of list items, not null.
+     */
+    ListBlock( final ListItemBlock[] blocks )
+    {
+        super( blocks );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ListItemBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ListItemBlock.java
new file mode 100644
index 0000000..05b5d47
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ListItemBlock.java
@@ -0,0 +1,122 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that represents the item in a list
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: ListItemBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class ListItemBlock
+    extends AbstractFatherBlock
+{
+    private final ListBlock innerList;
+
+    /**
+     * @see #ListItemBlock(Block[], ListBlock)
+     */
+    ListItemBlock( final Block[] blocks )
+    {
+        this( blocks, null );
+    }
+
+    /**
+     * Creates the ListItemBlock.
+     *
+     * @param blocks    text block, not null.
+     * @param innerList child list
+     */
+    ListItemBlock( final Block[] blocks, final ListBlock innerList )
+    {
+        super( blocks );
+        this.innerList = innerList;
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.listItem();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        if ( innerList != null )
+        {
+            innerList.traverse( sink );
+        }
+        sink.listItem_();
+    }
+
+    /**
+     * Returns the innerList.
+     *
+     * @return <code>UnorderedListBlock</code> with the innerList.
+     */
+    final ListBlock getInnerList()
+    {
+        return innerList;
+    }
+
+    /** {@inheritDoc} */
+    public final boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( obj == this )
+        {
+            ret = true;
+        }
+        else if ( obj == null || this == null )
+        {
+            ret = false;
+        }
+        else if ( obj instanceof ListItemBlock )
+        {
+            final ListItemBlock li = (ListItemBlock) obj;
+            if ( this.innerList == null && li.innerList == null )
+            {
+                ret = super.equals( li );
+            }
+            else if ( this.innerList == null && li.innerList != null )
+            {
+                ret = false;
+            }
+            else
+            {
+                ret = this.innerList.equals( li.innerList ) && super.equals( li );
+            }
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    public final int hashCode()
+    {
+        final int magic1 = 17;
+        final int magic2 = 37;
+
+        return magic1 + magic2 * super.hashCode() + ( innerList == null ? 0 : magic2 * innerList.hashCode() );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/MonospaceBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/MonospaceBlock.java
new file mode 100644
index 0000000..962ee1a
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/MonospaceBlock.java
@@ -0,0 +1,54 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that represents the monospaced text format
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: MonospaceBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class MonospaceBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the MonospaceBlock.
+     *
+     * @param childBlocks child blocks
+     */
+    MonospaceBlock( final Block[] childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.monospaced();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.monospaced_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/NopBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/NopBlock.java
new file mode 100644
index 0000000..782f2b4
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/NopBlock.java
@@ -0,0 +1,51 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that not represent anything
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: NopBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class NopBlock
+    implements Block
+{
+    /** {@inheritDoc} */
+    public final void traverse( final Sink sink )
+    {
+        // nothing to do!!
+    }
+
+    /** {@inheritDoc} */
+    public final boolean equals( final Object obj )
+    {
+        return this == obj && getClass().equals( obj.getClass() );
+    }
+
+    /** {@inheritDoc} */
+    public final int hashCode()
+    {
+        final int magic = 518409602;
+        return magic;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/NumeratedListBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/NumeratedListBlock.java
new file mode 100644
index 0000000..d24aaff
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/NumeratedListBlock.java
@@ -0,0 +1,86 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * <pre>
+ *    1. item1
+ *    2. item2
+ *        - item2.1
+ *        ...
+ * </pre>
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: NumeratedListBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class NumeratedListBlock
+    extends ListBlock
+{
+    /**
+     * order item type. one of Sink#NUMBERING_....
+     */
+    private final int type;
+
+    /**
+     * Creates the UnorderedListBlock.
+     *
+     * @param type   order item type. one of Sink#NUMBERING_....
+     * @param blocks list of list items, not null.
+     */
+    NumeratedListBlock( final int type, final ListItemBlock[] blocks )
+    {
+        super( blocks );
+        this.type = type;
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.numberedList( type );
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.numberedList_();
+    }
+
+    /** {@inheritDoc} */
+    public final boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( super.equals( obj ) )
+        {
+            ret = type == ( (NumeratedListBlock) obj ).type;
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    public final int hashCode()
+    {
+        final int magic = 17;
+        return super.hashCode() + magic * type;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ParagraphBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ParagraphBlock.java
new file mode 100644
index 0000000..825486c
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ParagraphBlock.java
@@ -0,0 +1,47 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @author Juan F. Codagnone
+ * @version $Id: ParagraphBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class ParagraphBlock
+    extends AbstractFatherBlock
+{
+    ParagraphBlock( final Block[] blocks )
+    {
+        super( blocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.paragraph();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.paragraph_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ParagraphBlockParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ParagraphBlockParser.java
new file mode 100644
index 0000000..6deaadf
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/ParagraphBlockParser.java
@@ -0,0 +1,267 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Parse paragraphs.
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: ParagraphBlockParser.java 746991 2009-02-23 12:35:46Z vsiveton $
+ */
+public class ParagraphBlockParser
+    implements BlockParser
+{
+    /**
+     * pattern used to dectect end of paragraph
+     */
+    private final Pattern paragraphSeparator = Pattern.compile( "^(\\s*)$" );
+
+    /**
+     * {@link SectionBlockParser} to use. injected
+     */
+    private SectionBlockParser sectionParser;
+
+    /**
+     * {@link ListBlockParser} to use. injected
+     */
+    private GenericListBlockParser listParser;
+
+    /**
+     * {@link FormatedTextParser} to use. injected
+     */
+    private FormatedTextParser textParser;
+
+    /**
+     * {@link HRuleBlockParser} to use. injected
+     */
+    private HRuleBlockParser hrulerParser;
+
+    /**
+     * {@link TableBlockParser} to use. injected
+     */
+    private TableBlockParser tableBlockParser;
+
+    /**
+     *  {@link TableBlockParser} to use. injected
+     */
+    private VerbatimBlockParser verbatimParser;
+
+    /**
+     * no operation block
+     */
+    private static final NopBlock NOP = new NopBlock();
+
+    /** {@inheritDoc} */
+    public final boolean accept( final String line )
+    {
+        return !sectionParser.accept( line ) && !hrulerParser.accept( line ) && !verbatimParser.accept( line );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final Block visit( final String line, final ByLineSource source )
+        throws ParseException
+    {
+        StringBuffer sb = new StringBuffer();
+        List childs = new ArrayList();
+
+        boolean sawText = false;
+
+        /*
+        * 1. Skip begininig new lines
+        * 2. Get the text, while \n\n is not found
+        */
+        boolean pre = false;
+        String l = line;
+        do
+        {
+            Matcher m = paragraphSeparator.matcher( l );
+
+            if ( m.lookingAt() )
+            {
+                if ( sawText )
+                {
+                    break;
+                }
+            }
+            else
+            {
+                sawText = true;
+
+                /* be able to parse lists / enumerations */
+                if ( listParser.accept( l ) )
+                {
+                    if ( sb.length() != 0 )
+                    {
+                        childs.addAll( Arrays.asList( textParser.parse( sb.toString().trim() ) ) );
+                        sb = new StringBuffer();
+                    }
+                    childs.add( listParser.visit( l, source ) );
+                }
+                else if ( tableBlockParser.accept( l ) )
+                {
+                    childs.add( tableBlockParser.visit( l, source ) );
+                }
+                else
+                {
+                    sb.append( l );
+                    // specific
+                    if ( l.indexOf( "<pre>" ) != -1 )
+                    {
+                        pre = true;
+                    }
+                    if ( l.indexOf( "</pre>" ) != -1 )
+                    {
+                        pre = false;
+                    }
+
+                    if ( !pre )
+                    {
+                        sb.append( " " );
+                    }
+                    else
+                    {
+                        // TODO use EOL
+                        sb.append( "\n" );
+                    }
+                }
+            }
+            l = source.getNextLine();
+        }
+        while ( l != null && accept( l ) );
+
+        if ( line != null )
+        {
+            source.ungetLine();
+        }
+
+        if ( sb.length() != 0 )
+        {
+            childs.addAll( Arrays.asList( textParser.parse( sb.toString().trim() ) ) );
+            sb = new StringBuffer();
+        }
+
+        if ( childs.size() == 0 )
+        {
+            return NOP;
+        }
+
+        return new ParagraphBlock( (Block[]) childs.toArray( new Block[] {} ) );
+    }
+
+    /**
+     * Sets the sectionParser.
+     *
+     * @param aSectionParser <code>SectionBlockParser</code> with the sectionParser.
+     */
+    public final void setSectionParser( final SectionBlockParser aSectionParser )
+    {
+        if ( aSectionParser == null )
+        {
+            throw new IllegalArgumentException( "arg can't be null" );
+        }
+        this.sectionParser = aSectionParser;
+    }
+
+    /**
+     * Sets the listParser.
+     *
+     * @param aListParser <code>ListBlockParser</code> with the listParser.
+     */
+    public final void setListParser( final GenericListBlockParser aListParser )
+    {
+        if ( aListParser == null )
+        {
+            throw new IllegalArgumentException( "arg can't be null" );
+        }
+
+        this.listParser = aListParser;
+    }
+
+    /**
+     * Sets the formatTextParser.
+     *
+     * @param aTextParser <code>FormatedTextParser</code>
+     *                   with the formatTextParser.
+     */
+    public final void setTextParser( final FormatedTextParser aTextParser )
+    {
+        if ( aTextParser == null )
+        {
+            throw new IllegalArgumentException( "arg can't be null" );
+        }
+        this.textParser = aTextParser;
+    }
+
+    /**
+     * Sets the hrulerParser.
+     *
+     * @param aHrulerParser <code>HRuleBlockParser</code> with the hrulerParser.
+     */
+    public final void setHrulerParser( final HRuleBlockParser aHrulerParser )
+    {
+        if ( aHrulerParser == null )
+        {
+            throw new IllegalArgumentException( "arg can't be null" );
+        }
+
+        this.hrulerParser = aHrulerParser;
+    }
+
+    /**
+     * <p>Setter for the field <code>tableBlockParser</code>.</p>
+     *
+     * @param aTableBlockParser Table parser to use
+     */
+    public final void setTableBlockParser( final TableBlockParser aTableBlockParser )
+    {
+        if ( aTableBlockParser == null )
+        {
+            throw new IllegalArgumentException( "arg can't be null" );
+        }
+
+        this.tableBlockParser = aTableBlockParser;
+    }
+
+    /**
+     * Sets the verbatimParser.
+     *
+     * @param aVerbatimParser <code>VerbatimBlockParser</code> with the verbatimParser.
+     * @since 1.1
+     */
+    public final void setVerbatimParser( final VerbatimBlockParser aVerbatimParser )
+    {
+        if ( aVerbatimParser == null )
+        {
+            throw new IllegalArgumentException( "arg can't be null" );
+        }
+        this.verbatimParser = aVerbatimParser;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/SectionBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/SectionBlock.java
new file mode 100644
index 0000000..7c1e3e9
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/SectionBlock.java
@@ -0,0 +1,177 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that represents a section
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: SectionBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+public class SectionBlock
+    extends AbstractFatherBlock
+{
+    /** {@inheritDoc} */
+    private final String title;
+
+    /** {@inheritDoc} */
+    private final int level;
+
+    /**
+     * Creates the SectionBlock.
+     * <p/>
+     * No parameter can be <code>null</code>
+     *
+     * @param title  the section title.
+     * @param level  the section level: 0 < level < 6
+     * @param blocks child blocks
+     */
+    public SectionBlock( final String title, final int level, final Block[] blocks )
+    {
+        super( blocks );
+        final int maxLevel = 5;
+        if ( title == null )
+        {
+            throw new IllegalArgumentException( "title cant be null" );
+        }
+        else if ( level < 1 || level > maxLevel )
+        {
+            throw new IllegalArgumentException( "invalid level: " + level );
+        }
+
+        this.title = title;
+        this.level = level;
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sectionStart( sink );
+        sectionTitle( sink );
+        sink.text( title );
+        sectionTitle_( sink );
+
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sectionEnd( sink );
+    }
+
+    /**
+     * call to sink.section<Level>()
+     *
+     * @param sink sink
+     */
+    private void sectionStart( final Sink sink )
+    {
+        invokeVoidVoid( sink, "section" + level );
+    }
+
+    /**
+     * call to sink.section<Level>_()
+     *
+     * @param sink sink
+     */
+    private void sectionEnd( final Sink sink )
+    {
+        invokeVoidVoid( sink, "section" + level + "_" );
+    }
+
+    /**
+     * Let you call sink's methods that returns <code>null</code> and have
+     * no parameters.
+     *
+     * @param sink the Sink
+     * @param name the name of the method to call
+     */
+    private void invokeVoidVoid( final Sink sink, final String name )
+    {
+        try
+        {
+            final Method m = sink.getClass().getMethod( name, new Class[] {} );
+            m.invoke( sink, Collections.EMPTY_LIST.toArray() );
+        }
+        catch ( Exception e )
+        {
+            // FIXME
+            throw new IllegalArgumentException( "invoking sink's " + name + " method: " + e.getMessage() );
+        }
+    }
+
+    /**
+     * Returns the level.
+     *
+     * @return <code>int</code> with the level.
+     */
+    public final int getLevel()
+    {
+        return level;
+    }
+
+    /**
+     * Returns the title.
+     *
+     * @return <code>String</code> with the title.
+     */
+    public final String getTitle()
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    public final String toString()
+    {
+        final StringBuffer sb = new StringBuffer();
+
+        sb.append( "Section  {title: '" );
+        sb.append( getTitle() );
+        sb.append( "' level: " );
+        sb.append( getLevel() );
+        sb.append( "}: [" );
+        for ( int i = 0; i < getBlocks().length; i++ )
+        {
+            final Block block = getBlocks()[i];
+
+            sb.append( block.toString() );
+            sb.append( ", " );
+        }
+        sb.append( "]" );
+        return sb.toString();
+    }
+
+    /** @param sink */
+    private void sectionTitle( final Sink sink )
+    {
+        invokeVoidVoid( sink, "sectionTitle" + level );
+    }
+
+    /** @param sink */
+    private void sectionTitle_( final Sink sink )
+    {
+        invokeVoidVoid( sink, "sectionTitle" + level + "_" );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/SectionBlockParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/SectionBlockParser.java
new file mode 100644
index 0000000..1b16268
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/SectionBlockParser.java
@@ -0,0 +1,167 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Parse looking for sections
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: SectionBlockParser.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+public class SectionBlockParser
+    implements BlockParser
+{
+    /**
+     * '---++ Header', '---## Header'
+     */
+    private static final Pattern HEADER_DA = Pattern.compile( "^---([+]+)\\s*(.+)\\s*$" );
+
+    /**
+     * {@link ParagraphBlockParser} to use. injected
+     */
+    private ParagraphBlockParser paraParser;
+
+    /**
+     * {@link ParagraphBlockParser} to use. injected
+     */
+    private HRuleBlockParser hrulerParser;
+
+    /** {@link VerbatimBlockParser} */
+    private VerbatimBlockParser verbatimBlockParser;
+
+    /**
+     * {@inheritDoc}
+     */
+    public final boolean accept( final String line )
+    {
+        return HEADER_DA.matcher( line ).lookingAt();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final Block visit( final String line, final ByLineSource source )
+        throws ParseException
+    {
+        final Matcher m = HEADER_DA.matcher( line );
+
+        if ( !m.lookingAt() )
+        {
+            throw new IllegalArgumentException( "don't know how to handle: " + line );
+        }
+
+        String newLine;
+        final ArrayList blocks = new ArrayList();
+
+        while ( ( newLine = source.getNextLine() ) != null && !accept( newLine ) )
+        {
+            if ( hrulerParser.accept( newLine ) )
+            {
+                blocks.add( hrulerParser.visit( newLine, source ) );
+            }
+            else
+            {
+                if ( verbatimBlockParser.accept( newLine ) )
+                {
+                    blocks.add( verbatimBlockParser.visit( newLine, source ) );
+                }
+                else
+                {
+                    blocks.add( paraParser.visit( newLine, source ) );
+                }
+            }
+        }
+
+        if ( newLine != null )
+        {
+            source.ungetLine();
+        }
+
+        return new SectionBlock( m.group( 2 ), getLevel( m.group( 1 ) ), (Block[]) blocks.toArray( new Block[] {} ) );
+    }
+
+    /**
+     * @param s "++"
+     * @return tha level of the section
+     * @throws IllegalArgumentException on error
+     */
+    static int getLevel( final String s )
+        throws IllegalArgumentException
+    {
+        for ( int i = 0, n = s.length(); i < n; i++ )
+        {
+            if ( s.charAt( i ) != '+' )
+            {
+                throw new IllegalArgumentException( "the argument must have only" + " '+' characters" );
+            }
+        }
+        return s.length();
+    }
+
+    /**
+     * Sets the paraParser.
+     *
+     * @param paraParser <code>ParagraphBlockParser</code> with the paraParser.
+     */
+    public final void setParaParser( final ParagraphBlockParser paraParser )
+    {
+        if ( paraParser == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+        this.paraParser = paraParser;
+    }
+
+    /**
+     * Sets the hrulerParser.
+     *
+     * @param hrulerParser <code>HRuleBlockParser</code> with the hrulerParser.
+     */
+    public final void setHrulerParser( final HRuleBlockParser hrulerParser )
+    {
+        if ( hrulerParser == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+        this.hrulerParser = hrulerParser;
+    }
+
+    /**
+     * Sets the verbatimBlockParser.
+     *
+     * @param verbatimBlockParser <code>VerbatimBlockParser</code> with the verbatimBlockParser.
+     * @since 1.1
+     */
+    public final void setVerbatimBlockParser( VerbatimBlockParser verbatimBlockParser )
+    {
+        if ( verbatimBlockParser == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+        this.verbatimBlockParser = verbatimBlockParser;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableBlock.java
new file mode 100644
index 0000000..8031ba9
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableBlock.java
@@ -0,0 +1,67 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Represents a table
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TableBlock.java 709605 2008-10-31 23:57:03Z hboutemy $
+ */
+class TableBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the TableBlock.
+     *
+     * @param childBlocks child blocks
+     */
+    public TableBlock( final Block[] childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.table();
+        sink.tableRows( getJustification(), false );
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.tableRows_();
+        sink.table_();
+    }
+
+    private int[] getJustification()
+    {
+        int[] justification = new int[( (AbstractFatherBlock) getBlocks()[0] ).getBlocks().length];
+        for ( int i = 0; i < justification.length; i++ )
+        {
+            justification[i] = Sink.JUSTIFY_CENTER;
+        }
+
+        return justification;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableBlockParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableBlockParser.java
new file mode 100644
index 0000000..a83e755
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableBlockParser.java
@@ -0,0 +1,115 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Parse tables
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TableBlockParser.java 746991 2009-02-23 12:35:46Z vsiveton $
+ */
+public class TableBlockParser
+    implements BlockParser
+{
+    /**
+     * pattern to detect tables
+     */
+    private static final Pattern TABLE_PATTERN = Pattern.compile( "^\\s*([|].*[|])+\\s*$" );
+
+    /**
+     * text parser
+     */
+    private FormatedTextParser textParser;
+
+    /** {@inheritDoc} */
+    public final boolean accept( final String line )
+    {
+        return TABLE_PATTERN.matcher( line ).lookingAt();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final Block visit( final String line, final ByLineSource source )
+        throws ParseException
+    {
+        if ( !accept( line ) )
+        {
+            throw new IllegalAccessError( "call accept before this ;)" );
+        }
+
+        final List rows = new ArrayList();
+        String l = line;
+
+        do
+        {
+            final Matcher m = TABLE_PATTERN.matcher( l );
+            if ( m.lookingAt() )
+            {
+                final List cells = new ArrayList();
+
+                /* for each cell... */
+                for ( int lh = l.indexOf( '|' ) + 1, rh; ( rh = l.indexOf( '|', lh ) ) != -1; lh = rh + 1 )
+                {
+                    final Block[] bs = textParser.parse( l.substring( lh, rh ).trim() );
+                    if ( bs.length == 1 && bs[0] instanceof BoldBlock )
+                    {
+                        final Block[] tmp = ( (BoldBlock) bs[0] ).getBlocks();
+                        cells.add( new TableCellHeaderBlock( tmp ) );
+                    }
+                    else
+                    {
+                        cells.add( new TableCellBlock( bs ) );
+                    }
+                }
+                rows.add( new TableRowBlock( (Block[]) cells.toArray( new Block[] {} ) ) );
+            }
+
+        }
+        while ( ( l = source.getNextLine() ) != null && accept( l ) );
+
+        assert rows.size() >= 1;
+
+        return new TableBlock( (Block[]) rows.toArray( new Block[] {} ) );
+    }
+
+    /**
+     * <p>Setter for the field <code>textParser</code>.</p>
+     *
+     * @param textParser text parser to be set
+     */
+    public final void setTextParser( final FormatedTextParser textParser )
+    {
+        if ( textParser == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+
+        this.textParser = textParser;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableCellBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableCellBlock.java
new file mode 100644
index 0000000..7c10fc6
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableCellBlock.java
@@ -0,0 +1,54 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Table cell
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TableCellBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class TableCellBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the TableCellBlock.
+     *
+     * @param childBlocks childrens
+     */
+    TableCellBlock( final Block[] childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.tableCell();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.tableCell_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableCellHeaderBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableCellHeaderBlock.java
new file mode 100644
index 0000000..c6bfdab
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableCellHeaderBlock.java
@@ -0,0 +1,54 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Table Cell headear
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TableCellHeaderBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class TableCellHeaderBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the TableCellHeaderBlock.
+     *
+     * @param childBlocks childrens
+     */
+    TableCellHeaderBlock( final Block[] childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.tableHeaderCell();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.tableHeaderCell_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableRowBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableRowBlock.java
new file mode 100644
index 0000000..885b486
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TableRowBlock.java
@@ -0,0 +1,54 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Represens a Table Row
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TableRowBlock.java 705065 2008-10-15 21:46:08Z vsiveton $
+ */
+class TableRowBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the TableRowBlock.
+     *
+     * @param childBlocks children blocks
+     */
+    TableRowBlock( final Block[] childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.tableRow();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.tableRow_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TextBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TextBlock.java
new file mode 100644
index 0000000..99655a2
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TextBlock.java
@@ -0,0 +1,99 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Block that holds plain text
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TextBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class TextBlock
+    implements Block
+{
+    /**
+     * the text
+     */
+    private final String text;
+
+    /**
+     * Creates the TextBlock.
+     *
+     * @param text some text. can't ben <code>null</code>
+     * @throws IllegalArgumentException if parameters are not in the domain
+     */
+    TextBlock( final String text )
+    {
+        if ( text == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+
+        this.text = text;
+    }
+
+    /** {@inheritDoc} */
+    public final void traverse( final Sink sink )
+    {
+        sink.text( text );
+    }
+
+    /** {@inheritDoc} */
+    public final String toString()
+    {
+        return getClass().getName() + ": [" + text.replaceAll( "\n", "\\n" ) + "]";
+    }
+
+    /**
+     * Returns the text.
+     *
+     * @return <code>String</code> with the text.
+     */
+    final String getText()
+    {
+        return text;
+    }
+
+    /** {@inheritDoc} */
+    public final boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( obj == this )
+        {
+            ret = true;
+        }
+        else if ( obj instanceof TextBlock )
+        {
+            final TextBlock textBlock = (TextBlock) obj;
+            ret = text.equals( textBlock.text );
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    public final int hashCode()
+    {
+        return text.hashCode();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TextParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TextParser.java
new file mode 100644
index 0000000..cef1874
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/TextParser.java
@@ -0,0 +1,418 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Parse almost plain text in search of WikiWords, links, ...
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: TextParser.java 785531 2009-06-17 09:47:59Z ltheussl $
+ */
+public class TextParser
+{
+    /**
+     * pattern to detect WikiWords
+     */
+    private static final Pattern WIKIWORD_PATTERN =
+        Pattern.compile( "(!?([A-Z]\\w*[.])?([A-Z][a-z]+){2,}(#\\w*)?)" );
+
+    /**
+     * pattern to detect SpecificLinks links [[reference][text]]
+     */
+    private static final Pattern SPECIFICLINK_PATTERN = Pattern.compile( "!?\\[\\[([^\\]]+)\\]\\[([^\\]]+)\\]\\]" );
+
+    /**
+     * pattern to detect ForcedLinks links [[reference asd]]
+     */
+    private static final Pattern FORCEDLINK_PATTERN = Pattern.compile( "(!)?(\\[\\[(.+)\\]\\])" );
+
+    /**
+     * anchor name
+     */
+    private static final Pattern ANCHOR_PATTERN = Pattern.compile( "#(([A-Z][A-Za-z]*){2,})" );
+
+    /**
+     * url word
+     */
+    private static final Pattern URL_PATTERN = Pattern.compile( "(\\w+):[/][/][^\\s]*" );
+
+    /**
+     *  image pattern specification
+     */
+    private static final Pattern IMAGE_PATTERN = Pattern.compile( "(.*)\\.(png|jpg|gif|bmp)" );
+
+    /**
+     *  image tag pattern specification (used for images at relative URLs)
+     */
+    private static final Pattern IMAGE_TAG_PATTERN =
+        Pattern.compile( "<img\\b.*?\\bsrc=([\"'])(.*?)\\1.*>", Pattern.CASE_INSENSITIVE );
+
+    /** HTML tag pattern */
+    private static final Pattern HTML_TAG_PATTERN = Pattern.compile( "<(/?)([\\w]*)(.*?)(/?)>", Pattern.DOTALL );
+
+    /**
+     * resolves wikiWordLinks
+     */
+    private final WikiWordLinkResolver wikiWordLinkResolver;
+
+    /** resolves noautolink tag */
+    private boolean noautolink;
+
+    /**
+     * Creates the TextParser.
+     *
+     * @param resolver resolver for wikiWord links
+     */
+    public TextParser( final WikiWordLinkResolver resolver )
+    {
+        this.wikiWordLinkResolver = resolver;
+    }
+
+    /**
+     * <p>parse.</p>
+     *
+     * @param line line to parse
+     * @return a list of block that represents the input
+     */
+    public final List parse( final String line )
+    {
+        final List ret = new ArrayList();
+
+        final Matcher linkMatcher = SPECIFICLINK_PATTERN.matcher( line );
+        final Matcher wikiMatcher = WIKIWORD_PATTERN.matcher( line );
+        final Matcher forcedLinkMatcher = FORCEDLINK_PATTERN.matcher( line );
+        final Matcher anchorMatcher = ANCHOR_PATTERN.matcher( line );
+        final Matcher urlMatcher = URL_PATTERN.matcher( line );
+        final Matcher imageTagMatcher = IMAGE_TAG_PATTERN.matcher( line );
+
+        final Matcher tagMatcher = HTML_TAG_PATTERN.matcher( line );
+        Matcher xhtmlMatcher = null;
+        if ( tagMatcher.find() )
+        {
+            String tag = tagMatcher.group( 2 );
+
+            Pattern pattern =
+                Pattern.compile( "(\\<" + tag + ".*\\>)(.*)?(\\<\\/" + tag + "\\>)(.*)?", Pattern.DOTALL );
+            xhtmlMatcher = pattern.matcher( line );
+        }
+
+        if ( xhtmlMatcher != null && xhtmlMatcher.find() )
+        {
+            parseXHTML( line, ret, xhtmlMatcher );
+        }
+        else if ( linkMatcher.find() )
+        {
+            parseLink( line, ret, linkMatcher );
+        }
+        else if ( wikiMatcher.find() && startLikeWord( wikiMatcher, line ) && !noautolink )
+        {
+            parseWiki( line, ret, wikiMatcher );
+        }
+        else if ( forcedLinkMatcher.find() )
+        {
+            parseForcedLink( line, ret, forcedLinkMatcher );
+        }
+        else if ( anchorMatcher.find() && isAWord( anchorMatcher, line ) )
+        {
+            parseAnchor( line, ret, anchorMatcher );
+        }
+        else if ( urlMatcher.find() && isAWord( urlMatcher, line ) )
+        {
+            parseUrl( line, ret, urlMatcher );
+        }
+        else if ( imageTagMatcher.find() )
+        {
+            parseImage( line, ret, imageTagMatcher );
+        }
+        else
+        {
+            if ( line.length() != 0 )
+            {
+                ret.add( new TextBlock( line ) );
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Parses the image tag
+     * @param line the line to parse
+     * @param ret where the results live
+     * @param imageTagMatcher image tag matcher
+     */
+    private void parseImage( final String line, final List ret, final Matcher imageTagMatcher )
+    {
+        ret.addAll( parse( line.substring( 0, imageTagMatcher.start() ) ) );
+        final String src = imageTagMatcher.group( 2 );
+        ret.add( new ImageBlock( src ) );
+        ret.addAll( parse( line.substring( imageTagMatcher.end(), line.length() ) ) );
+    }
+
+    /**
+     * Parses the url
+     * @param line the line to parse
+     * @param ret where the results live
+     * @param urlMatcher url matcher
+     */
+    private void parseUrl( final String line, final List ret, final Matcher urlMatcher )
+    {
+        ret.addAll( parse( line.substring( 0, urlMatcher.start() ) ) );
+        final String url = urlMatcher.group( 0 );
+        final Matcher imageMatcher = IMAGE_PATTERN.matcher( url );
+        if ( imageMatcher.matches() )
+        {
+            ret.add( new ImageBlock( url ) );
+        }
+        else
+        {
+            ret.add( new LinkBlock( url, new TextBlock( url ) ) );
+        }
+        ret.addAll( parse( line.substring( urlMatcher.end(), line.length() ) ) );
+    }
+
+    /**
+     * Parses the anchor
+     * @param line the line to parse
+     * @param ret where the results live
+     * @param anchorMatcher anchor matcher
+     */
+    private void parseAnchor( final String line, final List ret, final Matcher anchorMatcher )
+    {
+        ret.addAll( parse( line.substring( 0, anchorMatcher.start() ) ) );
+        ret.add( new AnchorBlock( anchorMatcher.group( 1 ) ) );
+        ret.addAll( parse( line.substring( anchorMatcher.end(), line.length() ) ) );
+    }
+
+    /**
+     * Parses the link
+     * @param line line to parse
+     * @param ret where the results live
+     * @param forcedLinkMatcher forced link matcher
+     */
+    private void parseForcedLink( final String line, final List ret, final Matcher forcedLinkMatcher )
+    {
+        if ( forcedLinkMatcher.group( 1 ) != null )
+        {
+            ret.add( new TextBlock( forcedLinkMatcher.group( 2 ) ) );
+        }
+        else
+        {
+            final String showText = forcedLinkMatcher.group( 3 );
+            // mailto link:
+            if ( showText.trim().startsWith( "mailto:" ) )
+            {
+                String s = showText.trim();
+                int i = s.indexOf( ' ' );
+                if ( i == -1 )
+                {
+                    ret.add( new TextBlock( s ) );
+                }
+                else
+                {
+                    ret.add( new LinkBlock( s.substring( 0, i ), new TextBlock( s.substring( i ).trim() ) ) );
+                }
+            }
+            else
+            {
+                ret.addAll( parse( line.substring( 0, forcedLinkMatcher.start() ) ) );
+                ret.add( createLink( showText, showText ) );
+                ret.addAll( parse( line.substring( forcedLinkMatcher.end(), line.length() ) ) );
+            }
+        }
+    }
+
+    /**
+     * Decides between a WikiWordBlock or a a LinkBlock
+     * @param link the link text
+     * @param showText the show text.
+     * @return either a WikiWordBlock or a LinkBlock
+     */
+    private Block createLink( final String link, final String showText )
+    {
+        final Block content;
+        if ( URL_PATTERN.matcher( showText ).matches() && IMAGE_PATTERN.matcher( showText ).matches() )
+        {
+            content = new ImageBlock( showText );
+        }
+        else
+        {
+            content = new TextBlock( showText );
+        }
+
+        if ( URL_PATTERN.matcher( link ).matches() )
+        {
+            return new LinkBlock( link, content );
+        }
+
+        final StringTokenizer tokenizer = new StringTokenizer( link );
+        final StringBuffer sb = new StringBuffer();
+
+        while ( tokenizer.hasMoreElements() )
+        {
+            final String s = tokenizer.nextToken();
+            sb.append( s.substring( 0, 1 ).toUpperCase() );
+            sb.append( s.substring( 1 ) );
+        }
+        return new WikiWordBlock( sb.toString(), content, wikiWordLinkResolver );
+    }
+
+    /**
+     * Parses a wiki word
+     * @param line the line to parse
+     * @param ret where the results live
+     * @param wikiMatcher wiki matcher
+     */
+    private void parseWiki( final String line, final List ret, final Matcher wikiMatcher )
+    {
+        final String wikiWord = wikiMatcher.group();
+        ret.addAll( parse( line.substring( 0, wikiMatcher.start() ) ) );
+        if ( wikiWord.startsWith( "!" ) )
+        { // link prevention
+            ret.add( new TextBlock( wikiWord.substring( 1 ) ) );
+        }
+        else
+        {
+            ret.add( new WikiWordBlock( wikiWord, wikiWordLinkResolver ) );
+        }
+        ret.addAll( parse( line.substring( wikiMatcher.end(), line.length() ) ) );
+    }
+
+    /**
+     * Parses a link
+     * @param line the line to parse
+     * @param ret where the results live
+     * @param linkMatcher link matcher
+     */
+    private void parseLink( final String line, final List ret, final Matcher linkMatcher )
+    {
+        ret.addAll( parse( line.substring( 0, linkMatcher.start() ) ) );
+        if ( line.charAt( linkMatcher.start() ) == '!' )
+        {
+            ret.add( new TextBlock( line.substring( linkMatcher.start() + 1, linkMatcher.end() ) ) );
+        }
+        else
+        {
+            ret.add( createLink( linkMatcher.group( 1 ), linkMatcher.group( 2 ) ) );
+        }
+        ret.addAll( parse( line.substring( linkMatcher.end(), line.length() ) ) );
+    }
+
+    /**
+     * Parses xhtml.
+     *
+     * @param line the line to parse
+     * @param ret where the results live
+     * @param xhtmlMatcher xhtml matcher
+     */
+    private void parseXHTML( final String line, final List ret, final Matcher xhtmlMatcher )
+    {
+        if ( xhtmlMatcher.group( 1 ).indexOf( "noautolink" ) != -1 )
+        {
+            noautolink = true;
+        }
+        else
+        {
+            ret.add( new XHTMLBlock( xhtmlMatcher.group( 1 ) ) );
+        }
+
+        ret.addAll( parse( xhtmlMatcher.group( 2 ) ) );
+
+        if ( xhtmlMatcher.group( 1 ).indexOf( "noautolink" ) != -1 )
+        {
+            noautolink = false;
+        }
+        else
+        {
+            ret.add( new XHTMLBlock( xhtmlMatcher.group( 3 ) ) );
+        }
+
+        ret.addAll( parse( xhtmlMatcher.group( 4 ) ) );
+    }
+
+    /**
+     * @param m    matcher to test
+     * @param line line to test
+     * @return <code>true</code> if the match on m represent a word (must be
+     *         a space before the word or must be the beginning of the line)
+     */
+    private boolean isAWord( final Matcher m, final String line )
+    {
+        return startLikeWord( m, line ) && endLikeWord( m, line );
+    }
+
+    /**
+     * @param m matcher to test
+     * @param line line to test
+     * @return true if it is the beginning of a word
+     */
+    private boolean startLikeWord( final Matcher m, final String line )
+    {
+        final int start = m.start();
+
+        boolean ret = false;
+        if ( start == 0 )
+        {
+            ret = true;
+        }
+        else if ( start > 0 )
+        {
+            if ( isSpace( line.charAt( start - 1 ) ) )
+            {
+                ret = true;
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * @param m matcher to test
+     * @param line line to test
+     * @return true if it is the end of a word
+     */
+    private boolean endLikeWord( final Matcher m, final String line )
+    {
+        final int end = m.end();
+
+        boolean ret = true;
+        if ( end < line.length() )
+        {
+            ret = isSpace( line.charAt( end ) );
+        }
+
+        return ret;
+    }
+
+    /**
+     * @param c char to test
+     * @return <code>true</code> if c is a space char
+     */
+    private boolean isSpace( final char c )
+    {
+        return c == ' ' || c == '\t';
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/UnorderedListBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/UnorderedListBlock.java
new file mode 100644
index 0000000..20473b3
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/UnorderedListBlock.java
@@ -0,0 +1,60 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * <pre>
+ *    - item1
+ *    - item2
+ *        - item2.1
+ *        ...
+ * </pre>
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: UnorderedListBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class UnorderedListBlock
+    extends ListBlock
+{
+    /**
+     * Creates the UnorderedListBlock.
+     *
+     * @param blocks list of list items
+     * @throws IllegalArgumentException if listItemBlocks is <code>null</code>
+     */
+    UnorderedListBlock( final ListItemBlock[] blocks )
+    {
+        super( blocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.list();
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.list_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/VerbatimBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/VerbatimBlock.java
new file mode 100644
index 0000000..36e3e41
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/VerbatimBlock.java
@@ -0,0 +1,55 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+/**
+ * Represents a verbatim block
+ *
+ * @author Christian Nardi
+ * @version $Id: VerbatimBlock.java 747837 2009-02-25 15:50:39Z ltheussl $
+ */
+class VerbatimBlock
+    extends AbstractFatherBlock
+{
+    /**
+     * Creates the VerbatimBlock.
+     *
+     * @param childBlocks child blocks
+     */
+    VerbatimBlock( final Block[] childBlocks )
+    {
+        super( childBlocks );
+    }
+
+    /** {@inheritDoc} */
+    final void before( final Sink sink )
+    {
+        sink.verbatim( SinkEventAttributeSet.BOXED );
+    }
+
+    /** {@inheritDoc} */
+    final void after( final Sink sink )
+    {
+        sink.verbatim_();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/VerbatimBlockParser.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/VerbatimBlockParser.java
new file mode 100644
index 0000000..12cb3ae
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/VerbatimBlockParser.java
@@ -0,0 +1,83 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.util.ByLineSource;
+
+/**
+ * Parse verbatim blocks
+ *
+ * @author Christian Nardi
+ * @version $Id: VerbatimBlockParser.java 746991 2009-02-23 12:35:46Z vsiveton $
+ * @since 1.1
+ */
+public class VerbatimBlockParser
+    implements BlockParser
+{
+    /**
+     * pattern to detect verbatim start tags
+     */
+    private static final Pattern VERBATIM_START_PATTERN = Pattern.compile( "\\s*<verbatim>" );
+
+    private static final Pattern VERBATIM_END_PATTERN = Pattern.compile( "</verbatim>" );
+
+    /** {@inheritDoc} */
+    public final boolean accept( final String line )
+    {
+        return VERBATIM_START_PATTERN.matcher( line ).lookingAt();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final Block visit( final String line, final ByLineSource source )
+        throws ParseException
+    {
+        if ( !accept( line ) )
+        {
+            throw new IllegalAccessError( "call accept before this ;)" );
+        }
+
+        final List lines = new ArrayList();
+        Matcher matcher = VERBATIM_START_PATTERN.matcher( line );
+        matcher.find();
+        String l = line.substring( matcher.end() );
+
+        while ( l != null )
+        {
+            matcher = VERBATIM_END_PATTERN.matcher( l );
+            if ( matcher.find() )
+            {
+                lines.add( new TextBlock( l.substring( 0, matcher.start() ) + "\n" ) );
+                break;
+            }
+            lines.add( new TextBlock( l + "\n" ) );
+            l = source.getNextLine();
+        }
+
+        return new VerbatimBlock( (Block[]) lines.toArray( new Block[] {} ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/WikiWordBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/WikiWordBlock.java
new file mode 100644
index 0000000..a437336
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/WikiWordBlock.java
@@ -0,0 +1,125 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Represent a WikiWord
+ *
+ * @author Juan F. Codagnone
+ * @version $Id: WikiWordBlock.java 709605 2008-10-31 23:57:03Z hboutemy $
+ */
+class WikiWordBlock
+    implements Block
+{
+    /**
+     * the wiki word
+     */
+    private final String wikiword;
+
+    /**
+     * content to show in the wiki word link
+     */
+    private final Block content;
+
+    /**
+     * Resolves WikiWord links
+     */
+    private final WikiWordLinkResolver wikiWordLinkResolver;
+
+    /**
+     * @see #WikiWordBlock(String, String)
+     * @param aWikiword the wikiWord
+     * @param resolver responsible of resolving the link to the wikiWord
+     */
+    WikiWordBlock( final String aWikiword, final WikiWordLinkResolver resolver )
+    {
+        this( aWikiword, aWikiword, resolver );
+    }
+
+    /**
+     * Creates the WikiWordBlock.
+     *
+     * @param aWikiword the wiki word
+     * @param aText text to show in the wiki link
+     * @param resolver responsible of resolving the link to the wikiWord
+     * @throws IllegalArgumentException if the wikiword is <code>null</code>
+     * @deprecated
+     */
+    WikiWordBlock( final String aWikiword, final String aText, final WikiWordLinkResolver resolver )
+    {
+        this( aWikiword, new TextBlock( aText ), resolver );
+    }
+
+    /**
+     * Creates the WikiWordBlock.
+     *
+     * @param aWikiword the wiki word
+     * @param content content to show in the wiki link
+     * @param resolver responsible of resolving the link to the wikiWord
+     * @throws IllegalArgumentException if the wikiword is <code>null</code>
+     */
+    WikiWordBlock( final String aWikiword, final Block content, final WikiWordLinkResolver resolver )
+    {
+        if ( aWikiword == null || content == null || resolver == null )
+        {
+            throw new IllegalArgumentException( "arguments can't be null" );
+        }
+        this.wikiword = aWikiword;
+        this.content = content;
+        this.wikiWordLinkResolver = resolver;
+    }
+
+    /** {@inheritDoc} */
+    public final void traverse( final Sink sink )
+    {
+        sink.link( wikiWordLinkResolver.resolveLink( wikiword ) );
+        content.traverse( sink );
+        sink.link_();
+    }
+
+    /** {@inheritDoc} */
+    public final boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( obj == this )
+        {
+            ret = true;
+        }
+        else if ( obj instanceof WikiWordBlock )
+        {
+            final WikiWordBlock w = (WikiWordBlock) obj;
+            ret = wikiword.equals( w.wikiword ) && content.equals( w.content );
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    public final int hashCode()
+    {
+        final int magic1 = 17;
+        final int magic2 = 37;
+
+        return magic1 + magic2 * wikiword.hashCode() + magic2 * content.hashCode();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/WikiWordLinkResolver.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/WikiWordLinkResolver.java
new file mode 100644
index 0000000..50d89f8
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/WikiWordLinkResolver.java
@@ -0,0 +1,37 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Resolves the WikiWordLinks
+ *
+ * @author Christian D. Nardi
+ * @version $Id: WikiWordLinkResolver.java 638290 2008-03-18 09:45:22Z bentmann $
+ */
+interface WikiWordLinkResolver
+{
+    /**
+     * Given a wikiWord, it returns a link string.
+     *
+     * @param wikiWord wikiWord to resolve
+     * @return the link representation of the wikiWord.
+     */
+    String resolveLink( final String wikiWord );
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/XHTMLBlock.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/XHTMLBlock.java
new file mode 100644
index 0000000..9ebeab6
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/XHTMLBlock.java
@@ -0,0 +1,85 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * An XHTML Block
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XHTMLBlock.java 763762 2009-04-09 18:19:56Z ltheussl $
+ */
+class XHTMLBlock
+    implements Block
+{
+    private final String tag;
+
+    /**
+     * Creates the XHTMLBlock.
+     *
+     * @param tag the tag, eg: <pre>
+     * @throws IllegalArgumentException if the arguments are <code>null</code>
+     */
+    XHTMLBlock( final String tag )
+    {
+        if ( tag == null )
+        {
+            throw new IllegalArgumentException( "argument can't be null" );
+        }
+        this.tag = tag;
+    }
+
+    /** {@inheritDoc}*/
+    public final void traverse( final Sink sink )
+    {
+        if ( tag.trim().length() == 0 )
+        {
+            return;
+        }
+
+        sink.rawText( tag );
+    }
+
+    /** {@inheritDoc}*/
+    public final boolean equals( final Object obj )
+    {
+        boolean ret = false;
+
+        if ( obj == this )
+        {
+            ret = true;
+        }
+        else if ( obj instanceof XHTMLBlock )
+        {
+            final XHTMLBlock a = (XHTMLBlock) obj;
+
+            ret = tag.equals( a.tag );
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc}*/
+    public final int hashCode()
+    {
+        return tag.hashCode();
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/XHTMLWikiWordLinkResolver.java b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/XHTMLWikiWordLinkResolver.java
new file mode 100644
index 0000000..27b6bec
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/main/java/org/apache/maven/doxia/module/twiki/parser/XHTMLWikiWordLinkResolver.java
@@ -0,0 +1,43 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Transform WikiWord to HTML links
+ *
+ * @author Christian Nardi
+ * @version $Id: XHTMLWikiWordLinkResolver.java 746991 2009-02-23 12:35:46Z vsiveton $
+ * @since 1.1
+ */
+public class XHTMLWikiWordLinkResolver
+    implements WikiWordLinkResolver
+{
+    /**
+     * {@inheritDoc}
+     *
+     * This only works for xhtml output, but there is no way
+     * of transforming a wikiWord in another context.
+     * @see org.apache.maven.doxia.module.twiki.parser.WikiWordLinkResolver#resolveLink(java.lang.String)
+     */
+    public final String resolveLink( final String wikiWord )
+    {
+        return "./" + wikiWord + ".html";
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/site/site.xml b/doxia-modules/doxia-module-twiki/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/TWikiSinkTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/TWikiSinkTest.java
new file mode 100644
index 0000000..f7b6ee0
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/TWikiSinkTest.java
@@ -0,0 +1,282 @@
+package org.apache.maven.doxia.module.twiki;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.util.HtmlTools;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Test the TWiki Sink
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: TWikiSinkTest.java 779565 2009-05-28 11:24:35Z vsiveton $
+ * @see TWikiSink
+ */
+public class TWikiSinkTest
+    extends AbstractSinkTest
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new TWikiSink( writer );
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        return EOL + "#" + anchor + anchor;
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        return null;
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        return TWikiMarkup.BOLD_START_MARKUP + text + TWikiMarkup.BOLD_END_MARKUP;
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return TWikiMarkup.DEFINITION_LIST_ITEM_MARKUP + definum + TWikiMarkup.DEFINITION_LIST_DEFINITION_MARKUP
+            + definition + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        return "<img src=\"" + source + "\" alt=\"" + caption + "\" />";
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return TWikiMarkup.HORIZONTAL_RULE_MARKUP + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        return TWikiMarkup.ITALIC_START_MARKUP + text + TWikiMarkup.ITALIC_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        return TWikiMarkup.LINK_START_MARKUP + link + TWikiMarkup.LINK_MIDDLE_MARKUP + text
+            + TWikiMarkup.LINK_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return TWikiMarkup.LIST_ITEM_MARKUP + item + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        return TWikiMarkup.MONOSPACED_START_MARKUP + text + TWikiMarkup.MONOSPACED_END_MARKUP;
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return TWikiMarkup.NUMBERING_LOWER_ROMAN_MARKUP + " " + item + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        return text + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return StringUtils.repeat( "-", 3 ) + StringUtils.repeat( "+", 1 ) + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return StringUtils.repeat( "-", 3 ) + StringUtils.repeat( "+", 2 ) + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        return StringUtils.repeat( "-", 3 ) + StringUtils.repeat( "+", 3 ) + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        return StringUtils.repeat( "-", 3 ) + StringUtils.repeat( "+", 4 ) + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        return StringUtils.repeat( "-", 3 ) + StringUtils.repeat( "+", 5 ) + title + EOL + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        return "| " + cell + " |" + EOL + "Table_caption";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        return HtmlTools.escapeHTML( text );
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        return "<div class=\"source\">" + EOL + "<verbatim>" + text + "</verbatim>" + EOL + "</div>" + EOL;
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "twiki";
+    }
+
+    // ----------------------------------------------------------------------
+    // Override unused tests
+    // ----------------------------------------------------------------------
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testAuthor()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testDate()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testHead()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testBody()
+    {
+        // nop
+    }
+
+    /** Not used.
+     * {@inheritDoc} */
+    public void testTitle()
+    {
+        // nop
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "";
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/AbstractBlockTestCase.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/AbstractBlockTestCase.java
new file mode 100644
index 0000000..122f01e
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/AbstractBlockTestCase.java
@@ -0,0 +1,108 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import junit.framework.TestCase;
+
+import org.apache.maven.doxia.module.twiki.TWikiParser;
+
+/**
+ * Common code to the Block unit tests
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 1, 2005
+ */
+public abstract class AbstractBlockTestCase
+    extends TestCase
+{
+    /**
+     * sectionParser to use in all the tests
+     */
+    protected final SectionBlockParser sectionParser = new SectionBlockParser();
+
+    /**
+     * ParagraphBlockParser  to use in all the tests
+     */
+    protected final ParagraphBlockParser paraParser = new ParagraphBlockParser();
+
+    /**
+     * ListBlockParser used in all the tests
+     */
+    protected final GenericListBlockParser listParser = new GenericListBlockParser();
+
+    /**
+     * FormatedTextParser used in all the tests
+     */
+    protected final FormatedTextParser formatTextParser = new FormatedTextParser();
+
+    /**
+     * TextParser used in all the tests
+     */
+    protected final TextParser textParser = new TextParser( new XHTMLWikiWordLinkResolver() );
+
+    /**
+     * TextParser used in all the tests
+     */
+    protected final HRuleBlockParser hruleParser = new HRuleBlockParser();
+
+    /**
+     * TableBlockParser used in all the tests
+     */
+    protected final TableBlockParser tableParser = new TableBlockParser();
+
+    /**
+     * TWiki used in all the tests
+     */
+    protected final TWikiParser twikiParser = new TWikiParser();
+
+    /**
+     * Parser for verbatim blocks
+     */
+    private final VerbatimBlockParser verbatimParser = new VerbatimBlockParser();
+
+    /**
+     * Creates the AbstractBlockTestCase.
+     */
+    public AbstractBlockTestCase()
+    {
+        sectionParser.setParaParser( paraParser );
+        sectionParser.setHrulerParser( hruleParser );
+        paraParser.setSectionParser( sectionParser );
+        paraParser.setListParser( listParser );
+        paraParser.setTextParser( formatTextParser );
+        paraParser.setHrulerParser( hruleParser );
+        paraParser.setTableBlockParser( tableParser );
+        paraParser.setVerbatimParser( verbatimParser );
+        sectionParser.setVerbatimBlockParser( new VerbatimBlockParser() );
+        listParser.setTextParser( formatTextParser );
+        formatTextParser.setTextParser( textParser );
+        tableParser.setTextParser( formatTextParser );
+    }
+
+    /**
+     * Returns the verbatimParser.
+     *
+     * @return <code>VerbatimBlockParser</code> with the verbatimParser.
+     */
+    protected final VerbatimBlockParser getVerbatimParser()
+    {
+        return verbatimParser;
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/BlockTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/BlockTest.java
new file mode 100644
index 0000000..ecf9887
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/BlockTest.java
@@ -0,0 +1,154 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+/**
+ * Generic unit tests for
+ * {@link Block}s
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 2, 2005
+ */
+public class BlockTest
+    extends TestCase
+{
+
+    /**
+     * @see TextBlock#equals(Object)
+     */
+    public final void testTextBlockEquals()
+    {
+        testEquals( new TextBlock( "bar" ), new TextBlock( "bar" ), new TextBlock( "foo" ) );
+    }
+
+    /**
+     * @see WikiWordBlock#equals(Object)
+     */
+    public final void testWikiWordBlockEquals()
+    {
+        final WikiWordLinkResolver resolver = new XHTMLWikiWordLinkResolver();
+        testEquals( new WikiWordBlock( "bar", resolver ), new WikiWordBlock( "bar", resolver ),
+                    new WikiWordBlock( "foo", resolver ) );
+
+        testEquals( new WikiWordBlock( "bar", new TextBlock( "text" ), resolver ),
+                    new WikiWordBlock( "bar", new TextBlock( "text" ), resolver ), new WikiWordBlock( "bar",
+                                                                                                      resolver ) );
+
+        testEquals( new WikiWordBlock( "bar", new TextBlock( "text" ), resolver ),
+                    new WikiWordBlock( "bar", new TextBlock( "text" ), resolver ),
+                    new WikiWordBlock( "text", new TextBlock( "bar" ), resolver ) );
+
+    }
+
+    /**
+     * @see LinkBlock#equals(Object)
+     */
+    public final void testLinkBlockEquals()
+    {
+        testEquals( new LinkBlock( "foo", new TextBlock( "bar" ) ),
+                    new LinkBlock( "foo", new TextBlock( "bar" ) ), new LinkBlock( "bar", new TextBlock( "foo" ) ) );
+    }
+
+    /**
+     * @see ListItemBlock#equals(Object)
+     */
+    public final void testListBlockEquals()
+    {
+        final Block[] blocks = new Block[] { new TextBlock( "hello" ) };
+
+        testEquals( new ListItemBlock( blocks ), new ListItemBlock( blocks ), new ListItemBlock( new Block[] {} ) );
+    }
+
+    /**
+     * @see ListItemBlock#equals(Object)
+     */
+    public final void testNestedBlockEquals()
+    {
+
+        testEquals( new ParagraphBlock( new Block[] { new BoldBlock( new Block[] { new TextBlock( "foo" ) } ) } ),
+                    new ParagraphBlock( new Block[] { new BoldBlock( new Block[] { new TextBlock( "foo" ) } ) } ),
+                    new ParagraphBlock( new Block[] { new BoldBlock( new Block[] { new TextBlock( "bar" ) } ) } ) );
+    }
+
+    /**
+     * @see AbstractFatherBlock#equals(Object)
+     */
+    public final void testAbstractFatherBlockEquals()
+    {
+        assertFalse( Arrays
+                           .equals(
+                                    new Block[] {
+                                        new TextBlock( "mary " ),
+                                        new ItalicBlock(
+                                                         new Block[] { new MonospaceBlock(
+                                                                                           new Block[] { new TextBlock(
+                                                                                                                        "has" ) } ) } ) },
+                                    new Block[] {
+                                        new TextBlock( "mary " ),
+                                        new BoldBlock(
+                                                       new Block[] { new MonospaceBlock(
+                                                                                         new Block[] { new TextBlock(
+                                                                                                                      "has" ) } ) } ) } ) );
+    }
+
+    /**
+     * @see AnchorBlock#equals(Object)
+     */
+    public final void testAnchorBlockEquals()
+    {
+        testEquals( new AnchorBlock( "anchor" ), new AnchorBlock( "anchor" ), new AnchorBlock( "anch" ) );
+    }
+
+    /**
+     * @see HorizontalRuleBlock#equals(Object)
+     */
+    public final void testHorizontalEquals()
+    {
+        testEquals( new HorizontalRuleBlock(), new HorizontalRuleBlock(), "foo" );
+    }
+
+    /**
+     * @param a an object
+     * @param b an object that is equals to a
+     * @param c a diferent object
+     */
+    public final void testEquals( final Object a, final Object b, final Object c )
+    {
+        assertFalse( a.equals( null ) );
+        assertFalse( b.equals( null ) );
+        assertFalse( c.equals( null ) );
+
+        assertNotSame( a, b );
+
+        assertEquals( a, a );
+        assertEquals( b, b );
+        assertEquals( c, c );
+
+        assertEquals( a, b );
+        assertEquals( b, a );
+        assertFalse( a.equals( c ) );
+
+        assertEquals( a.hashCode(), b.hashCode() );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/FormatedTextTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/FormatedTextTest.java
new file mode 100644
index 0000000..0995a5c
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/FormatedTextTest.java
@@ -0,0 +1,213 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Arrays;
+
+/**
+ * Tests the {@link org.apache.maven.doxia.module.twiki.parser.FormatedTextParser}
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 2, 2005
+ */
+public class FormatedTextTest
+    extends AbstractBlockTestCase
+{
+
+    /**
+     * test bold text
+     */
+    public final void testBold()
+    {
+        String text;
+        Block[] blocks;
+
+        text = "*bold*";
+        blocks = formatTextParser.parse( text );
+        assertEquals( 1, blocks.length );
+        assertEquals( new BoldBlock( new Block[] { new TextBlock( "bold" ) } ), blocks[0] );
+
+        text = "foo *bold* bar";
+        blocks = formatTextParser.parse( text );
+        assertTrue( Arrays.equals( new Block[] { new TextBlock( "foo " ),
+            new BoldBlock( new Block[] { new TextBlock( "bold" ) } ), new TextBlock( " bar" ) }, blocks ) );
+
+        text = "\t*bold* bar";
+        blocks = formatTextParser.parse( text );
+        assertTrue( Arrays.equals( new Block[] { new TextBlock( "\t" ),
+            new BoldBlock( new Block[] { new TextBlock( "bold" ) } ), new TextBlock( " bar" ) }, blocks ) );
+
+        text = "*nice* foo *bold* bar";
+        blocks = formatTextParser.parse( text );
+        assertTrue( Arrays.equals( new Block[] { new BoldBlock( new Block[] { new TextBlock( "nice" ) } ),
+            new TextBlock( " foo " ), new BoldBlock( new Block[] { new TextBlock( "bold" ) } ),
+            new TextBlock( " bar" ) }, blocks ) );
+    }
+
+    /**
+     * test italic text
+     */
+    public final void testItalic()
+    {
+        String text;
+        Block[] blocks;
+
+        text = "_italic_";
+        blocks = formatTextParser.parse( text );
+        assertEquals( 1, blocks.length );
+        assertEquals( new ItalicBlock( new Block[] { new TextBlock( "italic" ) } ), blocks[0] );
+
+        text = "foo _italic_ bar";
+        blocks = formatTextParser.parse( text );
+        assertTrue( Arrays.equals( new Block[] { new TextBlock( "foo " ),
+            new ItalicBlock( new Block[] { new TextBlock( "italic" ) } ), new TextBlock( " bar" ) }, blocks ) );
+
+        text = "_nice_ foo _italic_ bar";
+        blocks = formatTextParser.parse( text );
+        assertTrue( Arrays.equals( new Block[] { new ItalicBlock( new Block[] { new TextBlock( "nice" ) } ),
+            new TextBlock( " foo " ), new ItalicBlock( new Block[] { new TextBlock( "italic" ) } ),
+            new TextBlock( " bar" ) }, blocks ) );
+    }
+
+    /**
+     * test monospaced text
+     */
+    public final void testMonospaced()
+    {
+        String text;
+        Block[] blocks;
+
+        text = "mary =has= a =little= lamb He followed her (=to school one day=)";
+        blocks = formatTextParser.parse( text );
+        assertTrue( Arrays.equals( new Block[] { new TextBlock( "mary " ),
+            new MonospaceBlock( new Block[] { new TextBlock( "has" ) } ), new TextBlock( " a " ),
+            new MonospaceBlock( new Block[] { new TextBlock( "little" ) } ),
+            new TextBlock( " lamb He followed her (" ),
+            new MonospaceBlock( new Block[] { new TextBlock( "to school one day" ) } ), new TextBlock( ")" ) },
+                                   blocks ) );
+    }
+
+    /**
+     * test monospaced text
+     */
+    public final void testBoldMonospaced()
+    {
+        String text;
+        Block[] blocks;
+
+        text = "mary ==has== a ==little== lamb";
+        blocks = formatTextParser.parse( text );
+        Block[] expected =
+            new Block[] { new TextBlock( "mary " ),
+                new BoldBlock( new Block[] { new MonospaceBlock( new Block[] { new TextBlock( "has" ) } ) } ),
+                new TextBlock( " a " ),
+                new BoldBlock( new Block[] { new MonospaceBlock( new Block[] { new TextBlock( "little" ) } ) } ),
+                new TextBlock( " lamb" ) };
+
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * test monospaced text
+     */
+    public final void testBoldItalic()
+    {
+        String text;
+        Block[] blocks;
+
+        text = "mary __has__ a __little__ lamb";
+        blocks = formatTextParser.parse( text );
+        assertTrue( Arrays.equals( new Block[] { new TextBlock( "mary " ),
+            new BoldBlock( new Block[] { new ItalicBlock( new Block[] { new TextBlock( "has" ) } ) } ),
+            new TextBlock( " a " ),
+            new BoldBlock( new Block[] { new ItalicBlock( new Block[] { new TextBlock( "little" ) } ) } ),
+            new TextBlock( " lamb" ) }, blocks ) );
+    }
+
+    /**
+     * test mixed formats side by side
+     */
+    public final void testMultiFormatSideBySide()
+    {
+        String text;
+        Block[] blocks;
+        Block[] expected;
+
+        text = "All *work and* =no play= _makes_ Juan a dull *boy*";
+        blocks = formatTextParser.parse( text );
+
+        expected =
+            new Block[] { new TextBlock( "All " ), new BoldBlock( new Block[] { new TextBlock( "work and" ) } ),
+                new TextBlock( " " ), new MonospaceBlock( new Block[] { new TextBlock( "no play" ) } ),
+                new TextBlock( " " ), new ItalicBlock( new Block[] { new TextBlock( "makes" ) } ),
+                new TextBlock( " Juan a dull " ), new BoldBlock( new Block[] { new TextBlock( "boy" ) } ) };
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+    }
+
+    /**
+     * test mixed formats recursevily
+     */
+    public final void testMultiFormatInside()
+    {
+        String text;
+        Block[] blocks;
+        Block[] expected;
+
+        text = "All *work and =no play _makes_ Juan= a dull* boy";
+        blocks = formatTextParser.parse( text );
+
+        expected =
+            new Block[] {
+                new TextBlock( "All " ),
+                new BoldBlock(
+                               new Block[] {
+                                   new TextBlock( "work and " ),
+                                   new MonospaceBlock( new Block[] { new TextBlock( "no play " ),
+                                       new ItalicBlock( new Block[] { new TextBlock( "makes" ) } ),
+                                       new TextBlock( " Juan" ) } ), new TextBlock( " a dull" ) } ),
+                new TextBlock( " boy" ) };
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * test unbonded formats
+     */
+    public final void testUnboundedFormat()
+    {
+        testHanging( "All *work and no play makes Juan a dull boy" );
+        testHanging( "All __work and no play makes Juan a dull boy" );
+        testHanging( "All __work and *no play makes _Juan a = dull boy" );
+        testHanging( "*" );
+        testHanging( "==" );
+        testHanging( "**" ); // hehe
+        testHanging( "*  hello   *" );
+        testHanging( "*  hello   =*" );
+        testHanging( "*=_  hello   _=*" );
+    }
+
+    /**
+     * @param text unbonded text
+     */
+    public final void testHanging( final String text )
+    {
+        assertTrue( Arrays.equals( new Block[] { new TextBlock( text ) }, formatTextParser.parse( text ) ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/ListTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/ListTest.java
new file mode 100644
index 0000000..0e25c35
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/ListTest.java
@@ -0,0 +1,128 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringReader;
+
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Units tests for Lists
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 1, 2005
+ */
+public class ListTest
+    extends AbstractBlockTestCase
+{
+
+    /**
+     * unit test for recurrent enumeration
+     *
+     * @throws ParseException on error
+     */
+    public final void testList()
+        throws ParseException
+    {
+        final BlockParser parser = listParser;
+
+        final String text =
+            "" + "      * item1.1 \n" + "   * item2\n" + "      * item2.1\n" + "   * item3\n"
+                + "      * item3.1\n" + "      * item3.2\n" + "         * item3.2.1\n" + "         * item3.2.2\n"
+                + "         * item3.2.3\n" + "      * item3.3\n" + "         * item3.3.1\n" + "   * item4";
+
+        final ByLineSource source = new ByLineReaderSource( new StringReader( text ) );
+        final Block b = parser.visit( source.getNextLine(), source );
+        final Block[] firstLevelBlocks = ( (UnorderedListBlock) b ).getBlocks();
+        final int numberOfChild = 4;
+        assertEquals( numberOfChild, firstLevelBlocks.length );
+
+        for ( int i = 0; i < firstLevelBlocks.length; i++ )
+        {
+            Block block = firstLevelBlocks[i];
+            assertEquals( ListItemBlock.class, block.getClass() );
+        }
+
+        ListBlock list;
+        ListItemBlock item;
+        Block[] blocks;
+
+        item = (ListItemBlock) firstLevelBlocks[1];
+        blocks = item.getBlocks();
+        assertEquals( 1, blocks.length );
+        assertEquals( "item2", ( (TextBlock) blocks[0] ).getText() );
+        list = item.getInnerList();
+        assertNotNull( list );
+        blocks = list.getBlocks();
+        assertEquals( blocks.length, 1 );
+        item = (ListItemBlock) blocks[0];
+        assertEquals( 1, item.getBlocks().length );
+        assertEquals( "item2.1", ( (TextBlock) item.getBlocks()[0] ).getText() );
+    }
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testNumeringDecimal()
+        throws ParseException
+    {
+        final String text = "" + "   1. item1\n" + "   1. item2\n" + "   1. item3";
+
+        final ByLineSource source = new ByLineReaderSource( new StringReader( text ) );
+        Block blocks, expected;
+        expected =
+            new NumeratedListBlock( Sink.NUMBERING_DECIMAL, new ListItemBlock[] {
+                new ListItemBlock( new Block[] { new TextBlock( "item1" ) } ),
+                new ListItemBlock( new Block[] { new TextBlock( "item2" ) } ),
+                new ListItemBlock( new Block[] { new TextBlock( "item3" ) } ) } );
+        blocks = listParser.visit( source.getNextLine(), source );
+        assertEquals( expected, blocks );
+    }
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testHetero()
+        throws ParseException
+    {
+        final String text =
+            "" + "   A. item1\n" + "      * item1.1\n" + "      * item1.2\n" + "   B. item2\n"
+                + "      i. item2.1\n" + "      i. item2.2\n" + "   C. item3";
+
+        final ByLineSource source = new ByLineReaderSource( new StringReader( text ) );
+        Block blocks, expected;
+        expected =
+            new NumeratedListBlock( Sink.NUMBERING_UPPER_ALPHA, new ListItemBlock[] {
+                new ListItemBlock( new Block[] { new TextBlock( "item1" ) },
+                                   new UnorderedListBlock( new ListItemBlock[] {
+                                       new ListItemBlock( new Block[] { new TextBlock( "item1.1" ) } ),
+                                       new ListItemBlock( new Block[] { new TextBlock( "item1.2" ) } ) } ) ),
+                new ListItemBlock( new Block[] { new TextBlock( "item2" ) },
+                                   new NumeratedListBlock( Sink.NUMBERING_LOWER_ROMAN, new ListItemBlock[] {
+                                       new ListItemBlock( new Block[] { new TextBlock( "item2.1" ) } ),
+                                       new ListItemBlock( new Block[] { new TextBlock( "item2.2" ) } ) } ) ),
+                new ListItemBlock( new Block[] { new TextBlock( "item3" ) } ) } );
+        blocks = listParser.visit( source.getNextLine(), source );
+        assertEquals( expected, blocks );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/ParagraphTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/ParagraphTest.java
new file mode 100644
index 0000000..0c584a0
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/ParagraphTest.java
@@ -0,0 +1,169 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringReader;
+import java.util.Arrays;
+
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Tests the {@link org.apache.maven.doxia.module.twiki.parser.ParagraphBlockParser}
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 1, 2005
+ */
+public class ParagraphTest
+    extends AbstractBlockTestCase
+{
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testMultiLines()
+        throws ParseException
+    {
+        final String text =
+            "" + "\n\n\n" + "para1 -> text1\n" + "para1 -> text2\n" + "\n" + "para2 -> text1\n"
+                + "para2 -> text2\n" + "   \n   \n  " + "para2 -> text1\n" + "para2 -> text2\n";
+
+        final ByLineReaderSource source = new ByLineReaderSource( new StringReader( text ) );
+        final ParagraphBlockParser parser = paraParser;
+
+        ParagraphBlock block;
+
+        block = (ParagraphBlock) parser.visit( source.getNextLine(), source );
+        assertNotNull( block );
+        assertEquals( 1, block.getBlocks().length );
+        assertEquals( "para1 -> text1 para1 -> text2", ( (TextBlock) block.getBlocks()[0] ).getText() );
+
+        block = (ParagraphBlock) parser.visit( source.getNextLine(), source );
+        assertNotNull( block );
+        assertEquals( 1, block.getBlocks().length );
+        assertEquals( "para2 -> text1 para2 -> text2", ( (TextBlock) block.getBlocks()[0] ).getText() );
+    }
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testParagraphWithList()
+        throws ParseException
+    {
+        final String text =
+            "" + "Description text:\n" + "   * item1\n" + "   * item2\n"
+                + "This is more text in the same paragraph\n" + "\n" + "Another paragraph";
+
+        final ByLineReaderSource source = new ByLineReaderSource( new StringReader( text ) );
+        final ParagraphBlockParser parser = paraParser;
+
+        ParagraphBlock block;
+
+        block = (ParagraphBlock) parser.visit( source.getNextLine(), source );
+        assertNotNull( block );
+        final Block[] firstLevelChilds = block.getBlocks();
+        final int numberOfChilds = 3;
+        assertEquals( numberOfChilds, firstLevelChilds.length );
+        assertEquals( TextBlock.class, firstLevelChilds[0].getClass() );
+        assertEquals( UnorderedListBlock.class, firstLevelChilds[1].getClass() );
+        assertEquals( TextBlock.class, firstLevelChilds[2].getClass() );
+
+        final Block[] listChilds = ( (UnorderedListBlock) firstLevelChilds[1] ).getBlocks();
+        assertEquals( 2, listChilds.length );
+        assertEquals( 1, ( (ListItemBlock) listChilds[0] ).getBlocks().length );
+        assertEquals( "item1", ( (TextBlock) ( (ListItemBlock) listChilds[0] ).getBlocks()[0] ).getText() );
+        assertEquals( "item2", ( (TextBlock) ( (ListItemBlock) listChilds[1] ).getBlocks()[0] ).getText() );
+    }
+
+    /**
+     * tests some valid weired lists
+     *
+     * @throws ParseException on error
+     */
+    public final void testParagraphWithStartingList()
+        throws ParseException
+    {
+        final String text =
+            "" + "   * item1\n" + "   * item2\n" + "This is more text in the same paragraph\n" + "\n"
+                + "Another paragraph";
+
+        final ByLineReaderSource source = new ByLineReaderSource( new StringReader( text ) );
+        final ParagraphBlockParser parser = paraParser;
+
+        ParagraphBlock block;
+
+        block = (ParagraphBlock) parser.visit( source.getNextLine(), source );
+        assertNotNull( block );
+        final Block[] firstLevelChilds = block.getBlocks();
+        assertEquals( 2, firstLevelChilds.length );
+        assertEquals( UnorderedListBlock.class, firstLevelChilds[0].getClass() );
+        assertEquals( TextBlock.class, firstLevelChilds[1].getClass() );
+
+        final Block[] listChilds = ( (UnorderedListBlock) firstLevelChilds[0] ).getBlocks();
+        assertEquals( 2, listChilds.length );
+        assertEquals( 1, ( (ListItemBlock) listChilds[0] ).getBlocks().length );
+        assertEquals( "item1", ( (TextBlock) ( (ListItemBlock) listChilds[0] ).getBlocks()[0] ).getText() );
+        assertEquals( "item2", ( (TextBlock) ( (ListItemBlock) listChilds[1] ).getBlocks()[0] ).getText() );
+    }
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testHorizontalRule()
+        throws ParseException
+    {
+        Block block, expected;
+        ByLineReaderSource source;
+
+        assertTrue( hruleParser.accept( "---" ) );
+        assertFalse( hruleParser.accept( "---+ asdas" ) );
+
+        source = new ByLineReaderSource( new StringReader( "" ) );
+        expected = new HorizontalRuleBlock();
+        block = hruleParser.visit( "---", source );
+        assertNull( source.getNextLine() );
+        assertEquals( expected, block );
+
+        source = new ByLineReaderSource( new StringReader( "" ) );
+        expected = new HorizontalRuleBlock();
+        block = hruleParser.visit( "--- Some text ---- And some more", source );
+        assertEquals( expected, block );
+        expected = new ParagraphBlock( new Block[] { new TextBlock( "Some text ---- And some more" ) } );
+        block = paraParser.visit( source.getNextLine(), source );
+        assertEquals( expected, block );
+    }
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testHorizontalRuleAndParagraph()
+        throws ParseException
+    {
+        Block[] blocks, expected;
+        ByLineReaderSource source;
+
+        source = new ByLineReaderSource( new StringReader( "" + "Some text\n" + "-----------\n" + "More text" ) );
+        expected =
+            new Block[] { new ParagraphBlock( new Block[] { new TextBlock( "Some text" ) } ),
+                new HorizontalRuleBlock(), new ParagraphBlock( new Block[] { new TextBlock( "More text" ) } ) };
+        blocks = (Block[]) twikiParser.parse( source ).toArray( new Block[] {} );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/SectionTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/SectionTest.java
new file mode 100644
index 0000000..6849111
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/SectionTest.java
@@ -0,0 +1,176 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringReader;
+import java.util.Arrays;
+
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Tests the {@link org.apache.maven.doxia.module.twiki.parser.SectionBlockParser}
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 1, 2005
+ */
+public class SectionTest
+    extends AbstractBlockTestCase
+{
+
+    /**
+     * @see SectionBlock#SectionBlock(String, int, Block[])
+     */
+    public final void testSectionBlockWrongArgs()
+    {
+        final int maxLevel = 5;
+        new SectionBlock( "hola", 1, new Block[] {} );
+        new SectionBlock( "hola", maxLevel, new Block[] {} );
+
+        try
+        {
+            new SectionBlock( "hola", maxLevel + 1, new Block[] {} );
+            fail();
+        }
+        catch ( final Throwable e )
+        {
+            // ok
+        }
+
+        try
+        {
+            new SectionBlock( "hola", 0, new Block[] {} );
+            fail();
+        }
+        catch ( final Throwable e )
+        {
+            // ok
+        }
+
+        try
+        {
+            new SectionBlock( null, 1, null );
+            fail();
+        }
+        catch ( final Throwable e )
+        {
+            // ok
+        }
+
+        new SectionBlock( "", 1, new Block[] {} );
+    }
+
+    /**
+     * @see SectionBlockParser#getLevel(String)
+     */
+    public final void testSectionParserGetLevel()
+    {
+        assertEquals( 2, SectionBlockParser.getLevel( "++" ) );
+        try
+        {
+            SectionBlockParser.getLevel( "asdasd" );
+            fail( "expected exception was not thrown" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            // ok
+        }
+    }
+
+    /**
+     * @throws java.lang.Exception
+     * @see SectionBlockParser
+     */
+    public final void testSectionParser()
+        throws Exception
+    {
+        final SectionBlockParser parser = sectionParser;
+        assertTrue( parser.accept( "---+ Title1" ) );
+        assertTrue( parser.accept( "---++ Title2" ) );
+        assertFalse( parser.accept( " ---++ Title3" ) );
+        assertTrue( parser.accept( "---+++ Title4" ) );
+        assertTrue( parser.accept( "---++++ Title5" ) );
+        assertTrue( parser.accept( "---+++++ Title6" ) );
+
+        SectionBlock block;
+        block = (SectionBlock) parser.visit( "---++++ Title4", new ByLineReaderSource( new StringReader( "" ) ) );
+
+        final int level = 4;
+        assertEquals( "Title4", block.getTitle() );
+        assertEquals( level, block.getLevel() );
+        assertEquals( 0, block.getBlocks().length );
+
+        // ejemplo un poco m�s complejo
+        block =
+            (SectionBlock) parser.visit( "---+++ Title3",
+                                         new ByLineReaderSource( new StringReader(
+                                                                                   "This is *a* parragraph of a section.\n"
+                                                                                       + "Some text.\n"
+                                                                                       + "---+++ Another Title"
+                                                                                       + "... and more text" ) ) );
+        final SectionBlock expected =
+            new SectionBlock( "Title3", 3, new Block[] { new ParagraphBlock( new Block[] {
+                new TextBlock( "This is " ), new BoldBlock( new Block[] { new TextBlock( "a" ) } ),
+                new TextBlock( " parragraph of a section. Some text." ) } ) } );
+        assertEquals( expected, block );
+    }
+
+    /**
+     * Test section with several paragraphs (the paragraph are plain text)
+     *
+     * @throws Exception on error
+     */
+    public final void testSectionWithParagraphs()
+        throws Exception
+    {
+        final String text =
+            "" + "---++ Title\n" + "\n" + "hey!\n" + "how are\n" + "you?\n" + "  \n  " + "Fine!! thanks";
+
+        final SectionBlockParser parser = sectionParser;
+        parser.setVerbatimBlockParser( new VerbatimBlockParser() );
+        final ByLineReaderSource source = new ByLineReaderSource( new StringReader( text ) );
+        final SectionBlock block = (SectionBlock) parser.visit( source.getNextLine(), source );
+        assertEquals( 2, block.getBlocks().length );
+        assertEquals( "hey! how are you?",
+                      ( (TextBlock) ( (ParagraphBlock) block.getBlocks()[0] ).getBlocks()[0] ).getText() );
+        assertEquals( "Fine!! thanks",
+                      ( (TextBlock) ( (ParagraphBlock) block.getBlocks()[1] ).getBlocks()[0] ).getText() );
+    }
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testSectionAndParaAndHrule()
+        throws ParseException
+    {
+        Block[] blocks, expected;
+        ByLineReaderSource source;
+
+        source =
+            new ByLineReaderSource( new StringReader( "" + "---++ Title\n" + "Some text\n"
+                + "----------- More text\n" ) );
+        expected =
+            new Block[] { new SectionBlock( "Title", 1, new Block[] {
+                new ParagraphBlock( new Block[] { new TextBlock( "Some text" ) } ), new HorizontalRuleBlock(),
+                new ParagraphBlock( new Block[] { new TextBlock( "More text" ) } ) } ) };
+        blocks = (Block[]) twikiParser.parse( source ).toArray( new Block[] {} );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/TableTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/TableTest.java
new file mode 100644
index 0000000..3091a46
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/TableTest.java
@@ -0,0 +1,88 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringReader;
+
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.util.ByLineSource;
+import org.apache.maven.doxia.parser.ParseException;
+
+/**
+ * Tests the {@link org.apache.maven.doxia.module.twiki.parser.TableBlockParser}
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 9, 2005
+ */
+public class TableTest
+    extends AbstractBlockTestCase
+{
+
+    /**
+     * unit test the regex
+     */
+    public final void testRegex()
+    {
+        assertTrue( tableParser.accept( "  | cell1 | cell2|   " ) );
+        assertFalse( tableParser.accept( "  | cell1 | cell" ) );
+    }
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testTable()
+        throws ParseException
+    {
+        final StringReader sw = new StringReader( "" + "  |cell1|cell2|  \n" + "|cell3|cell4|\n" );
+
+        final ByLineSource source = new ByLineReaderSource( sw );
+
+        Block block, expected;
+        expected =
+            new TableBlock( new Block[] {
+                new TableRowBlock( new Block[] { new TableCellBlock( new Block[] { new TextBlock( "cell1" ) } ),
+                    new TableCellBlock( new Block[] { new TextBlock( "cell2" ) } ) } ),
+                new TableRowBlock( new Block[] { new TableCellBlock( new Block[] { new TextBlock( "cell3" ) } ),
+                    new TableCellBlock( new Block[] { new TextBlock( "cell4" ) } ) } ) } );
+
+        block = tableParser.visit( source.getNextLine(), source );
+        assertEquals( block, expected );
+    }
+
+    /**
+     * @throws ParseException on error
+     */
+    public final void testTableHeader()
+        throws ParseException
+    {
+        final StringReader sw = new StringReader( "|*cell1*|*cell2*|\n" );
+
+        final ByLineSource source = new ByLineReaderSource( sw );
+
+        Block block, expected;
+        expected =
+            new TableBlock( new Block[] { new TableRowBlock( new Block[] {
+                new TableCellHeaderBlock( new Block[] { new TextBlock( "cell1" ) } ),
+                new TableCellHeaderBlock( new Block[] { new TextBlock( "cell2" ) } ) } ) } );
+
+        block = tableParser.visit( source.getNextLine(), source );
+        assertEquals( block, expected );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/TitleTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/TitleTest.java
new file mode 100644
index 0000000..9ab9a98
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/TitleTest.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.doxia.module.twiki.parser;
+
+import java.io.StringReader;
+
+import org.apache.maven.doxia.module.twiki.TWikiParser;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.util.ByLineSource;
+
+/**
+ * Tests for {@link TWikiParser#getTitle(java.util.List)}
+ *
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 10, 2007
+ */
+public class TitleTest
+    extends AbstractBlockTestCase
+{
+
+    public void testSectionTitle()
+        throws Exception
+    {
+        final ByLineSource source = new ByLineReaderSource( new StringReader( "---++ Test\n hello world" ) );
+
+        final TWikiParser parser = new TWikiParser();
+
+        assertEquals( "Test", parser.getTitle( parser.parse( source ), source ) );
+    }
+
+    public void testNoSectionTitle()
+        throws Exception
+    {
+        final ByLineSource source =
+            new NamedByLineSource( new ByLineReaderSource( new StringReader( "hello world" ) ), "testpage" );
+
+        final TWikiParser parser = new TWikiParser();
+
+        assertEquals( "testpage", parser.getTitle( parser.parse( source ), source ) );
+    }
+
+    public void testNoSectionTwikiExtensionTitle()
+        throws Exception
+    {
+        final ByLineSource source =
+            new NamedByLineSource( new ByLineReaderSource( new StringReader( "hello world" ) ), "testpage.twiki" );
+
+        final TWikiParser parser = new TWikiParser();
+
+        assertEquals( "testpage", parser.getTitle( parser.parse( source ), source ) );
+    }
+
+}
+
+class NamedByLineSource
+    implements ByLineSource
+{
+    /** reader */
+    private final ByLineReaderSource reader;
+
+    /** reader's name */
+    private final String name;
+
+    public NamedByLineSource( final ByLineReaderSource reader, final String name )
+    {
+        if ( reader == null || name == null )
+        {
+            throw new IllegalArgumentException( "null arguments are not allowed" );
+        }
+
+        this.reader = reader;
+        this.name = name;
+    }
+
+    /** @see ByLineReaderSource#close() */
+    public final void close()
+    {
+        reader.close();
+    }
+
+    /** @see ByLineReaderSource#getLineNumber() */
+    public final int getLineNumber()
+    {
+        return reader.getLineNumber();
+    }
+
+    /** @see ByLineReaderSource#getName() */
+    public final String getName()
+    {
+        return name;
+    }
+
+    /** @see ByLineReaderSource#getNextLine() */
+    public final String getNextLine()
+        throws ParseException
+    {
+        return reader.getNextLine();
+    }
+
+    /** @see ByLineReaderSource#unget(java.lang.String) */
+    public final void unget( final String s )
+        throws IllegalStateException
+    {
+        reader.unget( s );
+    }
+
+    /** @see ByLineReaderSource#ungetLine() */
+    public final void ungetLine()
+        throws IllegalStateException
+    {
+        reader.ungetLine();
+    }
+}
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/VerbatimTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/VerbatimTest.java
new file mode 100644
index 0000000..32ed2a5
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/VerbatimTest.java
@@ -0,0 +1,116 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.util.ByLineReaderSource;
+import org.apache.maven.doxia.util.ByLineSource;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Tests the {@link org.apache.maven.doxia.module.twiki.parser.VerbatimBlock}
+ *
+ * @author Christian Nardi
+ * @since Nov 8, 2007
+ */
+public class VerbatimTest
+    extends AbstractBlockTestCase
+{
+
+    /**
+     * unit test the regex
+     */
+    public final void testRegex()
+    {
+        assertTrue( getVerbatimParser().accept( "<verbatim>" ) );
+        assertTrue( getVerbatimParser().accept( "   <verbatim>" ) );
+        assertTrue( getVerbatimParser().accept( "<verbatim> a word" ) );
+        assertTrue( getVerbatimParser().accept( "<verbatim> another Word" ) );
+    }
+
+    /**
+     * @throws ParseException if the parser does not accept the line
+     *
+     */
+    public void testVerbatim()
+        throws ParseException
+    {
+        final StringReader sw =
+            new StringReader( "" + "  <verbatim> hello, \n" + " this is a verbatim text \n"
+                + " which i would like to test \n" + " Thanks </verbatim>" );
+
+        final ByLineSource source = new ByLineReaderSource( sw );
+
+        Block block, expected;
+        expected =
+            new VerbatimBlock( new Block[] { new TextBlock( " hello, \n" ),
+                new TextBlock( " this is a verbatim text \n" ), new TextBlock( " which i would like to test \n" ),
+                new TextBlock( " Thanks \n" ) } );
+
+        block = getVerbatimParser().visit( source.getNextLine(), source );
+        assertEquals( block, expected );
+    }
+
+    /**
+     * @throws Exception .
+     */
+    public void testTwiki()
+        throws Exception
+    {
+        final StringReader sw =
+            new StringReader( "hello this is a paragraph \n" + "  <verbatim> hello, \n"
+                + " this is a verbatim text \n" + " which i would like to test \n" + " Thanks </verbatim>" );
+        final ByLineSource source = new ByLineReaderSource( sw );
+
+        Block[] expected;
+        expected =
+            new Block[] {
+                new ParagraphBlock( new Block[] { new TextBlock( "hello this is a paragraph" ) } ),
+                new VerbatimBlock( new Block[] { new TextBlock( " hello, \n" ),
+                    new TextBlock( " this is a verbatim text \n" ),
+                    new TextBlock( " which i would like to test \n" ), new TextBlock( " Thanks \n" ) } ) };
+
+        List block = twikiParser.parse( source );
+        assertTrue( Arrays.equals( block.toArray(), expected ) );
+
+    }
+
+    /** test
+     * @throws org.apache.maven.doxia.parser.ParseException
+     */
+    public void testVerbatimAfterSection()
+        throws ParseException
+    {
+        final StringReader sw =
+            new StringReader( "---++ fooo\n" + "  <verbatim> hello, \n" + " Thanks </verbatim>" );
+        final ByLineSource source = new ByLineReaderSource( sw );
+
+        Block[] expected;
+        expected =
+            new Block[] { new SectionBlock( "foo", 2, new Block[] { new VerbatimBlock( new Block[] {
+                new TextBlock( " hello, \n" ), new TextBlock( " Thanks \n" ) } ) } ) };
+
+        List block = twikiParser.parse( source );
+        assertTrue( Arrays.equals( block.toArray(), expected ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/WordsTest.java b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/WordsTest.java
new file mode 100644
index 0000000..d2b058e
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/java/org/apache/maven/doxia/module/twiki/parser/WordsTest.java
@@ -0,0 +1,327 @@
+package org.apache.maven.doxia.module.twiki.parser;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Arrays;
+
+/**
+ * tests the WikiWord parsing (and things like that)
+ *
+ * @author Juan F. Codagnone
+ * @since Nov 4, 2005
+ */
+public class WordsTest
+    extends AbstractBlockTestCase
+{
+    /**
+     * used to convert lists to arrays
+     */
+    private static final Block[] TOARRAY = new Block[] {};
+
+    /**
+     * Resolves links for wikiWords
+     */
+    private final WikiWordLinkResolver resolver = new XHTMLWikiWordLinkResolver();
+
+    /**
+     * ...
+     */
+    public final void testText()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new TextBlock( "     Some text    " ) };
+        blocks = (Block[]) textParser.parse( "     Some text    " ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * ...
+     */
+    public final void testWikiWords()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new WikiWordBlock( "WikiWord", resolver ) };
+        blocks = (Block[]) textParser.parse( "WikiWord" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        // this is not a wiki word
+        expected = new Block[] { new TextBlock( "Wiki" ) };
+        blocks = (Block[]) textParser.parse( "Wiki" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected = new Block[] { new TextBlock( "Web." ) };
+        blocks = (Block[]) textParser.parse( "Web." ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected = new Block[] { new TextBlock( "fooWikiBar" ) };
+        blocks = (Block[]) textParser.parse( "fooWikiBar" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected = new Block[] { new WikiWordBlock( "WikiWord", resolver ), new TextBlock( "...." ) };
+        blocks = (Block[]) textParser.parse( "WikiWord...." ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * ...
+     */
+    public final void testWebWikiWords()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new WikiWordBlock( "Web.WikiWord", resolver ) };
+        blocks = (Block[]) textParser.parse( "Web.WikiWord" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected = new Block[] { new WikiWordBlock( "My1Web.WikiWord", resolver ) };
+        blocks = (Block[]) textParser.parse( "My1Web.WikiWord" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * ...
+     */
+    public final void testWebAnchorWikiWords()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new WikiWordBlock( "WikiWord#anchor", resolver ) };
+        blocks = (Block[]) textParser.parse( "WikiWord#anchor" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected = new Block[] { new WikiWordBlock( "MyWeb.WikiWord#anchor", resolver ) };
+        blocks = (Block[]) textParser.parse( "MyWeb.WikiWord#anchor" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+    }
+
+    /**
+     * test Specific Links
+     */
+    public final void testURLSpecificLinks()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new LinkBlock( "http://reference.com", new TextBlock( "text" ) ) };
+        blocks = (Block[]) textParser.parse( "[[http://reference.com][text]]" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected =
+            new Block[] { new TextBlock( "foo" ),
+                new LinkBlock( "http://reference.com", new TextBlock( "text" ) ), new TextBlock( "bar" ) };
+        blocks = (Block[]) textParser.parse( "foo[[http://reference.com][text]]bar" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected =
+            new Block[] { new TextBlock( " foo " ),
+                new LinkBlock( "http://reference.com", new TextBlock( "text" ) ), new TextBlock( " bar " ) };
+        blocks = (Block[]) textParser.parse( " foo [[http://reference.com][text]] bar " ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected =
+            new Block[] {
+                new LinkBlock( "http://www.apache.org/licenses/LICENSE-2.0",
+                               new TextBlock( "Apache License, version 2.0" ) ),
+                new TextBlock( ". You can download it " ),
+                new WikiWordBlock( "DoxiaDownload", new TextBlock( "here" ), resolver ) };
+        blocks =
+            (Block[]) textParser.parse(
+                                        "[[http://www.apache.org/licenses/LICENSE-2.0]"
+                                            + "[Apache License, version 2.0]]. You can download it "
+                                            + "[[DoxiaDownload][here]]" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+    }
+
+    /**
+     * test Specific Links with wikiWords
+     */
+    public final void testWikiSpecificLinks()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new WikiWordBlock( "Reference", new TextBlock( "text" ), resolver ) };
+        blocks = (Block[]) textParser.parse( "[[reference][text]]" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected =
+            new Block[] { new TextBlock( "foo" ),
+                new WikiWordBlock( "ReferenceLink", new TextBlock( "text" ), resolver ), new TextBlock( "bar" ) };
+        blocks = (Block[]) textParser.parse( "foo[[referenceLink][text]]bar" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected =
+            new Block[] { new TextBlock( " foo " ),
+                new WikiWordBlock( "ReferenceLink", new TextBlock( "text" ), resolver ), new TextBlock( " bar " ) };
+        blocks = (Block[]) textParser.parse( " foo [[reference link][text]] bar " ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * test Specific Links
+     */
+    public final void testSpecificLinkPrevention()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new TextBlock( "[[reference][text]]" ) };
+        blocks = (Block[]) textParser.parse( "![[reference][text]]" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * ...
+     */
+    public final void testPreventLinkingWikiWord()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new TextBlock( " " ), new TextBlock( "WikiWord" ), new TextBlock( " " ) };
+        blocks = (Block[]) textParser.parse( " !WikiWord " ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected = new Block[] { new TextBlock( " !!WikiWord " ) };
+        blocks = (Block[]) textParser.parse( " !!WikiWord " ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * ej [[Main.TWiki rules]] would be wikiword Main.TWikiRules
+     */
+    public final void testForcedLinks()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new WikiWordBlock( "WikiSyntax", new TextBlock( "wiki syntax" ), resolver ) };
+        blocks = (Block[]) textParser.parse( "[[wiki syntax]]" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected = new Block[] { new TextBlock( "[[wiki syntax]]" ) };
+        blocks = (Block[]) textParser.parse( "![[wiki syntax]]" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected =
+            new Block[] { new TextBlock( "foo" ),
+                new WikiWordBlock( "WikiSyntax", new TextBlock( "wiki syntax" ), resolver ),
+                new TextBlock( "bar" ) };
+        blocks = (Block[]) textParser.parse( "foo[[wiki syntax]]bar" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected =
+            new Block[] { new TextBlock( "foo" ),
+                new LinkBlock( "http://twiki.com", new TextBlock( "http://twiki.com" ) ), new TextBlock( "bar" ) };
+        blocks = (Block[]) textParser.parse( "foo[[http://twiki.com]]bar" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * ...
+     */
+    public final void testMailtoForcedLinks()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new LinkBlock( "mailto:a at z.com", new TextBlock( "Mail" ) ) };
+        blocks = (Block[]) textParser.parse( "[[mailto:a at z.com Mail]]" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * ...
+     */
+    public final void testAnchors()
+    {
+        Block[] blocks, expected;
+
+        expected = new Block[] { new TextBlock( "mary has #anchor a little lamb" ) };
+        blocks = (Block[]) textParser.parse( "mary has #anchor a little lamb" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected =
+            new Block[] { new TextBlock( "mary has " ), new AnchorBlock( "AnchorName" ),
+                new TextBlock( " a little lamb" ) };
+        blocks = (Block[]) textParser.parse( "mary has #AnchorName a little lamb" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+
+        expected = new Block[] { new TextBlock( "mary has #AnchorName1233 a little lamb" ) };
+        blocks = (Block[]) textParser.parse( "mary has #AnchorName1233 a little lamb" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * unit test
+     */
+    public final void testAutomaticLink()
+    {
+        Block[] blocks, expected;
+
+        expected =
+            new Block[] { new TextBlock( "Go to " ),
+                new LinkBlock( "http://twiki.com", new TextBlock( "http://twiki.com" ) ),
+                new TextBlock( " and ..." ) };
+        blocks = (Block[]) textParser.parse( "Go to http://twiki.com and ..." ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /** unit test */
+    public final void testAutomaticImage()
+    {
+        Block[] blocks, expected;
+
+        expected =
+            new Block[] { new LinkBlock( "http://twiki.org", new ImageBlock( "http://twiki.org/logo.png" ) ) };
+        blocks = (Block[]) textParser.parse( "[[http://twiki.org][http://twiki.org/logo.png]]" ).toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /** unit test */
+    public final void testLinkImage()
+    {
+        Block[] blocks, expected;
+
+        expected =
+            new Block[] { new TextBlock( "Go to " ), new ImageBlock( "http://twiki.com/image.png" ),
+                new TextBlock( " thisisnotanimage.png and ..." ) };
+        blocks =
+            (Block[]) textParser.parse( "Go to http://twiki.com/image.png " + "thisisnotanimage.png and ..." )
+                                .toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+
+    /**
+     * Test image inserted with a html img tag
+     */
+    public final void testRelativeImage()
+    {
+        Block[] blocks, expected;
+
+        expected =
+            new Block[] { new TextBlock( "My summer house: " ), new ImageBlock( "images/summerhouse.png" ),
+                new TextBlock( " isn't it great?!" ) };
+        blocks =
+            (Block[]) textParser
+                                .parse(
+                                        "My summer house: <img class=\"some_class\" src=\"images/summerhouse.png\"/> isn't it great?!" )
+                                .toArray( TOARRAY );
+        assertTrue( Arrays.equals( expected, blocks ) );
+    }
+}
diff --git a/doxia-modules/doxia-module-twiki/src/test/resources/TWikiParserTest.twiki b/doxia-modules/doxia-module-twiki/src/test/resources/TWikiParserTest.twiki
new file mode 100644
index 0000000..943a5b1
--- /dev/null
+++ b/doxia-modules/doxia-module-twiki/src/test/resources/TWikiParserTest.twiki
@@ -0,0 +1,109 @@
+---+ Twiki Java Parser
+
+---++ Features
+
+This parser of the [[http://www.twiki.org][TWiki]] text format supports most
+of http://twiki.org/cgi-bin/view/TWiki/TextFormattingRules formatting commands.
+
+---+++ General
+
+   * Paragraps,
+   * Wiki Words
+      * WikiWord
+      * Web.WikiWord#anchor,  
+      * escaped: !WikiWord
+   * Forced Links:
+      * [[wiki word]]
+      * escaped ![[wiki word]]
+   * Specific links: 
+      * [[http://www.zauber.com.ar][Zauber]],
+      * prevention: ![[http://www.zauber.com.ar][Forced links]]
+   * Anchors: [[#AnchorEnd][End]]
+   * inline urls:
+      * http://twiki.org/
+   * mailto link:
+      * [[mailto:a at z.com Mail]]
+      * [[mailto:?subject=Hi Hi]]
+
+---+++ Text Format:
+
+   * *bold*
+   * _italic_
+   * __bold italic__
+   * =Fixedfont=
+   * ==Bold fixed==
+
+And nested formats like:
+   * *bold with _italic_ and some =fixed= and bold*
+Make sure there is no space between the text and the bold, italic, or other
+indicators (* _ __ = ==).
+
+---+++ Lists
+
+   * items
+      * nested items
+      * ordered list
+         * arabic numerals
+            1. item
+            1. item
+            1. ...
+         * uppercase letters
+            A. item
+            A. item
+            A. ...
+         * lowercase letters
+            a. item
+            a. item
+            a. ...
+         * uppercase roman numerals
+            A. item
+            A. item
+            A. ....
+         * Uppercase Roman Numerals
+            I. item
+            I. item
+            I. ...
+         * Lowercase Roman Numerals
+            i. item
+            i. item
+            i. ....
+
+---+++ Separators
+
+Up
+---------------------------
+Down
+
+---+++ Table
+
+ | *A* | *B* | *C* |
+ | Foo | bar | Foo |
+ | Bar | Foo | bar |
+ | Foo | bar | Foo |
+
+---++ Missing things
+---+++ Verbating Mode
+<verbatim>
+class CatAnimal {
+  void purr() {
+      <code here>
+}
+</verbatim>
+
+---+++ Definition List
+(i don't use it)
+   $ Sushi: Japan
+      $ Dim Sum: S.F.
+   $ Asado: Argentina
+
+---+++ Diable Links
+
+<noautolink>
+   RedHat &
+  SuSE
+</noautolink>
+   
+---+++ Html
+    *  <pre>some text</pre>
+
+#EndAnchor
diff --git a/doxia-modules/doxia-module-xdoc/pom.xml b/doxia-modules/doxia-module-xdoc/pom.xml
new file mode 100644
index 0000000..f393ef8
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/pom.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-xdoc</artifactId>
+
+  <name>Doxia :: XDoc Module</name>
+  <description>A Doxia module for Xdoc source documents.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+
+    <!-- test -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-test-docs</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>xerces</groupId>
+      <artifactId>xercesImpl</artifactId>
+      <version>2.9.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <!-- See DOXIA-408 -->
+            <exclude>org/apache/maven/doxia/module/xdoc/XmlWriterXdocSinkTest.java</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-maven-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/main/components/components.xml</descriptor>
+            <descriptor>target/generated-resources/plexus/META-INF/plexus/components.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>merge-descriptors</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>reporting</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <phase>site</phase>
+                <configuration>
+                  <tasks>
+                    <taskdef name="xsddoc" classname="net.sf.xframe.xsddoc.Task" />
+
+                    <condition property="dir" value="${project.reporting.outputDirectory}/xsddoc">
+                      <matches string="${project.reporting.outputDirectory}" pattern="^${basedir}" />
+                    </condition>
+                    <condition property="dir" value="${project.reporting.outputDirectory}/xsddoc">
+                      <not>
+                        <isset property="dir" />
+                      </not>
+                    </condition>
+
+                    <mkdir dir="${dir}" />
+
+                    <xsddoc file="${basedir}/src/main/resources/xdoc-2.0.xsd" out="${dir}" doctitle="Reference of Schema Xdoc 2.0" verbose="true" />
+                  </tasks>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+            <dependencies>
+              <dependency>
+                <groupId>xsddoc</groupId>
+                <artifactId>xsddoc</artifactId>
+                <version>1.0</version>
+                <exclusions>
+                  <exclusion>
+                    <groupId>ant</groupId>
+                    <artifactId>ant</artifactId>
+                  </exclusion>
+                </exclusions>
+              </dependency>
+              <dependency>
+                <groupId>org.apache.ant</groupId>
+                <artifactId>ant-apache-regexp</artifactId>
+                <version>1.7.1</version>
+              </dependency>
+              <dependency>
+                <groupId>xalan</groupId>
+                <artifactId>xalan</artifactId>
+                <version>2.7.1</version>
+              </dependency>
+            </dependencies>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/doxia-modules/doxia-module-xdoc/src/main/components/components.xml b/doxia-modules/doxia-module-xdoc/src/main/components/components.xml
new file mode 100644
index 0000000..cdd85f6
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/main/components/components.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<component-set>
+  <components>
+    <component>
+      <role>org.apache.maven.doxia.parser.Parser</role>
+      <role-hint>xdoc</role-hint>
+      <implementation>org.apache.maven.doxia.module.xdoc.XdocParser</implementation>
+      <description>Parse an xdoc model and emit events into the specified doxia Sink.</description>
+      <requirements>
+        <requirement>
+          <role>org.apache.maven.doxia.macro.manager.MacroManager</role>
+          <field-name>macroManager</field-name>
+        </requirement>
+      </requirements>
+    </component>
+  </components>
+</component-set>
diff --git a/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java
new file mode 100644
index 0000000..6ec369c
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java
@@ -0,0 +1,128 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.maven.doxia.markup.HtmlMarkup;
+
+/**
+ * List of <code>Xdoc</code> markups.
+ * <br/>
+ * Xdoc uses several  {@link javax.swing.text.html.HTML.Tag} and {@link javax.swing.text.html.HTML.Attribute}
+ * as markups and custom tags.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XdocMarkup.java 739557 2009-01-31 13:54:32Z vsiveton $
+ * @since 1.0
+ */
+public interface XdocMarkup
+    extends HtmlMarkup
+{
+    /** XDOC namespace: "http://maven.apache.org/XDOC/2.0" */
+    String XDOC_NAMESPACE = "http://maven.apache.org/XDOC/2.0";
+
+    /** XDOC system id: "http://maven.apache.org/xsd/xdoc-2.0.xsd" */
+    String XDOC_SYSTEM_ID = "http://maven.apache.org/xsd/xdoc-2.0.xsd";
+
+    // ----------------------------------------------------------------------
+    // Specific Xdoc tags
+    // ----------------------------------------------------------------------
+
+    /** Xdoc tag for <code>author</code> */
+    Tag AUTHOR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "author";
+        }
+    };
+
+    /** Xdoc tag for <code>date</code> */
+    Tag DATE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "date";
+        }
+    };
+
+    /** Xdoc tag for <code>document</code> */
+    Tag DOCUMENT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "document";
+        }
+    };
+
+    /** Xdoc tag for <code>macro</code> */
+    Tag MACRO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "macro";
+        }
+    };
+
+    /** Xdoc tag for <code>properties</code> */
+    Tag PROPERTIES_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "properties";
+        }
+    };
+
+    /** Xdoc tag for <code>section</code> */
+    Tag SECTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "section";
+        }
+    };
+
+    /** Xdoc tag for <code>source</code> */
+    Tag SOURCE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "source";
+        }
+    };
+
+    /** Xdoc tag for <code>subsection</code> */
+    Tag SUBSECTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "subsection";
+        }
+    };
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocParser.java b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocParser.java
new file mode 100644
index 0000000..05e491b
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocParser.java
@@ -0,0 +1,511 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.text.html.HTML.Attribute;
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.macro.manager.MacroNotFoundException;
+import org.apache.maven.doxia.macro.MacroRequest;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.XhtmlBaseParser;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Parse an xdoc model and emit events into the specified doxia Sink.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: XdocParser.java 807182 2009-08-24 12:27:26Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="xdoc"
+ */
+public class XdocParser
+    extends XhtmlBaseParser
+    implements XdocMarkup
+{
+    /** The source content of the input reader. Used to pass into macros. */
+    private String sourceContent;
+
+    /** Empty elements don't write a closing tag. */
+    private boolean isEmptyElement;
+
+    /** A macro name. */
+    private String macroName;
+
+    /** The macro parameters. */
+    private Map macroParameters = new HashMap();
+
+    /** Indicates that we're inside <properties> or <head>.*/
+    private boolean inHead;
+
+    /** Indicates that <title> was called from <properties> or <head>.*/
+    private boolean hasTitle;
+
+    /** {@inheritDoc} */
+    public void parse( Reader source, Sink sink )
+        throws ParseException
+    {
+        this.sourceContent = null;
+        init();
+
+        try
+        {
+            StringWriter contentWriter = new StringWriter();
+            IOUtil.copy( source, contentWriter );
+            sourceContent = contentWriter.toString();
+        }
+        catch ( IOException ex )
+        {
+            throw new ParseException( "Error reading the input source: " + ex.getMessage(), ex );
+        }
+        finally
+        {
+            IOUtil.close( source );
+        }
+
+        Reader tmp = new StringReader( sourceContent );
+
+        // leave this at default (false) until everything is properly implemented, see DOXIA-226
+        //setIgnorableWhitespace( true );
+
+        try
+        {
+            super.parse( tmp, sink );
+        }
+        finally
+        {
+            this.sourceContent = null;
+
+            setSecondParsing( false );
+            init();
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleStartTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        isEmptyElement = parser.isEmptyElementTag();
+
+        SinkEventAttributeSet attribs = getAttributesFromParser( parser );
+
+        if ( parser.getName().equals( DOCUMENT_TAG.toString() ) )
+        {
+            //Do nothing
+            return;
+        }
+        else if ( parser.getName().equals( HEAD.toString() ) )
+        {
+            if ( !inHead ) // we might be in head from a <properties> already
+            {
+                this.inHead = true;
+
+                sink.head( attribs );
+            }
+        }
+        else if ( parser.getName().equals( TITLE.toString() ) )
+        {
+            if ( hasTitle )
+            {
+                getLog().warn( "<title> was already defined in <properties>, ignored <title> in <head>." );
+
+                try
+                {
+                    parser.nextText(); // ignore next text event
+                }
+                catch ( IOException ex )
+                {
+                    throw new XmlPullParserException( "Failed to parse text", parser, ex );
+                }
+            }
+            else
+            {
+                sink.title( attribs );
+            }
+        }
+        else if ( parser.getName().equals( AUTHOR_TAG.toString() ) )
+        {
+            sink.author( attribs );
+        }
+        else if ( parser.getName().equals( DATE_TAG.toString() ) )
+        {
+            sink.date( attribs );
+        }
+        else if ( parser.getName().equals( META.toString() ) )
+        {
+            handleMetaStart( parser, sink, attribs );
+        }
+        else if ( parser.getName().equals( BODY.toString() ) )
+        {
+            if ( inHead )
+            {
+                sink.head_();
+                this.inHead = false;
+            }
+
+            sink.body( attribs );
+        }
+        else if ( parser.getName().equals( SECTION_TAG.toString() ) )
+        {
+            handleSectionStart( Sink.SECTION_LEVEL_1, sink, attribs, parser );
+        }
+        else if ( parser.getName().equals( SUBSECTION_TAG.toString() ) )
+        {
+            handleSectionStart( Sink.SECTION_LEVEL_2, sink, attribs, parser );
+        }
+        else if ( parser.getName().equals( SOURCE_TAG.toString() ) )
+        {
+            verbatim();
+
+            attribs.addAttributes( SinkEventAttributeSet.BOXED );
+
+            sink.verbatim( attribs );
+        }
+        else if ( parser.getName().equals( PROPERTIES_TAG.toString() ) )
+        {
+            if ( !inHead ) // we might be in head from a <head> already
+            {
+                this.inHead = true;
+
+                sink.head( attribs );
+            }
+        }
+
+        // ----------------------------------------------------------------------
+        // Macro
+        // ----------------------------------------------------------------------
+
+        else if ( parser.getName().equals( MACRO_TAG.toString() ) )
+        {
+            handleMacroStart( parser );
+        }
+        else if ( parser.getName().equals( PARAM.toString() ) )
+        {
+            handleParamStart( parser, sink );
+        }
+        else if ( !baseStartTag( parser, sink ) )
+        {
+            if ( isEmptyElement )
+            {
+                handleUnknown( parser, sink, TAG_TYPE_SIMPLE );
+            }
+            else
+            {
+                handleUnknown( parser, sink, TAG_TYPE_START );
+            }
+
+            if ( getLog().isDebugEnabled() )
+            {
+                String position = "[" + parser.getLineNumber() + ":"
+                    + parser.getColumnNumber() + "]";
+                String tag = "<" + parser.getName() + ">";
+
+                getLog().debug( "Unrecognized xdoc tag: " + tag + " at " + position );
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleEndTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( parser.getName().equals( DOCUMENT_TAG.toString() ) )
+        {
+            //Do nothing
+            return;
+        }
+        else if ( parser.getName().equals( HEAD.toString() ) )
+        {
+            //Do nothing, head is closed with BODY start.
+        }
+        else if ( parser.getName().equals( BODY.toString() ) )
+        {
+            consecutiveSections( 0, sink );
+
+            sink.body_();
+        }
+        else if ( parser.getName().equals( TITLE.toString() ) )
+        {
+            if ( !hasTitle )
+            {
+                sink.title_();
+                this.hasTitle = true;
+            }
+        }
+        else if ( parser.getName().equals( AUTHOR_TAG.toString() ) )
+        {
+            sink.author_();
+        }
+        else if ( parser.getName().equals( DATE_TAG.toString() ) )
+        {
+            sink.date_();
+        }
+        else if ( parser.getName().equals( SOURCE_TAG.toString() ) )
+        {
+            verbatim_();
+
+            sink.verbatim_();
+        }
+        else if ( parser.getName().equals( PROPERTIES_TAG.toString() ) )
+        {
+            //Do nothing, head is closed with BODY start.
+        }
+        else if ( parser.getName().equals( MACRO_TAG.toString() ) )
+        {
+            handleMacroEnd( sink );
+        }
+        else if ( parser.getName().equals( PARAM.toString() ) )
+        {
+            if ( !StringUtils.isNotEmpty( macroName ) )
+            {
+                handleUnknown( parser, sink, TAG_TYPE_END );
+            }
+        }
+        else if ( parser.getName().equals( SECTION_TAG.toString() ) )
+        {
+            consecutiveSections( 0, sink );
+
+            sink.section1_();
+        }
+        else if ( parser.getName().equals( SUBSECTION_TAG.toString() ) )
+        {
+            consecutiveSections( Sink.SECTION_LEVEL_1, sink );
+        }
+        else if ( !baseEndTag( parser, sink ) )
+        {
+            if ( !isEmptyElement )
+            {
+                handleUnknown( parser, sink, TAG_TYPE_END );
+            }
+        }
+
+        isEmptyElement = false;
+    }
+
+    /** {@inheritDoc} */
+    protected void consecutiveSections( int newLevel, Sink sink )
+    {
+        closeOpenSections( newLevel, sink );
+        openMissingSections( newLevel, sink );
+
+        setSectionLevel( newLevel );
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.isEmptyElement = false;
+        this.macroName = null;
+        this.macroParameters = null;
+        this.inHead = false;
+        this.hasTitle = false;
+    }
+
+    /**
+     * Close open h4, h5, h6 sections.
+     */
+    private void closeOpenSections( int newLevel, Sink sink )
+    {
+        while ( getSectionLevel() >= newLevel )
+        {
+            if ( getSectionLevel() == Sink.SECTION_LEVEL_5 )
+            {
+                sink.section5_();
+            }
+            else if ( getSectionLevel() == Sink.SECTION_LEVEL_4 )
+            {
+                sink.section4_();
+            }
+            else if ( getSectionLevel() == Sink.SECTION_LEVEL_3 )
+            {
+                sink.section3_();
+            }
+            else if ( getSectionLevel() == Sink.SECTION_LEVEL_2 )
+            {
+                sink.section2_();
+            }
+
+            setSectionLevel( getSectionLevel() - 1 );
+        }
+    }
+
+    private void handleMacroEnd( Sink sink )
+            throws MacroExecutionException
+    {
+        if ( !isSecondParsing() )
+        {
+            if ( StringUtils.isNotEmpty( macroName ) )
+            {
+                // TODO handles specific macro attributes
+                macroParameters.put( "sourceContent", sourceContent );
+                XdocParser xdocParser = new XdocParser();
+                xdocParser.setSecondParsing( true );
+                macroParameters.put( "parser", xdocParser );
+
+                MacroRequest request = new MacroRequest( macroParameters, getBasedir() );
+
+                try
+                {
+                    executeMacro( macroName, request, sink );
+                } catch ( MacroNotFoundException me )
+                {
+                    throw new MacroExecutionException( "Macro not found: " + macroName, me );
+                }
+            }
+        }
+
+        // Reinit macro
+        macroName = null;
+        macroParameters = null;
+    }
+
+    private void handleMacroStart( XmlPullParser parser )
+            throws MacroExecutionException
+    {
+        if ( !isSecondParsing() )
+        {
+            macroName = parser.getAttributeValue( null, Attribute.NAME.toString() );
+
+            if ( macroParameters == null )
+            {
+                macroParameters = new HashMap();
+            }
+
+            if ( StringUtils.isEmpty( macroName ) )
+            {
+                throw new MacroExecutionException( "The '" + Attribute.NAME.toString()
+                        + "' attribute for the '" + MACRO_TAG.toString() + "' tag is required." );
+            }
+        }
+    }
+
+    private void handleMetaStart( XmlPullParser parser, Sink sink, SinkEventAttributeSet attribs )
+    {
+        String name = parser.getAttributeValue( null, Attribute.NAME.toString() );
+        String content = parser.getAttributeValue( null, Attribute.CONTENT.toString() );
+
+        if ( "author".equals( name ) )
+        {
+            sink.author( null );
+            sink.text( content );
+            sink.author_();
+        }
+        else if ( "date".equals( name ) )
+        {
+            sink.date( null );
+            sink.text( content );
+            sink.date_();
+        }
+        else
+        {
+            sink.unknown( "meta", new Object[] {new Integer( TAG_TYPE_SIMPLE )}, attribs );
+        }
+    }
+
+    private void handleParamStart( XmlPullParser parser, Sink sink )
+            throws MacroExecutionException
+    {
+        if ( !isSecondParsing() )
+        {
+            if ( StringUtils.isNotEmpty( macroName ) )
+            {
+                String paramName = parser.getAttributeValue( null, Attribute.NAME.toString() );
+                String paramValue = parser.getAttributeValue( null,
+                        Attribute.VALUE.toString() );
+
+                if ( StringUtils.isEmpty( paramName ) || StringUtils.isEmpty( paramValue ) )
+                {
+                    throw new MacroExecutionException( "'" + Attribute.NAME.toString()
+                            + "' and '" + Attribute.VALUE.toString() + "' attributes for the '" + PARAM.toString()
+                            + "' tag are required inside the '" + MACRO_TAG.toString() + "' tag." );
+                }
+
+                macroParameters.put( paramName, paramValue );
+            }
+            else
+            {
+                // param tag from non-macro object, see MSITE-288
+                handleUnknown( parser, sink, TAG_TYPE_START );
+            }
+        }
+    }
+
+    private void handleSectionStart( int level, Sink sink, SinkEventAttributeSet attribs, XmlPullParser parser )
+    {
+        consecutiveSections( level, sink );
+
+        Object id = attribs.getAttribute( Attribute.ID.toString() );
+
+        if ( id != null )
+        {
+            sink.anchor( id.toString() );
+            sink.anchor_();
+        }
+
+        sink.section( level, attribs );
+        sink.sectionTitle( level, attribs );
+        sink.text( HtmlTools.unescapeHTML( parser.getAttributeValue( null, Attribute.NAME.toString() ) ) );
+        sink.sectionTitle_( level );
+    }
+
+    /**
+     * Open missing h4, h5, h6 sections.
+     */
+    private void openMissingSections( int newLevel, Sink sink )
+    {
+        while ( getSectionLevel() < newLevel - 1 )
+        {
+            setSectionLevel( getSectionLevel() + 1 );
+
+            if ( getSectionLevel() == Sink.SECTION_LEVEL_5 )
+            {
+                sink.section5();
+            }
+            else if ( getSectionLevel() == Sink.SECTION_LEVEL_4 )
+            {
+                sink.section4();
+            }
+            else if ( getSectionLevel() == Sink.SECTION_LEVEL_3 )
+            {
+                sink.section3();
+            }
+            else if ( getSectionLevel() == Sink.SECTION_LEVEL_2 )
+            {
+                sink.section2();
+            }
+        }
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSink.java b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSink.java
new file mode 100644
index 0000000..ae17f45
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSink.java
@@ -0,0 +1,532 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML.Attribute;
+
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.sink.SinkUtils;
+import org.apache.maven.doxia.sink.XhtmlBaseSink;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * <a href="http://maven.apache.org/doxia/references/xdoc-format.html">Xdoc</a> Sink implementation.
+ * <br/>
+ * It uses the Xdoc XSD <a href="http://maven.apache.org/xsd/xdoc-2.0.xsd">
+ * http://maven.apache.org/xsd/xdoc-2.0.xsd</a>.
+ *
+ * @author <a href="mailto:james at jamestaylor.org">James Taylor</a>
+ * @version $Id: XdocSink.java 926772 2010-03-23 20:52:37Z ltheussl $
+ * @since 1.0
+ */
+public class XdocSink
+    extends XhtmlBaseSink
+    implements XdocMarkup
+{
+    // ----------------------------------------------------------------------
+    // Instance fields
+    // ----------------------------------------------------------------------
+
+    /** An indication on if we're inside a box (verbatim). */
+    private boolean boxedFlag;
+
+    private String encoding;
+
+    private String languageId;
+
+    // ----------------------------------------------------------------------
+    // Constructors
+    // ----------------------------------------------------------------------
+
+    /**
+     * Constructor, initialize the Writer.
+     *
+     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
+     * You could use <code>newXmlWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
+     */
+    protected XdocSink( Writer writer )
+    {
+        super( writer );
+    }
+
+    /**
+     * Constructor, initialize the Writer and tells which encoding is used.
+     *
+     * @param writer not null writer to write the result.
+     * @param encoding the encoding used, that should be written to the generated HTML content
+     * if not <code>null</code>.
+     * @since 1.1
+     */
+    protected XdocSink( Writer writer, String encoding )
+    {
+        this( writer );
+        this.encoding = encoding;
+    }
+
+    /**
+     * Constructor, initialize the Writer and tells which encoding and languageId are used.
+     *
+     * @param writer not null writer to write the result.
+     * @param encoding the encoding used, that should be written to the generated HTML content
+     * if not <code>null</code>.
+     * @param languageId language identifier for the root element as defined by
+     * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
+     * in addition, the empty string may be specified.
+     * @since 1.1
+     */
+    protected XdocSink( Writer writer, String encoding, String languageId )
+    {
+        this( writer, encoding );
+
+        this.languageId = languageId;
+    }
+
+    // ----------------------------------------------------------------------
+    // Public protected methods
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        boxedFlag = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see #head(org.apache.maven.doxia.sink.SinkEventAttributes)
+     */
+    public void head()
+    {
+        head( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#DOCUMENT_TAG
+     * @see XdocMarkup#PROPERTIES_TAG
+     */
+    public void head( SinkEventAttributes attributes )
+    {
+        init();
+
+        setHeadFlag( true );
+
+        write( "<?xml version=\"1.0\"" );
+        if ( encoding != null )
+        {
+            write( " encoding=\"" + encoding + "\"" );
+        }
+        write( "?>" );
+
+        MutableAttributeSet atts = new SinkEventAttributeSet();
+        atts.addAttribute( "xmlns", XDOC_NAMESPACE );
+        atts.addAttribute( "xmlns:xsi", XML_NAMESPACE );
+        atts.addAttribute( "xsi:schemaLocation", XDOC_NAMESPACE + " " + XDOC_SYSTEM_ID );
+
+        if ( languageId != null )
+        {
+            atts.addAttribute( Attribute.LANG.toString(), languageId );
+            atts.addAttribute( "xml:lang", languageId );
+        }
+
+        if ( attributes != null )
+        {
+            atts.addAttributes( attributes );
+        }
+
+        writeStartTag( DOCUMENT_TAG, atts );
+
+        writeStartTag( PROPERTIES_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#DOCUMENT_TAG
+     * @see XdocMarkup#PROPERTIES_TAG
+     */
+    public void head_()
+    {
+        setHeadFlag( false );
+
+        writeEndTag( PROPERTIES_TAG );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TITLE
+     */
+    public void title()
+    {
+        writeStartTag( TITLE );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TITLE
+     */
+    public void title_()
+    {
+        content( getTextBuffer().toString() );
+
+        writeEndTag( TITLE );
+
+        resetTextBuffer();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#AUTHOR_TAG
+     */
+    public void author_()
+    {
+        if ( getTextBuffer().length() > 0 )
+        {
+            writeStartTag( AUTHOR_TAG );
+            String text = HtmlTools.escapeHTML( getTextBuffer().toString() );
+            // hack: un-escape numerical entities that have been escaped above
+            // note that numerical entities should really be written as one unicode character in the first place
+            text = StringUtils.replace( text, "&#", "&#" );
+            write( text );
+            writeEndTag( AUTHOR_TAG );
+            resetTextBuffer();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#DATE_TAG
+     */
+    public void date_()
+    {
+        if ( getTextBuffer().length() > 0 )
+        {
+            writeStartTag( DATE_TAG );
+            content( getTextBuffer().toString() );
+            writeEndTag( DATE_TAG );
+            resetTextBuffer();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see #body(org.apache.maven.doxia.sink.SinkEventAttributes)
+     */
+    public void body()
+    {
+       body( null );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BODY
+     */
+    public void body( SinkEventAttributes attributes )
+    {
+        writeStartTag( BODY, attributes );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BODY
+     * @see XdocMarkup#DOCUMENT_TAG
+     */
+    public void body_()
+    {
+        writeEndTag( BODY );
+
+        writeEndTag( DOCUMENT_TAG );
+
+        flush();
+
+        init();
+    }
+
+    // ----------------------------------------------------------------------
+    // Sections
+    // ----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     *
+     * Starts a section.
+     * @see XdocMarkup#SECTION_TAG
+     * @see XdocMarkup#SUBSECTION_TAG
+     */
+    protected void onSection( int depth, SinkEventAttributes attributes )
+    {
+        if ( depth == SECTION_LEVEL_1 )
+        {
+            write( String.valueOf( LESS_THAN ) + SECTION_TAG.toString()
+                    + SinkUtils.getAttributeString(
+                        SinkUtils.filterAttributes( attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) )
+                    + String.valueOf( SPACE ) + Attribute.NAME + String.valueOf( EQUAL ) + String.valueOf( QUOTE ) );
+        }
+        else if ( depth == SECTION_LEVEL_2 )
+        {
+            write( String.valueOf( LESS_THAN ) + SUBSECTION_TAG.toString()
+                    + SinkUtils.getAttributeString(
+                        SinkUtils.filterAttributes( attributes, SinkUtils.SINK_BASE_ATTRIBUTES  ) )
+                    + String.valueOf( SPACE ) + Attribute.NAME + String.valueOf( EQUAL ) + String.valueOf( QUOTE ) );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Ends a section.
+     * @see XdocMarkup#SECTION_TAG
+     * @see XdocMarkup#SUBSECTION_TAG
+     */
+    protected void onSection_( int depth )
+    {
+        if ( depth == SECTION_LEVEL_1 )
+        {
+            writeEndTag( SECTION_TAG );
+        }
+        else if ( depth == SECTION_LEVEL_2 )
+        {
+            writeEndTag( SUBSECTION_TAG );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Starts a section title.
+     * @see javax.swing.text.html.HTML.Tag#H4
+     * @see javax.swing.text.html.HTML.Tag#H5
+     * @see javax.swing.text.html.HTML.Tag#H6
+     */
+    protected void onSectionTitle( int depth, SinkEventAttributes attributes )
+    {
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
+
+        if ( depth == SECTION_LEVEL_3 )
+        {
+            writeStartTag( H4, atts );
+        }
+        else if ( depth == SECTION_LEVEL_4 )
+        {
+            writeStartTag( H5, atts );
+        }
+        else if ( depth == SECTION_LEVEL_5 )
+        {
+            writeStartTag( H6, atts );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Ends a section title.
+     * @see javax.swing.text.html.HTML.Tag#H4
+     * @see javax.swing.text.html.HTML.Tag#H5
+     * @see javax.swing.text.html.HTML.Tag#H6
+     */
+    protected void onSectionTitle_( int depth )
+    {
+        if ( depth == SECTION_LEVEL_1 || depth == SECTION_LEVEL_2 )
+        {
+            write( String.valueOf( QUOTE ) + String.valueOf( GREATER_THAN ) );
+        }
+        else if ( depth == SECTION_LEVEL_3 )
+        {
+            writeEndTag( H4 );
+        }
+        else if ( depth == SECTION_LEVEL_4 )
+        {
+            writeEndTag( H5 );
+        }
+        else if ( depth == SECTION_LEVEL_5 )
+        {
+            writeEndTag( H6 );
+        }
+    }
+
+    // -----------------------------------------------------------------------
+    //
+    // -----------------------------------------------------------------------
+
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#SOURCE_TAG
+     * @see javax.swing.text.html.HTML.Tag#PRE
+     */
+    public void verbatim( SinkEventAttributes attributes )
+    {
+        setVerbatimFlag( true );
+
+        MutableAttributeSet atts = SinkUtils.filterAttributes(
+                attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES  );
+
+
+        if ( atts == null )
+        {
+            atts = new SinkEventAttributeSet();
+        }
+
+        boolean boxed = false;
+
+        if ( atts.isDefined( SinkEventAttributes.DECORATION ) )
+        {
+            boxed = "boxed".equals(
+                (String) atts.getAttribute( SinkEventAttributes.DECORATION ) );
+        }
+
+        boxedFlag = boxed;
+        atts.removeAttribute( SinkEventAttributes.DECORATION );
+
+        if ( boxed )
+        {
+            writeStartTag( SOURCE_TAG, atts );
+        }
+        else
+        {
+            atts.removeAttribute( Attribute.ALIGN.toString() );
+            writeStartTag( PRE, atts );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#SOURCE_TAG
+     * @see javax.swing.text.html.HTML.Tag#PRE
+     */
+    public void verbatim_()
+    {
+        if ( boxedFlag )
+        {
+            writeEndTag( SOURCE_TAG );
+        }
+        else
+        {
+            writeEndTag( PRE );
+        }
+
+        setVerbatimFlag( false );
+
+        boxedFlag = false;
+    }
+
+    /**
+     * The default align is <code>center</code>.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TABLE
+     */
+    public void tableRows( int[] justification, boolean grid )
+    {
+        // similar to super.tableRows( justification, grid ) but without class.
+
+        this.tableRows = true;
+
+        setCellJustif( justification );
+
+        if ( this.tableAttributes == null )
+        {
+            this.tableAttributes = new SinkEventAttributeSet( 0 );
+        }
+
+        MutableAttributeSet att = new SinkEventAttributeSet();
+
+        if ( !tableAttributes.isDefined( Attribute.BORDER.toString() ) )
+        {
+            att.addAttribute( Attribute.BORDER, ( grid ? "1" : "0" ) );
+        }
+
+        att.addAttributes( tableAttributes );
+
+        tableAttributes.removeAttributes( tableAttributes );
+
+        writeStartTag( TABLE, att );
+    }
+
+    /**
+     * The default valign is <code>top</code>.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TR
+     */
+    public void tableRow()
+    {
+        MutableAttributeSet att = new SinkEventAttributeSet();
+        att.addAttribute( Attribute.VALIGN, "top" );
+
+        writeStartTag( TR, att );
+
+        setCellCount( 0 );
+    }
+
+    public void close()
+    {
+        super.close();
+
+        init();
+    }
+
+    /**
+     * Adds a link with an optional target.
+     *
+     * @param name the link name.
+     * @param target the link target, may be null.
+     */
+    public void link( String name, String target )
+    {
+        if ( isHeadFlag() )
+        {
+            return;
+        }
+
+        MutableAttributeSet att = new SinkEventAttributeSet();
+
+        att.addAttribute( Attribute.HREF, HtmlTools.escapeHTML( name ) );
+
+        if ( target != null )
+        {
+            att.addAttribute( Attribute.TARGET, target );
+        }
+
+        writeStartTag( A, att );
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    /**
+     * Write text to output, preserving white space.
+     *
+     * @param text The text to write.
+     * @deprecated use write(String)
+     */
+    protected void markup( String text )
+    {
+        write( text );
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSinkFactory.java b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSinkFactory.java
new file mode 100644
index 0000000..abd85ad
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSinkFactory.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractXmlSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Xdoc implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XdocSinkFactory.java 739565 2009-01-31 14:39:03Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="xdoc"
+ */
+public class XdocSinkFactory
+    extends AbstractXmlSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        return new XdocSink( writer, encoding );
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding, String languageId )
+    {
+        return new XdocSink( writer, encoding, languageId );
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSiteModule.java b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSiteModule.java
new file mode 100644
index 0000000..2189f65
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSiteModule.java
@@ -0,0 +1,42 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.site.AbstractSiteModule;
+
+/**
+ * <p>XdocSiteModule class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: XdocSiteModule.java 782392 2009-06-07 14:14:16Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.module.site.SiteModule" role-hint="xdoc"
+ */
+public class XdocSiteModule
+    extends AbstractSiteModule
+{
+    /**
+     * Default constructor.
+     */
+    public XdocSiteModule()
+    {
+        super( "xdoc", "xml", "xdoc" );
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XmlWriterXdocSink.java b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XmlWriterXdocSink.java
new file mode 100644
index 0000000..7933306
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XmlWriterXdocSink.java
@@ -0,0 +1,93 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+import org.codehaus.plexus.util.xml.XMLWriter;
+import org.codehaus.plexus.util.xml.XmlUtil;
+
+/**
+ * A Doxia Sink which produces an xdoc document.
+ *
+ * @author juan <a href="mailto:james at jamestaylor.org">James Taylor</a>
+ * @author Juan F. Codagnone  (replaced println with XmlWriterXdocSink)
+ * @version $Id: XmlWriterXdocSink.java 746992 2009-02-23 12:35:59Z vsiveton $
+ * @deprecated Since 1.1, this sink is not more supported. If you are looking for a <code>Sink</code> which produces
+ * pretty formatted XML, you could use the {@link XdocSink#XdocSink(java.io.Writer)} as usual and reformat the
+ * <code>Sink</code> content produced with {@link XmlUtil#prettyFormat(java.io.Reader, java.io.Writer)}.
+ */
+public class XmlWriterXdocSink
+    extends XdocSink
+{
+    /** Writer used by Xdoc */
+    private StringWriter xdocWriter;
+
+    private XMLWriter xmlWriter;
+
+    private XmlWriterXdocSink( StringWriter out, String encoding )
+    {
+        super( out, encoding );
+        this.xdocWriter = out;
+        this.xmlWriter = new PrettyPrintXMLWriter( out );
+    }
+
+    /**
+     * <p>Constructor for XmlWriterXdocSink.</p>
+     *
+     * @param out the wanted XML Writer.
+     * @deprecated since 1.1
+     */
+    public XmlWriterXdocSink( XMLWriter out )
+    {
+        this( new StringWriter(), "UTF-8" );
+        this.xmlWriter = out;
+    }
+
+    /** {@inheritDoc} */
+    public void close()
+    {
+        super.close();
+
+        String xdocContent = xdocWriter.toString();
+        if ( getLog().isDebugEnabled() )
+        {
+            getLog().debug( "Xdoc content: " + xdocContent );
+        }
+        StringWriter formattedContent = new StringWriter();
+        try
+        {
+            XmlUtil.prettyFormat( new StringReader( xdocContent ), formattedContent );
+        }
+        catch ( IOException e )
+        {
+            if ( getLog().isDebugEnabled() )
+            {
+                getLog().error( "IOException: " + e.getMessage(), e );
+            }
+            formattedContent = new StringWriter();
+            formattedContent.write( xdocContent );
+        }
+        xmlWriter.writeMarkup( formattedContent.toString() );
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/main/resources/xdoc-2.0.xsd b/doxia-modules/doxia-module-xdoc/src/main/resources/xdoc-2.0.xsd
new file mode 100644
index 0000000..065b90e
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/main/resources/xdoc-2.0.xsd
@@ -0,0 +1,2963 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<xs:schema version="1.0"
+  xml:lang="en"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  targetNamespace="http://maven.apache.org/XDOC/2.0"
+  xmlns="http://maven.apache.org/XDOC/2.0"
+  xmlns:xml="http://www.w3.org/XML/1998/namespace"
+  elementFormDefault="qualified">
+
+  <xs:annotation>
+    <xs:documentation source="description">
+      Xdoc 2.0 XML Schema.
+
+      This is based on: Extensible HTML version 1.0 Transitional XML Schema
+      http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd
+
+      For further information, see:
+      http://maven.apache.org/doxia/references/xdoc-format.html
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================ Character mnemonic entities =========================
+
+    XHTML entity sets are identified by the PUBLIC and SYSTEM identifiers:
+
+    PUBLIC "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+    SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent"
+
+    PUBLIC "-//W3C//ENTITIES Special for XHTML//EN"
+    SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent"
+
+    PUBLIC "-//W3C//ENTITIES Symbols for XHTML//EN"
+    SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent"
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== Imported Names ====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:simpleType name="ContentType">
+    <xs:annotation>
+      <xs:documentation>
+      media type, as per [RFC2045]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="ContentTypes">
+    <xs:annotation>
+      <xs:documentation>
+      comma-separated list of media types, as per [RFC2045]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Charset">
+    <xs:annotation>
+      <xs:documentation>
+      a character encoding, as per [RFC2045]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Charsets">
+    <xs:annotation>
+      <xs:documentation>
+      a space separated list of character encodings, as per [RFC2045]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="LanguageCode">
+    <xs:annotation>
+      <xs:documentation>
+      a language code, as per [RFC3066]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:language"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Character">
+    <xs:annotation>
+      <xs:documentation>
+      a single character, as per section 2.2 of [XML]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:length value="1" fixed="true"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Number">
+    <xs:annotation>
+      <xs:documentation>
+      one or more digits
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:nonNegativeInteger">
+      <xs:pattern value="[0-9]+"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="tabindexNumber">
+    <xs:annotation>
+      <xs:documentation>
+      tabindex attribute specifies the position of the current element
+      in the tabbing order for the current document. This value must be
+      a number between 0 and 32767. User agents should ignore leading zeros.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="Number">
+      <xs:minInclusive value="0"/>
+      <xs:maxInclusive value="32767"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="LinkTypes">
+    <xs:annotation>
+      <xs:documentation>
+      space-separated list of link types
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:NMTOKENS"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="MediaDesc">
+    <xs:annotation>
+      <xs:documentation>
+      single or comma-separated list of media descriptors
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[^,]+(,\s*[^,]+)*"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="URI">
+    <xs:annotation>
+      <xs:documentation>
+      a Uniform Resource Identifier, see [RFC2396]
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:anyURI"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="UriList">
+    <xs:annotation>
+      <xs:documentation>
+      a space separated list of Uniform Resource Identifiers
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Datetime">
+    <xs:annotation>
+      <xs:documentation>
+      date and time information. ISO date format
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:dateTime"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Script">
+    <xs:annotation>
+      <xs:documentation>
+      script expression
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="StyleSheet">
+    <xs:annotation>
+      <xs:documentation>
+      style sheet data
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="Text">
+    <xs:annotation>
+      <xs:documentation>
+      used for titles etc.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="FrameTarget">
+    <xs:annotation>
+      <xs:documentation>
+      render in this frame
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:NMTOKEN">
+      <xs:pattern value="_(blank|self|parent|top)|[A-Za-z]\c*"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Length">
+    <xs:annotation>
+      <xs:documentation>
+      nn for pixels or nn% for percentage length
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[\-+]?(\d+|\d+(\.\d+)?%)"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="MultiLength">
+    <xs:annotation>
+      <xs:documentation>
+      pixel, percentage, or relative
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[\-+]?(\d+|\d+(\.\d+)?%)|[1-9]?(\d+)?\*"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Pixels">
+    <xs:annotation>
+      <xs:documentation>
+      integer representing length in pixels
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:nonNegativeInteger"/>
+  </xs:simpleType>
+
+  <xs:annotation>
+    <xs:documentation>
+    these are used for image maps
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:simpleType name="Shape">
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="rect"/>
+      <xs:enumeration value="circle"/>
+      <xs:enumeration value="poly"/>
+      <xs:enumeration value="default"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Coords">
+    <xs:annotation>
+      <xs:documentation>
+      comma separated list of lengths
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[\-+]?(\d+|\d+(\.\d+)?%)(,\s*[\-+]?(\d+|\d+(\.\d+)?%))*"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="ImgAlign">
+    <xs:annotation>
+      <xs:documentation>
+      used for object, applet, img, input and iframe
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="top"/>
+      <xs:enumeration value="middle"/>
+      <xs:enumeration value="bottom"/>
+      <xs:enumeration value="left"/>
+      <xs:enumeration value="right"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="Color">
+    <xs:annotation>
+      <xs:documentation>
+      a color using sRGB: #RRGGBB as Hex values
+
+      There are also 16 widely known color names with their sRGB values:
+
+      Black  = #000000    Green  = #008000
+      Silver = #C0C0C0    Lime   = #00FF00
+      Gray   = #808080    Olive  = #808000
+      White  = #FFFFFF    Yellow = #FFFF00
+      Maroon = #800000    Navy   = #000080
+      Red    = #FF0000    Blue   = #0000FF
+      Purple = #800080    Teal   = #008080
+      Fuchsia= #FF00FF    Aqua   = #00FFFF
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[A-Za-z]+|#[0-9A-Fa-f]{3}|#[0-9A-Fa-f]{6}"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Generic Attributes ===============================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:attributeGroup name="coreattrs">
+    <xs:annotation>
+      <xs:documentation>
+      core attributes common to most elements
+      id       document-wide unique id
+      class    space separated list of classes
+      style    associated style info
+      title    advisory title/amplification
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="id" type="xs:ID"/>
+    <xs:attribute name="class" type="xs:NMTOKENS"/>
+    <xs:attribute name="style" type="StyleSheet"/>
+    <xs:attribute name="title" type="Text"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="i18n">
+    <xs:annotation>
+      <xs:documentation>
+      internationalization attributes
+      lang        language code (backwards compatible)
+      xml:lang    language code (as per XML 1.0 spec)
+      dir         direction for weak/neutral text
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="lang" type="LanguageCode"/>
+    <xs:attribute ref="xml:lang"/>
+    <xs:attribute name="dir">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="ltr"/>
+          <xs:enumeration value="rtl"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="events">
+    <xs:annotation>
+      <xs:documentation>
+      attributes for common UI events
+      onclick     a pointer button was clicked
+      ondblclick  a pointer button was double clicked
+      onmousedown a pointer button was pressed down
+      onmouseup   a pointer button was released
+      onmousemove a pointer was moved onto the element
+      onmouseout  a pointer was moved away from the element
+      onkeypress  a key was pressed and released
+      onkeydown   a key was pressed down
+      onkeyup     a key was released
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="onclick" type="Script"/>
+    <xs:attribute name="ondblclick" type="Script"/>
+    <xs:attribute name="onmousedown" type="Script"/>
+    <xs:attribute name="onmouseup" type="Script"/>
+    <xs:attribute name="onmouseover" type="Script"/>
+    <xs:attribute name="onmousemove" type="Script"/>
+    <xs:attribute name="onmouseout" type="Script"/>
+    <xs:attribute name="onkeypress" type="Script"/>
+    <xs:attribute name="onkeydown" type="Script"/>
+    <xs:attribute name="onkeyup" type="Script"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="focus">
+    <xs:annotation>
+      <xs:documentation>
+      attributes for elements that can get the focus
+      accesskey   accessibility key character
+      tabindex    position in tabbing order
+      onfocus     the element got the focus
+      onblur      the element lost the focus
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="accesskey" type="Character"/>
+    <xs:attribute name="tabindex" type="tabindexNumber"/>
+    <xs:attribute name="onfocus" type="Script"/>
+    <xs:attribute name="onblur" type="Script"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="attrs">
+    <xs:attributeGroup ref="coreattrs"/>
+    <xs:attributeGroup ref="i18n"/>
+    <xs:attributeGroup ref="events"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="TextAlign">
+    <xs:annotation>
+      <xs:documentation>
+      text alignment for p, div, h1-h6. The default is
+      align="left" for ltr headings, "right" for rtl
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="align">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="left"/>
+          <xs:enumeration value="center"/>
+          <xs:enumeration value="right"/>
+          <xs:enumeration value="justify"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:attributeGroup>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Text Elements ====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:group name="special.extra">
+    <xs:choice>
+      <xs:element ref="object"/>
+      <xs:element ref="applet"/>
+      <xs:element ref="img"/>
+      <xs:element ref="map"/>
+      <xs:element ref="iframe"/>
+      <xs:element ref="source"/> <!-- Xdoc specific -->
+      <xs:element ref="macro"/> <!-- Xdoc specific -->
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="special.basic">
+    <xs:choice>
+      <xs:element ref="br"/>
+      <xs:element ref="span"/>
+      <xs:element ref="bdo"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="special">
+    <xs:choice>
+      <xs:group ref="special.basic"/>
+      <xs:group ref="special.extra"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="fontstyle.extra">
+    <xs:choice>
+      <xs:element ref="big"/>
+      <xs:element ref="small"/>
+      <xs:element ref="font"/>
+      <xs:element ref="basefont"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="fontstyle.basic">
+    <xs:choice>
+      <xs:element ref="tt"/>
+      <xs:element ref="i"/>
+      <xs:element ref="b"/>
+      <xs:element ref="u"/>
+      <xs:element ref="s"/>
+      <xs:element ref="strike"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="fontstyle">
+    <xs:choice>
+      <xs:group ref="fontstyle.basic"/>
+      <xs:group ref="fontstyle.extra"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="phrase.extra">
+    <xs:choice>
+      <xs:element ref="sub"/>
+      <xs:element ref="sup"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="phrase.basic">
+    <xs:choice>
+      <xs:element ref="em"/>
+      <xs:element ref="strong"/>
+      <xs:element ref="dfn"/>
+      <xs:element ref="code"/>
+      <xs:element ref="q"/>
+      <xs:element ref="samp"/>
+      <xs:element ref="kbd"/>
+      <xs:element ref="var"/>
+      <xs:element ref="cite"/>
+      <xs:element ref="abbr"/>
+      <xs:element ref="acronym"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="phrase">
+    <xs:choice>
+      <xs:group ref="phrase.basic"/>
+      <xs:group ref="phrase.extra"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="inline.forms">
+    <xs:choice>
+      <xs:element ref="input"/>
+      <xs:element ref="select"/>
+      <xs:element ref="textarea"/>
+      <xs:element ref="label"/>
+      <xs:element ref="button"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="misc.inline">
+    <xs:annotation>
+      <xs:documentation>
+      these can only occur at block level
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice>
+      <xs:element ref="ins"/>
+      <xs:element ref="del"/>
+      <xs:element ref="script"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="misc">
+    <xs:annotation>
+      <xs:documentation>
+      these can only occur at block level
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice>
+      <xs:element ref="noscript"/>
+      <xs:group ref="misc.inline"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="inline">
+    <xs:choice>
+      <xs:element ref="a"/>
+      <xs:group ref="special"/>
+      <xs:group ref="fontstyle"/>
+      <xs:group ref="phrase"/>
+      <xs:group ref="inline.forms"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:complexType name="Inline" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      "Inline" covers inline or "text-level" element
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:group ref="inline"/>
+      <xs:group ref="misc.inline"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== Block level elements ==============================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:group name="heading">
+    <xs:choice>
+      <xs:element ref="h1"/>
+      <xs:element ref="h2"/>
+      <xs:element ref="h3"/>
+      <xs:element ref="h4"/>
+      <xs:element ref="h5"/>
+      <xs:element ref="h6"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="lists">
+    <xs:choice>
+      <xs:element ref="ul"/>
+      <xs:element ref="ol"/>
+      <xs:element ref="dl"/>
+      <xs:element ref="menu"/>
+      <xs:element ref="dir"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="blocktext">
+    <xs:choice>
+      <xs:element ref="pre"/>
+      <xs:element ref="hr"/>
+      <xs:element ref="blockquote"/>
+      <xs:element ref="address"/>
+      <xs:element ref="center"/>
+      <xs:element ref="noframes"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:group name="block">
+    <xs:choice>
+      <xs:element ref="p"/>
+      <xs:group ref="heading"/>
+      <xs:element ref="div"/>
+      <xs:group ref="lists"/>
+      <xs:group ref="blocktext"/>
+      <xs:element ref="isindex"/>
+      <xs:element ref="fieldset"/>
+      <xs:element ref="table"/>
+    </xs:choice>
+  </xs:group>
+
+  <xs:complexType name="Flow" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      "Flow" mixes block and inline and is used for list items etc.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:group ref="block"/>
+      <xs:element ref="form"/>
+      <xs:group ref="inline"/>
+      <xs:group ref="misc"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== Content models for exclusions =====================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:complexType name="a.content" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      a elements use "Inline" excluding a
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:group ref="special"/>
+      <xs:group ref="fontstyle"/>
+      <xs:group ref="phrase"/>
+      <xs:group ref="inline.forms"/>
+      <xs:group ref="misc.inline"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:complexType name="pre.content" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      pre uses "Inline" excluding img, object, applet, big, small,
+      font, or basefont
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element ref="a"/>
+      <xs:group ref="special.basic"/>
+      <xs:group ref="fontstyle.basic"/>
+      <xs:group ref="phrase.basic"/>
+      <xs:group ref="inline.forms"/>
+      <xs:group ref="misc.inline"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:complexType name="form.content" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      form uses "Flow" excluding form
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:group ref="block"/>
+      <xs:group ref="inline"/>
+      <xs:group ref="misc"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:complexType name="button.content" mixed="true">
+    <xs:annotation>
+      <xs:documentation>
+      button uses "Flow" but excludes a, form, form controls, iframe
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element ref="p"/>
+      <xs:group ref="heading"/>
+      <xs:element ref="div"/>
+      <xs:group ref="lists"/>
+      <xs:group ref="blocktext"/>
+      <xs:element ref="table"/>
+      <xs:element ref="br"/>
+      <xs:element ref="span"/>
+      <xs:element ref="bdo"/>
+      <xs:element ref="object"/>
+      <xs:element ref="applet"/>
+      <xs:element ref="img"/>
+      <xs:element ref="map"/>
+      <xs:group ref="fontstyle"/>
+      <xs:group ref="phrase"/>
+      <xs:group ref="misc"/>
+    </xs:choice>
+  </xs:complexType>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================ Document Head =======================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:group name="head.misc">
+    <xs:sequence>
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element ref="script"/>
+        <xs:element ref="style"/>
+        <xs:element ref="meta"/>
+        <xs:element ref="link"/>
+        <xs:element ref="object"/>
+        <xs:element ref="isindex"/>
+      </xs:choice>
+    </xs:sequence>
+  </xs:group>
+
+  <xs:element name="head">
+    <xs:annotation>
+      <xs:documentation>
+      content model is "head.misc" combined with a single
+      title and an optional base element in any order
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:group ref="head.misc"/>
+        <xs:choice>
+          <xs:sequence>
+            <xs:element ref="title"/>
+            <xs:group ref="head.misc"/>
+            <xs:sequence minOccurs="0">
+              <xs:element ref="base"/>
+              <xs:group ref="head.misc"/>
+            </xs:sequence>
+          </xs:sequence>
+          <xs:sequence>
+            <xs:element ref="base"/>
+            <xs:group ref="head.misc"/>
+            <xs:element ref="title"/>
+            <xs:group ref="head.misc"/>
+          </xs:sequence>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="profile" type="URI"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="title">
+    <xs:annotation>
+      <xs:documentation>
+      The title element is not considered part of the flow of text.
+      It should be displayed, for example as the page header or
+      window title. Exactly one title is required per document.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:ID"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="base">
+    <xs:annotation>
+      <xs:documentation>
+      document base URI
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="href" type="URI"/>
+      <xs:attribute name="target" type="FrameTarget"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="meta">
+    <xs:annotation>
+      <xs:documentation>
+      generic metainformation
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="http-equiv"/>
+      <xs:attribute name="name"/>
+      <xs:attribute name="content" use="required"/>
+      <xs:attribute name="scheme"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="link">
+    <xs:annotation>
+      <xs:documentation>
+      Relationship values can be used in principle:
+
+      a) for document specific toolbars/menus when used
+         with the link element in document head e.g.
+           start, contents, previous, next, index, end, help
+      b) to link to a separate style sheet (rel="stylesheet")
+      c) to make a link to a script (rel="script")
+      d) by stylesheets to control how collections of
+         html nodes are rendered into printed documents
+      e) to make a link to a printable version of this document
+         e.g. a PostScript or PDF version (rel="alternate" media="print")
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="charset" type="Charset"/>
+      <xs:attribute name="href" type="URI"/>
+      <xs:attribute name="hreflang" type="LanguageCode"/>
+      <xs:attribute name="type" type="ContentType"/>
+      <xs:attribute name="rel" type="LinkTypes"/>
+      <xs:attribute name="rev" type="LinkTypes"/>
+      <xs:attribute name="media" type="MediaDesc"/>
+      <xs:attribute name="target" type="FrameTarget"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="style">
+    <xs:annotation>
+      <xs:documentation>
+      style info, which may include CDATA sections
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="type" use="required" type="ContentType"/>
+      <xs:attribute name="media" type="MediaDesc"/>
+      <xs:attribute name="title" type="Text"/>
+      <xs:attribute ref="xml:space" fixed="preserve"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="script">
+    <xs:annotation>
+      <xs:documentation>
+      script statements, which may include CDATA sections
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="charset" type="Charset"/>
+      <xs:attribute name="type" use="required" type="ContentType"/>
+      <xs:attribute name="language"/>
+      <xs:attribute name="src" type="URI"/>
+      <xs:attribute name="defer">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="defer"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute ref="xml:space" fixed="preserve"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="noscript">
+    <xs:annotation>
+      <xs:documentation>
+      alternate content container for non script-based rendering
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ======================= Frames =======================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="iframe">
+    <xs:annotation>
+      <xs:documentation>
+      inline subwindow
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="coreattrs"/>
+          <xs:attribute name="longdesc" type="URI"/>
+          <xs:attribute name="name" type="xs:NMTOKEN"/>
+          <xs:attribute name="src" type="URI"/>
+          <xs:attribute name="frameborder" default="1">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="1"/>
+                <xs:enumeration value="0"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="marginwidth" type="Pixels"/>
+          <xs:attribute name="marginheight" type="Pixels"/>
+          <xs:attribute name="scrolling" default="auto">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="yes"/>
+                <xs:enumeration value="no"/>
+                <xs:enumeration value="auto"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="align" type="ImgAlign"/>
+          <xs:attribute name="height" type="Length"/>
+          <xs:attribute name="width" type="Length"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="noframes">
+    <xs:annotation>
+      <xs:documentation>
+      alternate content container for non frame-based rendering
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Document Body ====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="div">
+    <xs:annotation>
+      <xs:documentation>
+      generic language/style container
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Paragraphs =======================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="p">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Headings =========================================
+
+    There are six levels of headings from h1 (the most important)
+    to h6 (the least important).
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="h1">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h2">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h3">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h4">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h5">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="h6">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="TextAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Lists ============================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:simpleType name="ULStyle">
+    <xs:annotation>
+      <xs:documentation>
+      Unordered list bullet styles
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="disc"/>
+      <xs:enumeration value="square"/>
+      <xs:enumeration value="circle"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="ul">
+    <xs:annotation>
+      <xs:documentation>
+      Unordered list
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="li"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="type" type="ULStyle"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="OLStyle">
+    <xs:annotation>
+      <xs:documentation>
+      Ordered list numbering style
+
+      1   arabic numbers      1, 2, 3, ...
+      a   lower alpha         a, b, c, ...
+      A   upper alpha         A, B, C, ...
+      i   lower roman         i, ii, iii, ...
+      I   upper roman         I, II, III, ...
+
+      The style is applied to the sequence number which by default
+      is reset to 1 for the first list item in an ordered list.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:element name="ol">
+    <xs:annotation>
+      <xs:documentation>
+      Ordered (numbered) list
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="li"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="type" type="OLStyle"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="start" type="Number"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="menu">
+    <xs:annotation>
+      <xs:documentation>
+      single column list (DEPRECATED)
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="li"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="dir">
+    <xs:annotation>
+      <xs:documentation>
+      multiple column list (DEPRECATED)
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="li"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="LIStyle">
+    <xs:annotation>
+      <xs:documentation>
+      LIStyle is constrained to: "(ULStyle|OLStyle)"
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+
+  <xs:element name="li">
+    <xs:annotation>
+      <xs:documentation>
+      list item
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="type" type="LIStyle"/>
+          <xs:attribute name="value" type="Number"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    definition lists - dt for term, dd for its definition
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="dl">
+    <xs:complexType>
+      <xs:choice maxOccurs="unbounded">
+        <xs:element ref="dt"/>
+        <xs:element ref="dd"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="compact">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="compact"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="dt">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="dd">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Address ==========================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="address">
+    <xs:annotation>
+      <xs:documentation>
+      information on author
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:group ref="inline"/>
+        <xs:group ref="misc.inline"/>
+        <xs:element ref="p"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Horizontal Rule ==================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="hr">
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="align">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="left"/>
+            <xs:enumeration value="center"/>
+            <xs:enumeration value="right"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="noshade">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="noshade"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="size" type="Pixels"/>
+      <xs:attribute name="width" type="Length"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Preformatted Text ================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="pre">
+    <xs:annotation>
+      <xs:documentation>
+      content is "Inline" excluding
+         "img|object|applet|big|small|sub|sup|font|basefont"
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="pre.content">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="width" type="Number"/>
+          <xs:attribute ref="xml:space" fixed="preserve"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Block-like Quotes ================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="blockquote">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="cite" type="URI"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Text alignment ===================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="center">
+    <xs:annotation>
+      <xs:documentation>
+      center content
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Inserted/Deleted Text ============================
+
+    ins/del are allowed in block and inline content, but its
+    inappropriate to include block content within an ins element
+    occurring in inline content.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="ins">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="cite" type="URI"/>
+          <xs:attribute name="datetime" type="Datetime"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="del">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="cite" type="URI"/>
+          <xs:attribute name="datetime" type="Datetime"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== The Anchor Element ================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="a">
+    <xs:annotation>
+      <xs:documentation>
+      content is "Inline" except that anchors shouldn't be nested
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="a.content">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="focus"/>
+          <xs:attribute name="charset" type="Charset"/>
+          <xs:attribute name="type" type="ContentType"/>
+          <xs:attribute name="name" type="xs:NMTOKEN"/>
+          <xs:attribute name="href" type="URI"/>
+          <xs:attribute name="hreflang" type="LanguageCode"/>
+          <xs:attribute name="rel" type="LinkTypes"/>
+          <xs:attribute name="rev" type="LinkTypes"/>
+          <xs:attribute name="shape" default="rect" type="Shape"/>
+          <xs:attribute name="coords" type="Coords"/>
+          <xs:attribute name="target" type="FrameTarget"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ===================== Inline Elements ================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="span">
+    <xs:annotation>
+      <xs:documentation>
+      generic language/style container
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="bdo">
+    <xs:annotation>
+      <xs:documentation>
+      I18N BiDi over-ride
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="coreattrs"/>
+          <xs:attributeGroup ref="events"/>
+          <xs:attribute name="lang" type="LanguageCode"/>
+          <xs:attribute ref="xml:lang"/>
+          <xs:attribute name="dir" use="required">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="ltr"/>
+                <xs:enumeration value="rtl"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="br">
+    <xs:annotation>
+      <xs:documentation>
+      forced line break
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="coreattrs"/>
+      <xs:attribute name="clear" default="none">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="left"/>
+            <xs:enumeration value="all"/>
+            <xs:enumeration value="right"/>
+            <xs:enumeration value="none"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="em">
+    <xs:annotation>
+      <xs:documentation>
+      emphasis
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="strong">
+    <xs:annotation>
+      <xs:documentation>
+      strong emphasis
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="dfn">
+    <xs:annotation>
+      <xs:documentation>
+      definitional
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="code">
+    <xs:annotation>
+      <xs:documentation>
+      program code
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="samp">
+    <xs:annotation>
+      <xs:documentation>
+      sample
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="kbd">
+    <xs:annotation>
+      <xs:documentation>
+      something user would type
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="var">
+    <xs:annotation>
+      <xs:documentation>
+      variable
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="cite">
+    <xs:annotation>
+      <xs:documentation>
+      citation
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="abbr">
+    <xs:annotation>
+      <xs:documentation>
+      abbreviation
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="acronym">
+    <xs:annotation>
+      <xs:documentation>
+      acronym
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="q">
+    <xs:annotation>
+      <xs:documentation>
+      inlined quote
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="cite" type="URI"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="sub">
+    <xs:annotation>
+      <xs:documentation>
+      subscript
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="sup">
+    <xs:annotation>
+      <xs:documentation>
+      superscript
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="tt">
+    <xs:annotation>
+      <xs:documentation>
+      fixed pitch font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="i">
+    <xs:annotation>
+      <xs:documentation>
+      italic font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="b">
+    <xs:annotation>
+      <xs:documentation>
+      bold font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="big">
+    <xs:annotation>
+      <xs:documentation>
+      bigger font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="small">
+    <xs:annotation>
+      <xs:documentation>
+      smaller font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="u">
+    <xs:annotation>
+      <xs:documentation>
+      underline
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="s">
+    <xs:annotation>
+      <xs:documentation>
+      strike-through
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="strike">
+    <xs:annotation>
+      <xs:documentation>
+      strike-through
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="basefont">
+    <xs:annotation>
+      <xs:documentation>
+      base font size
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="size" use="required"/>
+      <xs:attribute name="color" type="Color"/>
+      <xs:attribute name="face"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="font">
+    <xs:annotation>
+      <xs:documentation>
+      local change to font
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="coreattrs"/>
+          <xs:attributeGroup ref="i18n"/>
+          <xs:attribute name="size"/>
+          <xs:attribute name="color" type="Color"/>
+          <xs:attribute name="face"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ==================== Object ======================================
+
+    object is used to embed objects as part of HTML pages.
+    param elements should precede other content. Parameters
+    can also be expressed as attribute/value pairs on the
+    object element itself when brevity is desired.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="object">
+    <xs:complexType mixed="true">
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element ref="param"/>
+        <xs:group ref="block"/>
+        <xs:element ref="form"/>
+        <xs:group ref="inline"/>
+        <xs:group ref="misc"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="declare">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="declare"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="classid" type="URI"/>
+      <xs:attribute name="codebase" type="URI"/>
+      <xs:attribute name="data" type="URI"/>
+      <xs:attribute name="type" type="ContentType"/>
+      <xs:attribute name="codetype" type="ContentType"/>
+      <xs:attribute name="archive" type="UriList"/>
+      <xs:attribute name="standby" type="Text"/>
+      <xs:attribute name="height" type="Length"/>
+      <xs:attribute name="width" type="Length"/>
+      <xs:attribute name="usemap" type="URI"/>
+      <xs:attribute name="name" type="xs:NMTOKEN"/>
+      <xs:attribute name="tabindex" type="Number"/>
+      <xs:attribute name="align" type="ImgAlign"/>
+      <xs:attribute name="border" type="Pixels"/>
+      <xs:attribute name="hspace" type="Pixels"/>
+      <xs:attribute name="vspace" type="Pixels"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="param">
+    <xs:annotation>
+      <xs:documentation>
+      param is used to supply a named property value.
+      In XML it would seem natural to follow RDF and support an
+      abbreviated syntax where the param elements are replaced
+      by attribute value pairs on the object start tag.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attribute name="id" type="xs:ID"/>
+      <xs:attribute name="name" use="required"/>
+      <xs:attribute name="value"/>
+      <xs:attribute name="valuetype" default="data">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="data"/>
+            <xs:enumeration value="ref"/>
+            <xs:enumeration value="object"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="type" type="ContentType"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Java applet ==================================
+
+    One of code or object attributes must be present.
+    Place param elements before other content.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="applet">
+    <xs:complexType mixed="true">
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element ref="param"/>
+        <xs:group ref="block"/>
+        <xs:element ref="form"/>
+        <xs:group ref="inline"/>
+        <xs:group ref="misc"/>
+      </xs:choice>
+      <xs:attributeGroup ref="coreattrs"/>
+      <xs:attribute name="codebase" type="URI"/>
+      <xs:attribute name="archive"/>
+      <xs:attribute name="code"/>
+      <xs:attribute name="object"/>
+      <xs:attribute name="alt" type="Text"/>
+      <xs:attribute name="name" type="xs:NMTOKEN"/>
+      <xs:attribute name="width" use="required" type="Length"/>
+      <xs:attribute name="height" use="required" type="Length"/>
+      <xs:attribute name="align" type="ImgAlign"/>
+      <xs:attribute name="hspace" type="Pixels"/>
+      <xs:attribute name="vspace" type="Pixels"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    =================== Images ===========================================
+
+    To avoid accessibility problems for people who aren't
+    able to see the image, you should provide a text
+    description using the alt and longdesc attributes.
+    In addition, avoid the use of server-side image maps.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="img">
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="src" use="required" type="URI"/>
+      <xs:attribute name="alt" use="required" type="Text"/>
+      <xs:attribute name="name" type="xs:NMTOKEN"/>
+      <xs:attribute name="longdesc" type="URI"/>
+      <xs:attribute name="height" type="Length"/>
+      <xs:attribute name="width" type="Length"/>
+      <xs:attribute name="usemap" type="URI">
+  <xs:annotation>
+    <xs:documentation>
+          usemap points to a map element which may be in this document
+          or an external document, although the latter is not widely supported
+          </xs:documentation>
+  </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="ismap">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="ismap"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="align" type="ImgAlign"/>
+      <xs:attribute name="border" type="Length"/>
+      <xs:attribute name="hspace" type="Pixels"/>
+      <xs:attribute name="vspace" type="Pixels"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================== Client-side image maps ============================
+
+    These can be placed in the same document or grouped in a
+    separate document although this isn't yet widely supported
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="map">
+    <xs:complexType>
+      <xs:choice>
+        <xs:choice maxOccurs="unbounded">
+          <xs:group ref="block"/>
+          <xs:element ref="form"/>
+          <xs:group ref="misc"/>
+        </xs:choice>
+        <xs:element maxOccurs="unbounded" ref="area"/>
+      </xs:choice>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attributeGroup ref="events"/>
+      <xs:attribute name="id" use="required" type="xs:ID"/>
+      <xs:attribute name="class"/>
+      <xs:attribute name="style" type="StyleSheet"/>
+      <xs:attribute name="title" type="Text"/>
+      <xs:attribute name="name"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="area">
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="focus"/>
+      <xs:attribute name="shape" default="rect" type="Shape"/>
+      <xs:attribute name="coords" type="Coords"/>
+      <xs:attribute name="href" type="URI"/>
+      <xs:attribute name="nohref">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="nohref"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="alt" use="required" type="Text"/>
+      <xs:attribute name="target" type="FrameTarget"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ================ Forms ===============================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="form">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="form.content">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="action" use="required" type="URI"/>
+          <xs:attribute name="method" default="get">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="get"/>
+                <xs:enumeration value="post"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="enctype" type="ContentType" default="application/x-www-form-urlencoded"/>
+          <xs:attribute name="onsubmit" type="Script"/>
+          <xs:attribute name="onreset" type="Script"/>
+          <xs:attribute name="accept" type="ContentTypes"/>
+          <xs:attribute name="accept-charset" type="Charsets"/>
+          <xs:attribute name="target" type="FrameTarget"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="label">
+    <xs:annotation>
+      <xs:documentation>
+      Each label must not contain more than ONE field
+      Label elements shouldn't be nested.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="for" type="xs:IDREF"/>
+          <xs:attribute name="accesskey" type="Character"/>
+          <xs:attribute name="onfocus" type="Script"/>
+          <xs:attribute name="onblur" type="Script"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="InputType">
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="text"/>
+      <xs:enumeration value="password"/>
+      <xs:enumeration value="checkbox"/>
+      <xs:enumeration value="radio"/>
+      <xs:enumeration value="submit"/>
+      <xs:enumeration value="reset"/>
+      <xs:enumeration value="file"/>
+      <xs:enumeration value="hidden"/>
+      <xs:enumeration value="image"/>
+      <xs:enumeration value="button"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="input">
+    <xs:annotation>
+      <xs:documentation>
+      form control
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="focus"/>
+      <xs:attribute name="type" default="text" type="InputType"/>
+      <xs:attribute name="name">
+  <xs:annotation>
+    <xs:documentation>
+          the name attribute is required for all but submit & reset
+          </xs:documentation>
+  </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="value"/>
+      <xs:attribute name="checked">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="checked"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="readonly">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="readonly"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="size"/>
+      <xs:attribute name="maxlength" type="Number"/>
+      <xs:attribute name="src" type="URI"/>
+      <xs:attribute name="alt"/>
+      <xs:attribute name="usemap" type="URI"/>
+      <xs:attribute name="onselect" type="Script"/>
+      <xs:attribute name="onchange" type="Script"/>
+      <xs:attribute name="accept" type="ContentTypes"/>
+      <xs:attribute name="align" type="ImgAlign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="select">
+    <xs:annotation>
+      <xs:documentation>
+      option selector
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:choice maxOccurs="unbounded">
+        <xs:element ref="optgroup"/>
+        <xs:element ref="option"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="name"/>
+      <xs:attribute name="size" type="Number"/>
+      <xs:attribute name="multiple">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="multiple"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="tabindex" type="tabindexNumber"/>
+      <xs:attribute name="onfocus" type="Script"/>
+      <xs:attribute name="onblur" type="Script"/>
+      <xs:attribute name="onchange" type="Script"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="optgroup">
+    <xs:annotation>
+      <xs:documentation>
+      option group
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="option"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="label" use="required" type="Text"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="option">
+    <xs:annotation>
+      <xs:documentation>
+      selectable choice
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="selected">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="selected"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="label" type="Text"/>
+      <xs:attribute name="value"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="textarea">
+    <xs:annotation>
+      <xs:documentation>
+      multi-line text field
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="focus"/>
+      <xs:attribute name="name"/>
+      <xs:attribute name="rows" use="required" type="Number"/>
+      <xs:attribute name="cols" use="required" type="Number"/>
+      <xs:attribute name="disabled">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="disabled"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="readonly">
+        <xs:simpleType>
+          <xs:restriction base="xs:token">
+            <xs:enumeration value="readonly"/>
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="onselect" type="Script"/>
+      <xs:attribute name="onchange" type="Script"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="fieldset">
+    <xs:annotation>
+      <xs:documentation>
+      The fieldset element is used to group form fields.
+      Only one legend element should occur in the content
+      and if present should only be preceded by whitespace.
+
+      NOTE: this content model is different from the XHTML 1.0 DTD,
+      closer to the intended content model in HTML4 DTD
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:sequence>
+        <xs:element ref="legend"/>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:group ref="block"/>
+          <xs:element ref="form"/>
+          <xs:group ref="inline"/>
+          <xs:group ref="misc"/>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="LAlign">
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="top"/>
+      <xs:enumeration value="bottom"/>
+      <xs:enumeration value="left"/>
+      <xs:enumeration value="right"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="legend">
+    <xs:annotation>
+      <xs:documentation>
+      fieldset label
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="accesskey" type="Character"/>
+          <xs:attribute name="align" type="LAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="button">
+    <xs:annotation>
+      <xs:documentation>
+      Content is "Flow" excluding a, form and form controls
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="button.content">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attributeGroup ref="focus"/>
+          <xs:attribute name="name"/>
+          <xs:attribute name="value"/>
+          <xs:attribute name="type" default="submit">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="button"/>
+                <xs:enumeration value="submit"/>
+                <xs:enumeration value="reset"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="disabled">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="disabled"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="isindex">
+    <xs:annotation>
+      <xs:documentation>
+      single-line text input control (DEPRECATED)
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="coreattrs"/>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="prompt" type="Text"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    ======================= Tables =======================================
+
+    Derived from IETF HTML table standard, see [RFC1942]
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:simpleType name="TFrame">
+    <xs:annotation>
+      <xs:documentation>
+      The border attribute sets the thickness of the frame around the
+      table. The default units are screen pixels.
+
+      The frame attribute specifies which parts of the frame around
+      the table should be rendered. The values are not the same as
+      CALS to avoid a name clash with the valign attribute.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="void"/>
+      <xs:enumeration value="above"/>
+      <xs:enumeration value="below"/>
+      <xs:enumeration value="hsides"/>
+      <xs:enumeration value="lhs"/>
+      <xs:enumeration value="rhs"/>
+      <xs:enumeration value="vsides"/>
+      <xs:enumeration value="box"/>
+      <xs:enumeration value="border"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="TRules">
+    <xs:annotation>
+      <xs:documentation>
+      The rules attribute defines which rules to draw between cells:
+
+      If rules is absent then assume:
+          "none" if border is absent or border="0" otherwise "all"
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="none"/>
+      <xs:enumeration value="groups"/>
+      <xs:enumeration value="rows"/>
+      <xs:enumeration value="cols"/>
+      <xs:enumeration value="all"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:simpleType name="TAlign">
+    <xs:annotation>
+      <xs:documentation>
+      horizontal placement of table relative to document
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="left"/>
+      <xs:enumeration value="center"/>
+      <xs:enumeration value="right"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:attributeGroup name="cellhalign">
+    <xs:annotation>
+      <xs:documentation>
+      horizontal alignment attributes for cell contents
+
+      char        alignment char, e.g. char=':'
+      charoff     offset for alignment char
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="align">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="left"/>
+          <xs:enumeration value="center"/>
+          <xs:enumeration value="right"/>
+          <xs:enumeration value="justify"/>
+          <xs:enumeration value="char"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+    <xs:attribute name="char" type="Character"/>
+    <xs:attribute name="charoff" type="Length"/>
+  </xs:attributeGroup>
+
+  <xs:attributeGroup name="cellvalign">
+    <xs:annotation>
+      <xs:documentation>
+      vertical alignment attributes for cell contents
+      </xs:documentation>
+    </xs:annotation>
+    <xs:attribute name="valign">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="top"/>
+          <xs:enumeration value="middle"/>
+          <xs:enumeration value="bottom"/>
+          <xs:enumeration value="baseline"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:attributeGroup>
+
+  <xs:element name="table">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element minOccurs="0" ref="caption"/>
+        <xs:choice>
+          <xs:element minOccurs="0" maxOccurs="unbounded" ref="col"/>
+          <xs:element minOccurs="0" maxOccurs="unbounded" ref="colgroup"/>
+        </xs:choice>
+        <xs:element minOccurs="0" ref="thead"/>
+        <xs:element minOccurs="0" ref="tfoot"/>
+        <xs:choice>
+          <xs:element maxOccurs="unbounded" ref="tbody"/>
+          <xs:element maxOccurs="unbounded" ref="tr"/>
+        </xs:choice>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="summary" type="Text"/>
+      <xs:attribute name="width" type="Length"/>
+      <xs:attribute name="border" type="Pixels"/>
+      <xs:attribute name="frame" type="TFrame"/>
+      <xs:attribute name="rules" type="TRules"/>
+      <xs:attribute name="cellspacing" type="Length"/>
+      <xs:attribute name="cellpadding" type="Length"/>
+      <xs:attribute name="align" type="TAlign"/>
+      <xs:attribute name="bgcolor" type="Color"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="CAlign">
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="top"/>
+      <xs:enumeration value="bottom"/>
+      <xs:enumeration value="left"/>
+      <xs:enumeration value="right"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="caption">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Inline">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="align" type="CAlign"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+    Use thead to duplicate headers when breaking table
+    across page boundaries, or for static headers when
+    tbody sections are rendered in scrolling panel.
+
+    Use tfoot to duplicate footers when breaking table
+    across page boundaries, or for static footers when
+    tbody sections are rendered in scrolling panel.
+
+    Use multiple tbody sections when rules are needed
+    between groups of table rows.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="thead">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="tr"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="tfoot">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="tr"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="tbody">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="tr"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="colgroup">
+    <xs:annotation>
+      <xs:documentation>
+      colgroup groups a set of col elements. It allows you to group
+      several semantically related columns together.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element minOccurs="0" maxOccurs="unbounded" ref="col"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="span" default="1" type="Number"/>
+      <xs:attribute name="width" type="MultiLength"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="col">
+    <xs:annotation>
+      <xs:documentation>
+      col elements define the alignment properties for cells in
+      one or more columns.
+
+      The width attribute specifies the width of the columns, e.g.
+
+          width=64        width in screen pixels
+          width=0.5*      relative width of 0.5
+
+      The span attribute causes the attributes of one
+      col element to apply to more than one column.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="span" default="1" type="Number"/>
+      <xs:attribute name="width" type="MultiLength"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="tr">
+    <xs:complexType>
+      <xs:choice maxOccurs="unbounded">
+        <xs:element ref="th"/>
+        <xs:element ref="td"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attributeGroup ref="cellhalign"/>
+      <xs:attributeGroup ref="cellvalign"/>
+      <xs:attribute name="bgcolor" type="Color"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:simpleType name="Scope">
+    <xs:annotation>
+      <xs:documentation>
+      Scope is simpler than headers attribute for common tables
+      </xs:documentation>
+    </xs:annotation>
+    <xs:restriction base="xs:token">
+      <xs:enumeration value="row"/>
+      <xs:enumeration value="col"/>
+      <xs:enumeration value="rowgroup"/>
+      <xs:enumeration value="colgroup"/>
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:annotation>
+    <xs:documentation>
+    th is for headers, td for data and for cells acting as both
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="th">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="abbr" type="Text"/>
+          <xs:attribute name="axis"/>
+          <xs:attribute name="headers" type="xs:IDREFS"/>
+          <xs:attribute name="scope" type="Scope"/>
+          <xs:attribute name="rowspan" default="1" type="Number"/>
+          <xs:attribute name="colspan" default="1" type="Number"/>
+          <xs:attributeGroup ref="cellhalign"/>
+          <xs:attributeGroup ref="cellvalign"/>
+          <xs:attribute name="nowrap">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="nowrap"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="bgcolor" type="Color"/>
+          <xs:attribute name="width" type="Length"/>
+          <xs:attribute name="height" type="Length"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="td">
+    <xs:complexType mixed="true">
+      <xs:complexContent>
+        <xs:extension base="Flow">
+          <xs:attributeGroup ref="attrs"/>
+          <xs:attribute name="abbr" type="Text"/>
+          <xs:attribute name="axis"/>
+          <xs:attribute name="headers" type="xs:IDREFS"/>
+          <xs:attribute name="scope" type="Scope"/>
+          <xs:attribute name="rowspan" default="1" type="Number"/>
+          <xs:attribute name="colspan" default="1" type="Number"/>
+          <xs:attributeGroup ref="cellhalign"/>
+          <xs:attributeGroup ref="cellvalign"/>
+          <xs:attribute name="nowrap">
+            <xs:simpleType>
+              <xs:restriction base="xs:token">
+                <xs:enumeration value="nowrap"/>
+              </xs:restriction>
+            </xs:simpleType>
+          </xs:attribute>
+          <xs:attribute name="bgcolor" type="Color"/>
+          <xs:attribute name="width" type="Length"/>
+          <xs:attribute name="height" type="Length"/>
+        </xs:extension>
+      </xs:complexContent>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+      ================== Xdoc Specific =====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:annotation>
+    <xs:documentation>
+      ================ Document Structure ==================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="document">
+    <xs:annotation>
+      <xs:documentation source="version">2.0.0</xs:documentation>
+      <xs:documentation source="description">
+        The <document/> element is the root of the Xdoc descriptor.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="properties" minOccurs="0" maxOccurs="1">
+          <xs:annotation>
+            <xs:documentation source="version">2.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Optional properties element for this document element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+        <xs:element ref="head" minOccurs="0" maxOccurs="1" >
+          <xs:annotation>
+            <xs:documentation source="version">2.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Optional head element for this document element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+        <xs:element ref="body" minOccurs="1" maxOccurs="1">
+          <xs:annotation>
+            <xs:documentation source="version">2.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Required body element for this document element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+      </xs:sequence>
+      <xs:attributeGroup ref="i18n"/>
+      <xs:attribute name="id" type="xs:string">
+        <xs:annotation>
+          <xs:documentation source="version">2.0.0</xs:documentation>
+          <xs:documentation source="description">
+            The identifier of this document element.
+          </xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+      ================ Document Properties =================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="properties">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="title" minOccurs="1" maxOccurs="1">
+          <xs:annotation>
+            <xs:documentation source="version">2.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Required title element for the document element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+        <xs:element ref="author" minOccurs="0" maxOccurs="unbounded">
+          <xs:annotation>
+            <xs:documentation source="version">2.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Optional author element for the document element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+        <xs:element ref="date" minOccurs="0" maxOccurs="1">
+          <xs:annotation>
+            <xs:documentation source="version">2.0.0</xs:documentation>
+            <xs:documentation source="description">
+              Optional creation/last updated date for the document element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="author">
+    <xs:annotation>
+      <xs:documentation source="version">2.0.0</xs:documentation>
+      <xs:documentation source="description">
+        An author element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:attribute name="email" use="optional" type="xs:string">
+        <xs:annotation>
+          <xs:documentation source="version">2.0.0</xs:documentation>
+          <xs:documentation source="description">
+            The email attribute for the author element.
+          </xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="date">
+    <xs:annotation>
+      <xs:documentation source="version">2.0.0</xs:documentation>
+      <xs:documentation source="description">
+        An date element. The date is recommended (but it is not a requirement) to be align to
+        the ISO-8601 standard, i.e.: YYYY-MM-DD.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true"/>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+      =================== Document Body ====================================
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="body">
+    <xs:annotation>
+      <xs:documentation source="version">2.0.0</xs:documentation>
+      <xs:documentation source="description">
+        The body element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="section" maxOccurs="unbounded">
+          <xs:annotation>
+            <xs:documentation source="version">2.0.0</xs:documentation>
+            <xs:documentation source="description">
+              A section in the body element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+      </xs:sequence>
+      <xs:attributeGroup ref="attrs"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="section">
+    <xs:annotation>
+      <xs:documentation source="version">2.0.0</xs:documentation>
+      <xs:documentation source="description">
+        A section element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:group ref="block"/>
+        <xs:element ref="form"/>
+        <xs:group ref="inline"/>
+        <xs:group ref="misc"/>
+        <xs:element ref="subsection">
+          <xs:annotation>
+            <xs:documentation source="version">2.0.0</xs:documentation>
+            <xs:documentation source="description">
+              A subsection in the section element.
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="name" use="required" type="Text"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="subsection">
+    <xs:annotation>
+      <xs:documentation source="version">2.0.0</xs:documentation>
+      <xs:documentation source="description">
+        A subsection element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:group ref="block"/>
+        <xs:element ref="form"/>
+        <xs:group ref="inline"/>
+        <xs:group ref="misc"/>
+      </xs:choice>
+      <xs:attributeGroup ref="attrs"/>
+      <xs:attribute name="name" use="required" type="Text"/>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:annotation>
+    <xs:documentation>
+      ================ Document Addons =====================================
+      See "special.extra" group.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:element name="source">
+    <xs:annotation>
+      <xs:documentation source="version">2.0.0</xs:documentation>
+      <xs:documentation source="description">
+        A source element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType mixed="true">
+      <xs:sequence>
+        <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="macro">
+    <xs:annotation>
+      <xs:documentation source="version">2.0.0</xs:documentation>
+      <xs:documentation source="description">
+        A macro element.
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element ref="param"/>
+      </xs:choice>
+      <xs:attribute name="name" use="required" type="Text"/>
+    </xs:complexType>
+  </xs:element>
+</xs:schema>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-xdoc/src/site/apt/using-xdoc-xsd.apt b/doxia-modules/doxia-module-xdoc/src/site/apt/using-xdoc-xsd.apt
new file mode 100644
index 0000000..b3150cf
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/site/apt/using-xdoc-xsd.apt
@@ -0,0 +1,42 @@
+ -----
+ Using Schema XDOC 2.0
+ -----
+ Vincent Siveton
+ ------
+ 2009-01-28
+ ------
+
+~~ Licensed to the Apache Software Foundation (ASF) under one
+~~ or more contributor license agreements.  See the NOTICE file
+~~ distributed with this work for additional information
+~~ regarding copyright ownership.  The ASF licenses this file
+~~ to you under the Apache License, Version 2.0 (the
+~~ "License"); you may not use this file except in compliance
+~~ with the License.  You may obtain a copy of the License at
+~~
+~~   http://www.apache.org/licenses/LICENSE-2.0
+~~
+~~ Unless required by applicable law or agreed to in writing,
+~~ software distributed under the License is distributed on an
+~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+~~ KIND, either express or implied.  See the License for the
+~~ specific language governing permissions and limitations
+~~ under the License.
+
+~~ NOTE: For help with the syntax of this file, see:
+~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Using Schema XDOC 2.0
+
+  The XDOC XSD is located {{{http://maven.apache.org/xsd/xdoc-2.0.xsd}here}}.
+
+  Your favorite IDE probably supports XSD schema's for .xml/.xdoc files. You need to specify the following:
+
++-----+
+
+<document xmlns="http://maven.apache.org/XDOC/2.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
+...
+</document>
++-----+
diff --git a/doxia-modules/doxia-module-xdoc/src/site/site.xml b/doxia-modules/doxia-module-xdoc/src/site/site.xml
new file mode 100644
index 0000000..0000128
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/site/site.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 file:../../../../../doxia-sitetools/doxia-decoration-model/target/generated-site/xsd/decoration-1.0.0.xsd">
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu name="Schema XDOC 2.0">
+      <item name="Reference of Schema XDOC" href="xsddoc/index.html"/>
+      <item name="Using Schema XDOC" href="using-xdoc-xsd.html"/>
+    </menu>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocIdentityTest.java b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocIdentityTest.java
new file mode 100644
index 0000000..8748f8d
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocIdentityTest.java
@@ -0,0 +1,83 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.module.AbstractIdentityTest;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Check that piping a full model through an XdocParser and an XdocSink
+ * leaves the model unchanged.
+ */
+public class XdocIdentityTest
+    extends AbstractIdentityTest
+{
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+        assertIdentity( true );
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new XdocSink( writer );
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return new XdocParser();
+    }
+
+    /** {@inheritDoc} */
+    protected String getExpected()
+    {
+        // DOXIA-177
+        String expected = super.getExpected();
+
+        String startCaption = "begin:tableCaption";
+        String endCaption = "end:tableCaption";
+
+        int iStartCaption = expected.indexOf( startCaption );
+        int iEndCaption = expected.indexOf( endCaption ) + endCaption.length();
+
+        String captionTag = expected.substring( iStartCaption, iEndCaption ) + EOL + EOL + EOL;
+        expected = StringUtils.replace( expected, captionTag, "" );
+
+        int iStartTableRows =
+            expected.substring( 0, iStartCaption ).lastIndexOf( "begin:tableRows" ) + "begin:tableRows".length();
+
+        StringBuffer text = new StringBuffer();
+        text.append( expected.substring( 0, iStartTableRows ) );
+        text.append( EOL + EOL + EOL );
+        text.append( captionTag.subSequence( 0, captionTag.indexOf( "end:tableCaption" )
+            + "end:tableCaption".length() ) );
+        text.append( expected.substring( iStartTableRows ) );
+
+        return text.toString();
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocParserTest.java b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocParserTest.java
new file mode 100644
index 0000000..9268c0a
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocParserTest.java
@@ -0,0 +1,540 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.Reader;
+import java.io.Writer;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.doxia.parser.AbstractParserTest;
+import org.apache.maven.doxia.parser.ParseException;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @author <a href="mailto:evenisse at codehaus.org">Emmanuel Venisse</a>
+ * @version $Id: XdocParserTest.java 943628 2010-05-12 18:47:56Z ltheussl $
+ * @since 1.0
+ */
+public class XdocParserTest
+    extends AbstractParserTest
+{
+    private XdocParser parser;
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        parser = (XdocParser) lookup( Parser.ROLE, "xdoc" );
+
+        // AbstractXmlParser.CachedFileEntityResolver downloads DTD/XSD files in ${java.io.tmpdir}
+        // Be sure to delete them
+        String tmpDir = System.getProperty( "java.io.tmpdir" );
+        String excludes = "xdoc-*.xsd, xml.xsd";
+        List tmpFiles = FileUtils.getFileNames( new File( tmpDir ), excludes, null, true );
+        for ( Iterator it = tmpFiles.iterator(); it.hasNext(); )
+        {
+            File tmpFile = new File( it.next().toString() );
+            tmpFile.delete();
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "xml";
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return parser;
+    }
+
+    /** @throws Exception  */
+    public void testSnippetMacro()
+        throws Exception
+    {
+        Writer output = null;
+        Reader reader = null;
+
+        try
+        {
+            output = getTestWriter( "macro" );
+            reader = getTestReader( "macro" );
+
+            Sink sink = new XdocSink( output );
+            createParser().parse( reader, sink );
+            sink.close();
+        }
+        finally
+        {
+            IOUtil.close( output );
+            IOUtil.close( reader );
+        }
+
+        File f = getTestFile( getBasedir(), outputBaseDir() + getOutputDir() + "macro.xml" );
+        assertTrue( "The file " + f.getAbsolutePath() + " was not created", f.exists() );
+
+        String content;
+        try
+        {
+            reader = new FileReader( f );
+            content = IOUtil.toString( reader );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+
+        assertTrue( content.indexOf( "<modelVersion>4.0.0</modelVersion>" ) != -1 );
+    }
+
+    /** @throws Exception  */
+    public void testTocMacro()
+        throws Exception
+    {
+        Writer output = null;
+        Reader reader = null;
+
+        try
+        {
+            output = getTestWriter( "toc" );
+            reader = getTestReader( "toc" );
+
+            Sink sink = new XdocSink( output );
+            createParser().parse( reader, sink );
+            sink.close();
+        }
+        finally
+        {
+            IOUtil.close( output );
+            IOUtil.close( reader );
+        }
+
+        File f = getTestFile( getBasedir(), outputBaseDir() + getOutputDir() + "toc.xml" );
+        assertTrue( "The file " + f.getAbsolutePath() + " was not created", f.exists() );
+
+        String content;
+        try
+        {
+            reader = new FileReader( f );
+            content = IOUtil.toString( reader );
+        }
+        finally
+        {
+            IOUtil.close( reader );
+        }
+
+        // No section, only subsection 1 and 2
+        assertTrue( content.indexOf( "<a href=\"#Section_11\">Section 11</a>" ) != -1 );
+        assertTrue( content.indexOf( "<a href=\"#Section_1211\">Section 1211</a>" ) == -1 );
+    }
+
+    /** @throws Exception  */
+    public void testHeadEventsList()
+        throws Exception
+    {
+        String text = "<document>"
+                + "<properties><title>title</title>"
+                + "<!-- Test comment: DOXIA-312 -->"
+                + "<author email=\"a at b.c\">John Doe</author></properties>"
+                + "<head><meta name=\"security\" content=\"low\"/></head><body></body></document>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "comment", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "author", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "author_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        // DOXIA-359
+        text = "<document>"
+                + "<properties><title>properties title</title></properties>"
+                + "<head><title>head title</title></head>"
+                + "<body></body></document>";
+
+        sink.reset();
+        parser.parse( text, sink );
+
+        it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title", ( (SinkEventElement) it.next() ).getName() );
+
+        SinkEventElement title = (SinkEventElement) it.next();
+        assertEquals( "text", title.getName() );
+        assertEquals( "properties title", title.getArgs()[0] );
+
+        assertEquals( "title_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testDocumentBodyEventsList()
+        throws Exception
+    {
+        String text = "<document><body></body></document>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testSectionEventsList()
+        throws Exception
+    {
+        String text = "<section name=\"sec 1\"><subsection name=\"sub 1\"></subsection></section>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testNestedSectionsEventsList()
+        throws Exception
+    {
+        // DOXIA-241
+        String text = "<section name=\"section\"><h6>h6</h6><subsection name=\"subsection\"></subsection></section>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section3", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section4", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section5", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle5", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle5_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section5_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section4_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section3_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "section2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testSourceEventsList()
+        throws Exception
+    {
+        String text = "<source><a href=\"what.html\">what</a></source>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        text = "<source><![CDATA[<a href=\"what.html\">what</a>]]></source>";
+        sink.reset();
+        parser.parse( text, sink );
+
+        it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+        text = "<source><![CDATA[<source>what</source>]]></source>";
+        sink.reset();
+        parser.parse( text, sink );
+
+        it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testSourceContainingDTD()
+        throws Exception
+    {
+        String text = "<source><![CDATA[" +
+                          "<!DOCTYPE web-app PUBLIC " +
+                          "\"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN\"" +
+                          " \"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd\">" +
+                      "]]></source>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+
+    }
+
+    /** @throws Exception  */
+    public void testPreEOL()
+        throws Exception
+    {
+        // test EOLs within <source>: the sink MUST receive a text event for the EOL
+        String text = "<source><a href=\"what.html\">what</a>" + EOL
+                + "<a href=\"what.html\">what</a></source>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "link_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+    }
+
+    /**
+     * Test section with ids.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    public void testSectionIdAnchor()
+        throws Exception
+    {
+        String text = "<section name=\"test\" id=\"test-id\">This is a test."
+                + "<subsection name=\"sub-test\" id=\"sub-id\">Sub-section</subsection></section>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        SinkEventElement anchorEvt = (SinkEventElement) it.next();
+
+        assertEquals( "anchor", anchorEvt.getName() );
+        assertEquals( "test-id", anchorEvt.getArgs()[0] );
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+
+        anchorEvt = (SinkEventElement) it.next();
+        assertEquals( "anchor", anchorEvt.getName() );
+        assertEquals( "sub-id", anchorEvt.getArgs()[0] );
+        assertEquals( "anchor_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle2_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section2_", ( (SinkEventElement) it.next() ).getName() );
+
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /**
+     * Test script block.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    public void testJavaScript()
+        throws Exception
+    {
+        String text = "<script type=\"text/javascript\"><![CDATA[alert(\"Hello!\");]]></script>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /**
+     * Test unknown tags.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    public void testUnknown()
+        throws Exception
+    {
+        String text = "<applet><param name=\"name\" value=\"value\"/><unknown/></applet>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /**
+     * Test invalid macro tags.
+     */
+    public void testMacroExceptions()
+    {
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+        assertParseException( sink, "<macro/>" );
+        assertParseException( sink, "<macro name=\"\"/>" );
+        assertParseException( sink, "<macro name=\"name\"><param name=\"\" value=\"value\"/></macro>" );
+        assertParseException( sink, "<macro name=\"name\"><param name=\"name\" value=\"\"/></macro>" );
+        assertParseException( sink, "<macro name=\"name\"><param value=\"value\"/></macro>" );
+        assertParseException( sink, "<macro name=\"name\"><param name=\"name\"/></macro>" );
+        assertParseException( sink, "<macro name=\"unknown\"></macro>" );
+    }
+
+    private void assertParseException( Sink sink, String text )
+    {
+        try
+        {
+            parser.parse( text, sink );
+
+            fail( "Should not be parseable: '" + text + "'" );
+        }
+        catch ( ParseException ex )
+        {
+            assertNotNull( ex );
+        }
+    }
+
+    /** @throws Exception  */
+    public void testEntities()
+        throws Exception
+    {
+        final String text = "<!DOCTYPE test [<!ENTITY foo \"&#x159;\"><!ENTITY tritPos  \"&#x1d7ed;\">]>"
+                + "<section name=\"&&foo;&tritPos;\"><p>&&foo;&tritPos;</p></section>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        parser.setValidate( false );
+        parser.parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "section1", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "sectionTitle1", ( (SinkEventElement) it.next() ).getName() );
+
+        SinkEventElement textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "&\u0159\uD835\uDFED", textEvt.getArgs()[0] );
+
+        assertEquals( "sectionTitle1_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "paragraph", ( (SinkEventElement) it.next() ).getName() );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "&", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\u0159", textEvt.getArgs()[0] );
+
+        textEvt = (SinkEventElement) it.next();
+        assertEquals( "text", textEvt.getName() );
+        assertEquals( "\uD835\uDFED", textEvt.getArgs()[0] );
+
+        assertEquals( "paragraph_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "section1_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocSinkTest.java b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocSinkTest.java
new file mode 100644
index 0000000..c35f482
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocSinkTest.java
@@ -0,0 +1,293 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+/**
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: XdocSinkTest.java 926772 2010-03-23 20:52:37Z ltheussl $
+ * @since 1.0
+ */
+public class XdocSinkTest
+    extends AbstractSinkTest
+{
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "xml";
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new XdocSink( writer, "UTF-8" );
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        return "<title>" + title + "</title>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        return author;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        return date;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            + "<document xmlns=\"http://maven.apache.org/XDOC/2.0\" "
+            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
+            + "xsi:schemaLocation=\"http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd\">"
+            + "<properties></properties>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return "<body></body></document>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return "<section name=\"" + title + "\"></section>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return "<subsection name=\"" + title + "\"></subsection>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        return "<h4>" + title + "</h4>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        return "<h5>" + title + "</h5>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        return "<h6>" + title + "</h6>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return "<ul><li>" + item + "</li></ul>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return "<ol style=\"list-style-type: lower-roman\"><li>" + item + "</li></ol>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return "<dl><dt>" + definum + "</dt><dd>" + definition + "</dd></dl>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        return "<img src=\"" + source + "\" alt=\"" + caption + "\" />";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        return "<table border=\"0\"><caption>" + caption
+                + "</caption><tr valign=\"top\"><td>" + cell + "</td></tr></table>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        return "<p>" + text + "</p>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        return "<source>" + text + "</source>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return "<hr />";
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return "<!-- PB -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        return "<a name=\"" + anchor + "\">" + anchor + "</a>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        return "<a href=\"" + link + "\">" + text + "</a>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        return "<i>" + text + "</i>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        return "<b>" + text + "</b>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        return "<tt>" + text + "</tt>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return "<br />";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return " ";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        // TODO: need to be able to retreive those from outside the sink
+        return HtmlTools.escapeHTML( text );
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        return "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
+    }
+
+    /**
+     * Test verbatim.
+     */
+    public void testBoxedVerbatim()
+    {
+        Writer writer =  new StringWriter();
+        XdocSink sink = null;
+
+        try
+        {
+            sink = new XdocSink( writer );
+
+            sink.verbatim( null );
+            sink.verbatim_();
+            sink.verbatim( SinkEventAttributeSet.BOXED );
+            sink.verbatim_();
+            sink.verbatim( new SinkEventAttributeSet( new String[] {SinkEventAttributeSet.WIDTH, "20%"} ) );
+            sink.verbatim_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<pre></pre><source></source><pre width=\"20%\"></pre>", writer.toString() );
+    }
+
+    /**
+     * Test link.
+     */
+    public void testLinkWithTarget()
+    {
+        Writer writer =  new StringWriter();
+        XdocSink sink = null;
+
+        try
+        {
+            sink = new XdocSink( writer );
+
+            sink.link( "name", (String) null );
+            sink.link_();
+            sink.link( "name", "nirvana" );
+            sink.link_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<a href=\"name\"></a><a href=\"name\" target=\"nirvana\"></a>", writer.toString() );
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "<!-- Simple comment with - - - - -->";
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocSinkWithLanguageIdTest.java b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocSinkWithLanguageIdTest.java
new file mode 100644
index 0000000..67bcc77
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocSinkWithLanguageIdTest.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+import java.util.Locale;
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XdocSinkWithLanguageIdTest.java 739565 2009-01-31 14:39:03Z vsiveton $
+ */
+public class XdocSinkWithLanguageIdTest
+    extends XdocSinkTest
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new XdocSink( writer, "UTF-8", Locale.US.getLanguage() );
+    }
+
+    /** {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            + "<document xmlns=\"http://maven.apache.org/XDOC/2.0\" "
+            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
+            + "xsi:schemaLocation=\"http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd\" "
+            + "lang=\"en\" xml:lang=\"en\">" + "<properties></properties>";
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocValidatorTest.java b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocValidatorTest.java
new file mode 100644
index 0000000..57728ca
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocValidatorTest.java
@@ -0,0 +1,86 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.doxia.markup.XmlMarkup;
+import org.apache.maven.doxia.xsd.AbstractXmlValidatorTest;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Test XDOC files with namespace.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XdocValidatorTest.java 739557 2009-01-31 13:54:32Z vsiveton $
+ * @since 1.0
+ */
+public class XdocValidatorTest
+    extends AbstractXmlValidatorTest
+{
+    /** The xsd to use */
+    private static final File XDOC_XSD = new File( getBasedir(), "/src/main/resources/xdoc-2.0.xsd" );
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+    }
+
+    /** {@inheritDoc} */
+    protected void tearDown()
+        throws Exception
+    {
+        super.tearDown();
+    }
+
+    /** {@inheritDoc} */
+    protected String[] getIncludes()
+    {
+        return new String[] { "**/*.xml", "**/xdoc/*" };
+    }
+
+    /** {@inheritDoc} */
+    protected String addNamespaces( String content )
+    {
+        Pattern pattern = Pattern.compile( ".*<([A-Za-z][A-Za-z0-9:_.-]*)([^>]*)>.*" );
+        Matcher matcher = pattern.matcher( content );
+        if ( matcher.find() )
+        {
+            String root = matcher.group( 1 );
+            String value = matcher.group( 2 );
+
+            if ( value.indexOf( XDOC_XSD.getName() ) == -1 )
+            {
+                String faqs =
+                    "<" + root + " xmlns=\"http://maven.apache.org/XDOC/2.0\""
+                        + "  xmlns:xsi=\"" + XmlMarkup.XML_NAMESPACE + "\""
+                        + "  xsi:schemaLocation=\"http://maven.apache.org/XDOC/2.0 " + XDOC_XSD.toURI() + "\" ";
+
+                return StringUtils.replace( content, "<" + root, faqs );
+            }
+        }
+
+        return content;
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XmlWriterXdocSinkTest.java b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XmlWriterXdocSinkTest.java
new file mode 100644
index 0000000..0feb522
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XmlWriterXdocSinkTest.java
@@ -0,0 +1,106 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+import org.codehaus.plexus.util.xml.XmlUtil;
+
+/**
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XmlWriterXdocSinkTest.java 926772 2010-03-23 20:52:37Z ltheussl $
+ * @since 1.1
+ */
+public class XmlWriterXdocSinkTest
+    extends XdocSinkTest
+{
+    private static final String DEFAULT_INDENT = StringUtils.repeat( " ", XmlUtil.DEFAULT_INDENTATION_SIZE );
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new XmlWriterXdocSink( new PrettyPrintXMLWriter( writer ) );
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return "<section name=\"" + title + "\"/>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return "<subsection name=\"" + title + "\"/>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return "<ul>" + XmlUtil.DEFAULT_LINE_SEPARATOR + DEFAULT_INDENT + "<li>" + item + "</li>"
+            + XmlUtil.DEFAULT_LINE_SEPARATOR + "</ul>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return "<ol style=\"list-style-type: lower-roman\">" + XmlUtil.DEFAULT_LINE_SEPARATOR + DEFAULT_INDENT
+            + "<li>" + item + "</li>" + XmlUtil.DEFAULT_LINE_SEPARATOR + "</ol>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return "<dl>" + XmlUtil.DEFAULT_LINE_SEPARATOR + DEFAULT_INDENT + "<dt>" + definum + "</dt>"
+            + XmlUtil.DEFAULT_LINE_SEPARATOR + DEFAULT_INDENT + "<dd>" + definition + "</dd>"
+            + XmlUtil.DEFAULT_LINE_SEPARATOR + "</dl>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        return "<img src=\"" + source + "\" alt=\"" + caption + "\"/>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        return "<table border=\"0\">" + XmlUtil.DEFAULT_LINE_SEPARATOR + DEFAULT_INDENT
+            + "<caption>" + caption + "</caption>" + XmlUtil.DEFAULT_LINE_SEPARATOR + DEFAULT_INDENT
+            + "<tr valign=\"top\">" + XmlUtil.DEFAULT_LINE_SEPARATOR + DEFAULT_INDENT + DEFAULT_INDENT
+            + "<td>" + cell + "</td>" + XmlUtil.DEFAULT_LINE_SEPARATOR + DEFAULT_INDENT + "</tr>"
+            + XmlUtil.DEFAULT_LINE_SEPARATOR + "</table>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return "<hr/>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return "<br/>";
+    }
+}
diff --git a/doxia-modules/doxia-module-xdoc/src/test/resources/macro.xml b/doxia-modules/doxia-module-xdoc/src/test/resources/macro.xml
new file mode 100644
index 0000000..5de8e64
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/resources/macro.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document xmlns="http://maven.apache.org/XDOC/2.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 file:../../main/resources/xdoc-2.0.xsd">
+  <properties>
+    <title>Test DOXIA-77</title>
+    <author email="vsiveton at apache.org">Vincent Siveton</author>
+  </properties>
+  <body>
+    <section name="Test DOXIA-77">
+      <macro name="snippet">
+        <param name="id" value="superpom"/>
+        <param name="url" value="http://svn.apache.org/repos/asf/maven/doxia/doxia/trunk/doxia-test-docs/src/main/resources/pom-4.0.0.xml"/>
+      </macro>
+    </section>
+  </body>
+</document>
diff --git a/doxia-modules/doxia-module-xdoc/src/test/resources/report.xml b/doxia-modules/doxia-module-xdoc/src/test/resources/report.xml
new file mode 100644
index 0000000..933ff94
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/resources/report.xml
@@ -0,0 +1,41 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document xmlns="http://maven.apache.org/XDOC/2.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 file:../../main/resources/xdoc-2.0.xsd">
+  <properties>
+    <title>Synchronization report for Codehaus</title>
+    <author>meeper</author>
+  </properties>
+  <body>
+    <section name="Synchronization report for Codehaus">
+      <table>
+        <tr>
+          <th>Artifact</th>
+          <th>Size (in bytes)</th>
+          <th>Date</th>
+        </tr>
+      </table>
+      <p>
+        <a href="codehaus-2004-08-29.txt">Raw report</a>
+      </p>
+    </section>
+  </body>
+</document>
diff --git a/doxia-modules/doxia-module-xdoc/src/test/resources/test.xml b/doxia-modules/doxia-module-xdoc/src/test/resources/test.xml
new file mode 100644
index 0000000..104539d
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/resources/test.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" ?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<document xmlns="http://maven.apache.org/XDOC/2.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 file:../../main/resources/xdoc-2.0.xsd">
+
+  <properties>
+    <title>Title</title>
+    <author>Author</author>
+    <date>Date</date>
+  </properties>
+
+  <body>
+
+    <section name="Some Paragraph">
+      <p>Paragraph 1, line 1. Paragraph 1, line 2.</p>
+      <p>Paragraph 2, line 1. Paragraph 2, line 2.</p>
+    </section>
+
+    <section name="Section title">
+      <subsection name="Sub-section title">
+        <h4>Sub-sub-section title</h4>
+        <h5>Sub-sub-sub-section</h5>
+        <h6>Sub-sub-sub-sub-section</h6>
+
+        <ul>
+          <li>List item 1.</li>
+          <li>List item 2. <p>Paragraph contained in list item 2.</p>
+            <ul>
+              <li>Sub-list item 1.</li>
+              <li>Sub-list item 2.</li>
+            </ul>
+          </li>
+          <li>List item 3. Force end of list:</li>
+        </ul>
+
+        <source>Verbatim text not contained in list item 3</source>
+
+        <ol style="list-style-type: decimal">
+          <li>Numbered item 1. <ol style="list-style-type: upper-alpha">
+              <li>Numbered item A.</li>
+              <li>Numbered item B.</li>
+            </ol>
+          </li>
+          <li>Numbered item 2.</li>
+        </ol>
+
+        <p>List numbering schemes: [[1]], [[a]], [[A]], [[i]], [[I]].</p>
+
+        <dl compact="compact">
+          <dt>
+            <b>Defined term 1</b>
+          </dt>
+          <dd>of definition list.</dd>
+          <dt>
+            <b>Defined term 2</b>
+          </dt>
+          <dd>of definition list. <source>Verbatim text in a box </source>
+          </dd>
+        </dl>
+
+        <p>--- instead of +-- suppresses the box around verbatim text.</p>
+
+        <img src="figure" alt="Figure caption"/>
+
+        <table align="center">
+          <caption>Table caption</caption>
+          <tr valign="top">
+            <th align="center">Centered<br/>cell 1,1</th>
+            <th align="left">Left-aligned<br/>cell 1,2</th>
+            <th align="right">Right-aligned<br/>cell 1,3</th>
+          </tr>
+          <tr valign="top">
+            <td align="center">cell 2,1</td>
+            <td align="left">cell 2,2</td>
+            <td align="right">cell 2,3</td>
+          </tr>
+        </table>
+
+        <p>No grid, no caption:</p>
+
+        <table align="center">
+          <tr valign="top">
+            <td align="center">cell</td>
+            <td align="center">cell</td>
+          </tr>
+          <tr valign="top">
+            <td align="center">cell</td>
+            <td align="center">cell</td>
+          </tr>
+        </table>
+
+        <p>Horizontal line:</p>
+        <hr/>
+
+        <p>New page.</p>
+
+        <p><i>Italic</i> font. <b>Bold</b> font. <tt>Monospaced</tt> font.</p>
+
+        <p>
+          <a id="anchor" name="anchor">Anchor</a>. Link to <a href="#anchor">Anchor</a>.
+          Link to <a href="http://www.pixware.fr">http://www.pixware.fr</a>. Link to <a
+            href="#anchor">showing alternate text</a>. Link to <a
+            href="http://www.pixware.fr">Pixware home page</a>. </p>
+
+        <p>Force line<br/>break.</p>
+
+        <p>Non breaking space.</p>
+
+        <p>Escaped special characters:<br/> ~<br/> =<br/> -<br/> +<br/> *<br/> [<br/> ]<br/>
+          <<br/> ><br/> {<br/> }<br/> \ </p>
+
+        <p>Copyright symbol: © © ©.</p>
+
+      </subsection>
+    </section>
+
+  </body>
+
+</document>
diff --git a/doxia-modules/doxia-module-xdoc/src/test/resources/toc.xml b/doxia-modules/doxia-module-xdoc/src/test/resources/toc.xml
new file mode 100644
index 0000000..0afe53b
--- /dev/null
+++ b/doxia-modules/doxia-module-xdoc/src/test/resources/toc.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document xmlns="http://maven.apache.org/XDOC/2.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 file:../../main/resources/xdoc-2.0.xsd">
+  <properties>
+    <title>Test DOXIA-40</title>
+    <author email="vsiveton at apache.org">Vincent Siveton</author>
+  </properties>
+  <body>
+    <section name="Section 1">
+      <p>Test DOXIA-40</p>
+      <p> Section 1 </p>
+      <p>
+        <macro name="toc">
+          <param name="section" value="1"/>
+          <param name="fromDepth" value="1"/>
+          <param name="toDepth" value="2"/>
+        </macro>
+      </p>
+      <subsection name="Section 11">
+        <p> Section 11 </p>
+      </subsection>
+      <subsection name="Section 12">
+        <p> Section 12 </p>
+      </subsection>
+      <subsection name="Section 13">
+        <p> Section 13 </p>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/doxia-modules/doxia-module-xhtml/pom.xml b/doxia-modules/doxia-module-xhtml/pom.xml
new file mode 100644
index 0000000..e48fac0
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia-modules</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-module-xhtml</artifactId>
+
+  <name>Doxia :: XHTML Module</name>
+  <description>A Doxia module for Xhtml source documents.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/AbstractXhtmlSink.java b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/AbstractXhtmlSink.java
new file mode 100644
index 0000000..a5fbf61
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/AbstractXhtmlSink.java
@@ -0,0 +1,34 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.SinkAdapter;
+
+/**
+ * <p>Abstract AbstractXhtmlSink class.</p>
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: AbstractXhtmlSink.java 746993 2009-02-23 12:36:16Z vsiveton $
+ * @since 1.0
+ */
+public abstract class AbstractXhtmlSink
+    extends SinkAdapter
+{
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java
new file mode 100644
index 0000000..e4ea0d3
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java
@@ -0,0 +1,45 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.markup.HtmlMarkup;
+
+/**
+ * List of <code>Xhtml</code> markups.
+ * <br/>
+ * Xhtml uses all {@link javax.swing.text.html.HTML.Tag} and {@link javax.swing.text.html.HTML.Attribute}
+ * as markups.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XhtmlMarkup.java 739557 2009-01-31 13:54:32Z vsiveton $
+ * @since 1.0
+ */
+public interface XhtmlMarkup
+    extends HtmlMarkup
+{
+    /** XHTML namespace: "http://www.w3.org/1999/xhtml" */
+    String XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+
+    /** XHTML 1.0 transitional public id: "-//W3C//DTD XHTML 1.0 Transitional//EN" */
+    String XHTML_TRANSITIONAL_PUBLIC_ID = "-//W3C//DTD XHTML 1.0 Transitional//EN";
+
+    /** XHTML 1.0 transitional system id: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" */
+    String XHTML_TRANSITIONAL_SYSTEM_ID = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlParser.java b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlParser.java
new file mode 100644
index 0000000..f3020fa
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlParser.java
@@ -0,0 +1,215 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Attribute;
+
+import org.apache.maven.doxia.macro.MacroExecutionException;
+import org.apache.maven.doxia.parser.XhtmlBaseParser;
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+
+import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Parse an xhtml model and emit events into a Doxia Sink.
+ *
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @version $Id: XhtmlParser.java 807186 2009-08-24 12:29:12Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="xhtml"
+ */
+public class XhtmlParser
+    extends XhtmlBaseParser
+    implements XhtmlMarkup
+{
+    /** For boxed verbatim. */
+    private boolean boxed;
+
+    /** Empty elements don't write a closing tag. */
+    private boolean isEmptyElement;
+
+    /** {@inheritDoc} */
+    protected void handleStartTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        isEmptyElement = parser.isEmptyElementTag();
+
+        SinkEventAttributeSet attribs = getAttributesFromParser( parser );
+
+        if ( parser.getName().equals( HTML.toString() ) )
+        {
+            //Do nothing
+            return;
+        }
+        else if ( parser.getName().equals( HEAD.toString() ) )
+        {
+            sink.head( attribs );
+        }
+        else if ( parser.getName().equals( TITLE.toString() ) )
+        {
+            sink.title( attribs );
+        }
+        else if ( parser.getName().equals( META.toString() ) )
+        {
+            String name = parser.getAttributeValue( null, Attribute.NAME.toString() );
+            String content = parser.getAttributeValue( null, Attribute.CONTENT.toString() );
+
+            if ( "author".equals( name ) )
+            {
+                sink.author( null );
+
+                sink.text( content );
+
+                sink.author_();
+            }
+            else if ( "date".equals( name ) )
+            {
+                sink.date( null );
+
+                sink.text( content );
+
+                sink.date_();
+            }
+            else
+            {
+                sink.unknown( "meta", new Object[] {new Integer( TAG_TYPE_SIMPLE )}, attribs );
+            }
+        }
+        /*
+         * The ADDRESS element may be used by authors to supply contact information
+         * for a model or a major part of a model such as a form. This element
+         *  often appears at the beginning or end of a model.
+         */
+        else if ( parser.getName().equals( ADDRESS.toString() ) )
+        {
+            sink.author( attribs );
+        }
+        else if ( parser.getName().equals( BODY.toString() ) )
+        {
+            sink.body( attribs );
+        }
+        else if ( parser.getName().equals( DIV.toString() ) )
+        {
+            String divclass = parser.getAttributeValue( null, Attribute.CLASS.toString() );
+
+            if ( "source".equals( divclass ) )
+            {
+                this.boxed = true;
+            }
+
+            super.baseStartTag( parser, sink ); // pick up other divs
+        }
+        /*
+         * The PRE element tells visual user agents that the enclosed text is
+         * "preformatted". When handling preformatted text, visual user agents:
+         * - May leave white space intact.
+         * - May render text with a fixed-pitch font.
+         * - May disable automatic word wrap.
+         * - Must not disable bidirectional processing.
+         * Non-visual user agents are not required to respect extra white space
+         * in the content of a PRE element.
+         */
+        else if ( parser.getName().equals( PRE.toString() ) )
+        {
+            if ( boxed )
+            {
+                attribs.addAttributes( SinkEventAttributeSet.BOXED );
+            }
+
+            verbatim();
+
+            sink.verbatim( attribs );
+        }
+        else if ( !baseStartTag( parser, sink ) )
+        {
+            if ( isEmptyElement )
+            {
+                handleUnknown( parser, sink, TAG_TYPE_SIMPLE );
+            }
+            else
+            {
+                handleUnknown( parser, sink, TAG_TYPE_START );
+            }
+
+            if ( getLog().isDebugEnabled() )
+            {
+                String position = "[" + parser.getLineNumber() + ":"
+                    + parser.getColumnNumber() + "]";
+                String tag = "<" + parser.getName() + ">";
+
+                getLog().debug( "Unrecognized xhtml tag: " + tag + " at " + position );
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected void handleEndTag( XmlPullParser parser, Sink sink )
+        throws XmlPullParserException, MacroExecutionException
+    {
+        if ( parser.getName().equals( HTML.toString() ) )
+        {
+            //Do nothing
+            return;
+        }
+        else if ( parser.getName().equals( HEAD.toString() ) )
+        {
+            sink.head_();
+        }
+        else if ( parser.getName().equals( TITLE.toString() ) )
+        {
+            sink.title_();
+        }
+        else if ( parser.getName().equals( BODY.toString() ) )
+        {
+            consecutiveSections( 0, sink );
+
+            sink.body_();
+        }
+        else if ( parser.getName().equals( ADDRESS.toString() ) )
+        {
+            sink.author_();
+        }
+        else if ( parser.getName().equals( DIV.toString() ) )
+        {
+            this.boxed = false;
+            super.baseEndTag( parser, sink );
+        }
+        else if ( !baseEndTag( parser, sink ) )
+        {
+            if ( !isEmptyElement )
+            {
+                handleUnknown( parser, sink, TAG_TYPE_END );
+            }
+        }
+
+        isEmptyElement = false;
+    }
+
+    /** {@inheritDoc} */
+    protected void init()
+    {
+        super.init();
+
+        this.boxed = false;
+        this.isEmptyElement = false;
+    }
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSink.java b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSink.java
new file mode 100644
index 0000000..996a406
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSink.java
@@ -0,0 +1,268 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML.Attribute;
+
+import org.apache.maven.doxia.sink.XhtmlBaseSink;
+import org.apache.maven.doxia.sink.SinkEventAttributeSet;
+import org.apache.maven.doxia.util.HtmlTools;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * <a href="http://www.w3.org/TR/xhtml1/">Xhtml 1.0 Transitional</a> sink implementation.
+ * <br/>
+ * It uses the DTD/xhtml1-transitional <a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ * http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</a>.
+ *
+ * @author Jason van Zyl
+ * @author ltheussl
+ * @version $Id: XhtmlSink.java 807186 2009-08-24 12:29:12Z vsiveton $
+ * @since 1.0
+ */
+public class XhtmlSink
+    extends XhtmlBaseSink
+    implements XhtmlMarkup
+{
+    // ----------------------------------------------------------------------
+    // Instance fields
+    // ----------------------------------------------------------------------
+
+    private String encoding;
+
+    private String languageId;
+
+    /** An indication on if we're inside a head title. */
+    private boolean headTitleFlag;
+
+    // ----------------------------------------------------------------------
+    // Constructors
+    // ----------------------------------------------------------------------
+
+    /**
+     * Constructor, initialize the Writer.
+     *
+     * @param writer not null writer to write the result.
+     */
+    protected XhtmlSink( Writer writer )
+    {
+        super( writer );
+    }
+
+    /**
+     * Constructor, initialize the Writer and tells which encoding is used.
+     *
+     * @param writer not null writer to write the result.
+     * @param encoding the encoding used, that should be written to the generated HTML content
+     * if not <code>null</code>.
+     */
+    protected XhtmlSink( Writer writer, String encoding )
+    {
+        super( writer );
+
+        this.encoding = encoding;
+    }
+
+    /**
+     * Constructor, initialize the Writer and tells which encoding and languageId are used.
+     *
+     * @param writer not null writer to write the result.
+     * @param encoding the encoding used, that should be written to the generated HTML content
+     * if not <code>null</code>.
+     * @param languageId language identifier for the root element as defined by
+     * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
+     * in addition, the empty string may be specified.
+     */
+    protected XhtmlSink( Writer writer, String encoding, String languageId )
+    {
+        this( writer, encoding );
+
+        this.languageId = languageId;
+    }
+
+    /** {@inheritDoc} */
+    public void head()
+    {
+        init();
+
+        setHeadFlag( true );
+
+        write( "<!DOCTYPE html PUBLIC \"" + XHTML_TRANSITIONAL_PUBLIC_ID + "\" \"" + XHTML_TRANSITIONAL_SYSTEM_ID
+            + "\">" );
+
+        MutableAttributeSet atts = new SinkEventAttributeSet();
+        atts.addAttribute( "xmlns", XHTML_NAMESPACE );
+
+        if ( languageId != null )
+        {
+            atts.addAttribute( Attribute.LANG.toString(), languageId );
+            atts.addAttribute( "xml:lang", languageId );
+        }
+
+        writeStartTag( HTML, atts );
+
+        writeStartTag( HEAD );
+    }
+
+    /** {@inheritDoc} */
+    public void head_()
+    {
+        if ( !isHeadTitleFlag() )
+        {
+            // The content of element type "head" must match
+            // "((script|style|meta|link|object|isindex)*,
+            //  ((title,(script|style|meta|link|object|isindex)*,
+            //  (base,(script|style|meta|link|object|isindex)*)?)|(base,(script|style|meta|link|object|isindex)*,
+            //  (title,(script|style|meta|link|object|isindex)*))))"
+            writeStartTag( TITLE );
+            writeEndTag( TITLE );
+        }
+
+        setHeadFlag( false );
+        setHeadTitleFlag( false );
+
+        if ( encoding != null )
+        {
+            write( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + encoding + "\"/>" );
+        }
+
+        writeEndTag( HEAD );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TITLE
+     */
+    public void title()
+    {
+        setHeadTitleFlag( true );
+
+        writeStartTag( TITLE );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TITLE
+     */
+    public void title_()
+    {
+        content( getTextBuffer().toString() );
+
+        writeEndTag( TITLE );
+
+        resetTextBuffer();
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#META
+     */
+    public void author_()
+    {
+        if ( getTextBuffer().length() > 0 )
+        {
+            MutableAttributeSet att = new SinkEventAttributeSet();
+            att.addAttribute( Attribute.NAME, "author" );
+            String text = HtmlTools.escapeHTML( getTextBuffer().toString() );
+            // hack: un-escape numerical entities that have been escaped above
+            // note that numerical entities should really be added as one unicode character in the first place
+            text = StringUtils.replace( text, "&#", "&#" );
+            att.addAttribute( Attribute.CONTENT, text );
+
+            writeSimpleTag( META, att );
+
+            resetTextBuffer();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#META
+     */
+    public void date_()
+    {
+        if ( getTextBuffer().length() > 0 )
+        {
+            MutableAttributeSet att = new SinkEventAttributeSet();
+            att.addAttribute( Attribute.NAME, "date" );
+            att.addAttribute( Attribute.CONTENT, getTextBuffer().toString() );
+
+            writeSimpleTag( META, att );
+
+            resetTextBuffer();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BODY
+     */
+    public void body()
+    {
+        writeStartTag( BODY );
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BODY
+     * @see javax.swing.text.html.HTML.Tag#HTML
+     */
+    public void body_()
+    {
+        writeEndTag( BODY );
+
+        writeEndTag( HTML );
+
+        flush();
+
+        init();
+    }
+
+    // ----------------------------------------------------------------------
+    // Public protected methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * <p>Setter for the field <code>headTitleFlag</code>.</p>
+     *
+     * @param headTitleFlag an header title flag.
+     * @since 1.1
+     */
+    protected void setHeadTitleFlag( boolean headTitleFlag )
+    {
+        this.headTitleFlag = headTitleFlag;
+    }
+
+    /**
+     * <p>isHeadTitleFlag.</p>
+     *
+     * @return the current headTitleFlag.
+     * @since 1.1
+     */
+    protected boolean isHeadTitleFlag()
+    {
+        return this.headTitleFlag ;
+    }
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkFactory.java b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkFactory.java
new file mode 100644
index 0000000..ba42ac6
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkFactory.java
@@ -0,0 +1,49 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractXmlSinkFactory;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Xhtml implementation of the Sink factory.
+ *
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XhtmlSinkFactory.java 739565 2009-01-31 14:39:03Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.sink.SinkFactory" role-hint="xhtml"
+ */
+public class XhtmlSinkFactory
+    extends AbstractXmlSinkFactory
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding )
+    {
+        return new XhtmlSink( writer, encoding );
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer, String encoding, String languageId )
+    {
+        return new XhtmlSink( writer, encoding, languageId );
+    }
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSiteModule.java b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSiteModule.java
new file mode 100644
index 0000000..d5c7ac4
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSiteModule.java
@@ -0,0 +1,42 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.module.site.AbstractSiteModule;
+
+/**
+ * <p>XhtmlSiteModule class.</p>
+ *
+ * @author ltheussl
+ * @version $Id: XhtmlSiteModule.java 782392 2009-06-07 14:14:16Z vsiveton $
+ * @since 1.0
+ * @plexus.component role="org.apache.maven.doxia.module.site.SiteModule" role-hint="xhtml"
+ */
+public class XhtmlSiteModule
+    extends AbstractSiteModule
+{
+    /**
+     * Default constructor.
+     */
+    public XhtmlSiteModule()
+    {
+        super( "xhtml", "xhtml", "xhtml" );
+    }
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/site/site.xml b/doxia-modules/doxia-module-xhtml/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlIdentityTest.java b/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlIdentityTest.java
new file mode 100644
index 0000000..ee21bdf
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlIdentityTest.java
@@ -0,0 +1,83 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+
+import org.apache.maven.doxia.module.AbstractIdentityTest;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.Sink;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Check that piping a full model through an XhtmlParser and an XhtmlSink
+ * leaves the model unchanged.
+ */
+public class XhtmlIdentityTest
+    extends AbstractIdentityTest
+{
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+        assertIdentity( true );
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new XhtmlSink( writer );
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return new XhtmlParser();
+    }
+
+    /** {@inheritDoc} */
+    protected String getExpected()
+    {
+        // DOXIA-177
+        String expected = super.getExpected();
+
+        String startCaption = "begin:tableCaption";
+        String endCaption = "end:tableCaption";
+
+        int iStartCaption = expected.indexOf( startCaption );
+        int iEndCaption = expected.indexOf( endCaption ) + endCaption.length();
+
+        String captionTag = expected.substring( iStartCaption, iEndCaption ) + EOL + EOL + EOL;
+        expected = StringUtils.replace( expected, captionTag, "" );
+
+        int iStartTableRows =
+            expected.substring( 0, iStartCaption ).lastIndexOf( "begin:tableRows" ) + "begin:tableRows".length();
+
+        StringBuffer text = new StringBuffer();
+        text.append( expected.substring( 0, iStartTableRows ) );
+        text.append( EOL + EOL + EOL );
+        text.append( captionTag.subSequence( 0, captionTag.indexOf( "end:tableCaption" )
+            + "end:tableCaption".length() ) );
+        text.append( expected.substring( iStartTableRows ) );
+
+        return text.toString();
+    }
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlParserTest.java b/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlParserTest.java
new file mode 100644
index 0000000..43132a2
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlParserTest.java
@@ -0,0 +1,157 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.doxia.parser.AbstractParserTest;
+import org.apache.maven.doxia.parser.Parser;
+import org.apache.maven.doxia.sink.SinkEventElement;
+import org.apache.maven.doxia.sink.SinkEventTestingSink;
+import org.codehaus.plexus.util.FileUtils;
+
+
+/**
+ * @author <a href="mailto:lars at trieloff.net">Lars Trieloff</a>
+ * @version $Id: XhtmlParserTest.java 784707 2009-06-15 09:53:12Z vsiveton $
+ */
+public class XhtmlParserTest
+    extends AbstractParserTest
+{
+    private XhtmlParser parser;
+
+    /** {@inheritDoc} */
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        parser = (XhtmlParser) lookup( Parser.ROLE, "xhtml" );
+
+        // AbstractXmlParser.CachedFileEntityResolver downloads DTD/XSD files in ${java.io.tmpdir}
+        // Be sure to delete them
+        String tmpDir = System.getProperty( "java.io.tmpdir" );
+        String excludes = "xhtml-lat1.ent, xhtml1-transitional.dtd, xhtml-special.ent, xhtml-symbol.ent";
+        List tmpFiles = FileUtils.getFileNames( new File( tmpDir ), excludes, null, true );
+        for ( Iterator it = tmpFiles.iterator(); it.hasNext(); )
+        {
+            File tmpFile = new File( it.next().toString() );
+            tmpFile.delete();
+        }
+    }
+
+    /** {@inheritDoc} */
+    protected Parser createParser()
+    {
+        return parser;
+    }
+
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "xhtml";
+    }
+
+    /** @throws Exception  */
+    public void testDocumentBodyEventsList()
+        throws Exception
+    {
+        String text = "<html><body></body></html>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        ( (XhtmlParser) createParser() ).parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "body", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "body_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testHeadEventsList()
+        throws Exception
+    {
+        String text = "<head><title>Title</title><meta name=\"author\" content=\"Author\" />"
+                + "<meta name=\"date\" content=\"Date\" /><meta name=\"security\" content=\"low\"/></head>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        ( (XhtmlParser) createParser() ).parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "head", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "title_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "author", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "author_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "date", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "text", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "date_", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "head_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /** @throws Exception  */
+    public void testPreEventsList()
+        throws Exception
+    {
+        String text = "<pre></pre>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        ( (XhtmlParser) createParser() ).parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+
+        assertEquals( "verbatim", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "verbatim_", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+
+    /**
+     * Test unknown tags.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    public void testUnknown()
+        throws Exception
+    {
+        String text = "<applet><param name=\"name\" value=\"value\"/><unknown/></applet>";
+
+        SinkEventTestingSink sink = new SinkEventTestingSink();
+
+        ( (XhtmlParser) createParser() ).parse( text, sink );
+
+        Iterator it = sink.getEventList().iterator();
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertEquals( "unknown", ( (SinkEventElement) it.next() ).getName() );
+        assertFalse( it.hasNext() );
+    }
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkTest.java b/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkTest.java
new file mode 100644
index 0000000..c8c1323
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkTest.java
@@ -0,0 +1,342 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.apache.maven.doxia.sink.AbstractSinkTest;
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @author Jason van Zyl
+ * @version $Id: XhtmlSinkTest.java 926772 2010-03-23 20:52:37Z ltheussl $
+ * @since 1.0
+ */
+public class XhtmlSinkTest
+    extends AbstractSinkTest
+{
+    /** {@inheritDoc} */
+    protected String outputExtension()
+    {
+        return "xhtml";
+    }
+
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new XhtmlSink( writer, "UTF-8" );
+    }
+
+    /** {@inheritDoc} */
+    protected boolean isXmlSink()
+    {
+        return true;
+    }
+
+    /**
+     * Test link generation.
+     *
+     * @throws java.lang.Exception if any.
+     */
+    public void testLinks()
+        throws Exception
+    {
+        XhtmlSink sink = null;
+        Writer writer =  new StringWriter();
+        try
+        {
+            sink = (XhtmlSink) createSink( writer );
+            sink.link( "http:/www.xdoc.com" );
+            sink.link_();
+            sink.link( "./index.html#anchor" );
+            sink.link_();
+            sink.link( "../index.html#anchor" );
+            sink.link_();
+            sink.link( "index.html" );
+            sink.link_();
+        }
+        finally
+        {
+            if ( sink != null )
+            {
+                sink.close();
+            }
+        }
+
+        String actual = writer.toString();
+        assertTrue( actual.indexOf( "<a class=\"externalLink\" href=\"http:/www.xdoc.com\"></a>" ) != -1 );
+        assertTrue( actual.indexOf( "<a href=\"./index.html#anchor\"></a>" ) != -1 );
+        assertTrue( actual.indexOf( "<a href=\"../index.html#anchor\"></a>" ) != -1 );
+        assertTrue( actual.indexOf( "<a href=\"index.html\"></a>" ) != -1 );
+    }
+
+    /** {@inheritDoc} */
+    protected String getTitleBlock( String title )
+    {
+        return "<title>" + title + "</title>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getAuthorBlock( String author )
+    {
+        return author;
+    }
+
+    /** {@inheritDoc} */
+    protected String getDateBlock( String date )
+    {
+        return date;
+    }
+
+    /** {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" +
+                "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title></title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/></head>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBodyBlock()
+    {
+        return "<body></body></html>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSectionTitleBlock( String title )
+    {
+        return title;
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection1Block( String title )
+    {
+        return "<div class=\"section\"><h2>" + title + "</h2></div>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection2Block( String title )
+    {
+        return "<div class=\"section\"><h3>" + title + "</h3></div>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection3Block( String title )
+    {
+        return "<div class=\"section\"><h4>" + title + "</h4></div>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection4Block( String title )
+    {
+        return "<div class=\"section\"><h5>" + title + "</h5></div>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getSection5Block( String title )
+    {
+        return "<div class=\"section\"><h6>" + title + "</h6></div>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getListBlock( String item )
+    {
+        return "<ul><li>" + item + "</li></ul>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNumberedListBlock( String item )
+    {
+        return "<ol style=\"list-style-type: lower-roman\"><li>" + item + "</li></ol>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getDefinitionListBlock( String definum, String definition )
+    {
+        return "<dl><dt>" + definum + "</dt><dd>" + definition + "</dd></dl>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getFigureBlock( String source, String caption )
+    {
+        return "<img src=\"" + source + "\" alt=\"" + caption + "\" />";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTableBlock( String cell, String caption )
+    {
+        return "<table border=\"0\" class=\"bodyTable\">"
+            + "<caption>Table caption</caption><tr class=\"a\"><td>cell</td></tr>"
+            + "</table>";
+    }
+
+    // Disable testTable until the order of attributes issue is clarified
+    // TODO: remove
+    /** {@inheritDoc} */
+    public void testTable()
+    {
+        assertEquals( "Dummy!", "", "" );
+    }
+
+    /** {@inheritDoc} */
+    protected String getParagraphBlock( String text )
+    {
+        return "<p>" + text + "</p>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getVerbatimBlock( String text )
+    {
+        return "<div class=\"source\"><pre>" + text + "</pre></div>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getHorizontalRuleBlock()
+    {
+        return "<hr />";
+    }
+
+    /** {@inheritDoc} */
+    protected String getPageBreakBlock()
+    {
+        return "<!-- PB -->";
+    }
+
+    /** {@inheritDoc} */
+    protected String getAnchorBlock( String anchor )
+    {
+        return "<a name=\"" + anchor + "\">" + anchor + "</a>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLinkBlock( String link, String text )
+    {
+        return "<a href=\"" + link + "\">" + text + "</a>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getItalicBlock( String text )
+    {
+        return "<i>" + text + "</i>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getBoldBlock( String text )
+    {
+        return "<b>" + text + "</b>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getMonospacedBlock( String text )
+    {
+        return "<tt>" + text + "</tt>";
+    }
+
+    /** {@inheritDoc} */
+    protected String getLineBreakBlock()
+    {
+        return "<br />";
+    }
+
+    /** {@inheritDoc} */
+    protected String getNonBreakingSpaceBlock()
+    {
+        return " ";
+    }
+
+    /** {@inheritDoc} */
+    protected String getTextBlock( String text )
+    {
+        // TODO: need to be able to retreive those from outside the sink
+        return "~,_=,_-,_+,_*,_[,_],_<,_>,_{,_},_\\";
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        return text;
+    }
+
+    /**
+     * Test entities is section titles and paragraphs.
+     */
+    public void testEntities()
+    {
+        XhtmlSink sink = null;
+        Writer writer =  new StringWriter();
+
+        try
+        {
+            sink = new XhtmlSink( writer );
+            sink.section( Sink.SECTION_LEVEL_1, null );
+            sink.sectionTitle( Sink.SECTION_LEVEL_1, null );
+            sink.text( "&", null );
+            sink.sectionTitle_( Sink.SECTION_LEVEL_1 );
+            sink.paragraph( null );
+            sink.text( "&", null );
+            sink.paragraph_();
+            sink.section_( Sink.SECTION_LEVEL_1 );
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        assertEquals( "<div class=\"section\"><h2>&</h2><p>&</p></div>", writer.toString() );
+    }
+
+    /**
+     * Test head events.
+     */
+    public void testHead()
+    {
+        XhtmlSink sink = null;
+        Writer writer =  new StringWriter();
+
+        try
+        {
+            sink = new XhtmlSink( writer );
+            sink.head();
+            sink.title();
+            sink.text( "Title" );
+            sink.title_();
+            sink.comment( "A comment" );
+            sink.author();
+            // note: this is really illegal, there should be no un-resolved entities emitted into text()
+            sink.text( "&#x123;&" );
+            sink.author_();
+            sink.head_();
+        }
+        finally
+        {
+            sink.close();
+        }
+
+        String exp =
+                "<head><title>Title</title><!-- A comment --><meta name=\"author\" content=\"&#x123;&\" /></head>";
+        assertTrue( writer.toString().indexOf( exp ) != -1 );
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "<!-- Simple comment with - - - - -->";
+    }
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkWithLanguageIdTest.java b/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkWithLanguageIdTest.java
new file mode 100644
index 0000000..8711ecf
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/java/org/apache/maven/doxia/module/xhtml/XhtmlSinkWithLanguageIdTest.java
@@ -0,0 +1,48 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.Writer;
+import java.util.Locale;
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @version $Id: XhtmlSinkWithLanguageIdTest.java 739565 2009-01-31 14:39:03Z vsiveton $
+ */
+public class XhtmlSinkWithLanguageIdTest
+    extends XhtmlSinkTest
+{
+    /** {@inheritDoc} */
+    protected Sink createSink( Writer writer )
+    {
+        return new XhtmlSink( writer, "UTF-8", Locale.US.getLanguage() );
+    }
+
+    /** {@inheritDoc} */
+    protected String getHeadBlock()
+    {
+        return "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+            + "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">"
+            + "<head><title></title>"
+            + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/></head>";
+    }
+}
diff --git a/doxia-modules/doxia-module-xhtml/src/test/resources/download.apt.vm b/doxia-modules/doxia-module-xhtml/src/test/resources/download.apt.vm
new file mode 100644
index 0000000..6e09992
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/resources/download.apt.vm
@@ -0,0 +1,79 @@
+ ------
+Download Maven ${currentVersion}
+ ------
+Brett Porter
+Jason van Zyl
+ ------
+4 October 2005
+ ------
+
+Download Maven ${currentVersion}
+
+  Maven is distributed in several formats for your convenience.
+
+  You will be prompted for a mirror - if the file is not found on yours, please be patient, as it may take 24
+  hours to reach all mirrors.
+
+  Maven ${currentVersion} is distributed under the {{{http://maven.apache.org/license.html} Apache License, version 2.0}}.
+
+  We <<strongly>> encourage our users to configure a Maven repository mirror closer to their location, please read {{{guides/mini/guide-mirror-settings.html} How to Use Mirrors for Repositories}}.
+
+*-------------------------+---------+----------+-----------+
+|                         | Mirrors | Checksum | Signature |
+*-------------------------+---------+----------+-----------+
+| Maven ${currentVersion} (tar.bz2)     | {{{http://www.apache.org/dyn/closer.cgi/maven/binaries/maven-${currentVersion}-bin.tar.bz2} maven-${currentVersion}-bin.tar.bz2}} | {{{http://www.apache.org/dist/maven/binaries/maven-${currentVersion}-bin.tar.bz2.md5} maven-${currentVersion}-bin.tar.bz2.md5}} | {{{http://www.apache.org/dist/maven/binaries/maven-${currentVersion}-bin.tar.bz2.asc} maven-${currentVersion}-bin.tar.bz2.asc}} |
+*-------------------------+---------+----------+-----------+
+| Maven ${currentVersion} (tar.gz)      | {{{http://www.apache.org/dyn/closer.cgi/maven/binaries/maven-${currentVersion}-bin.tar.gz} maven-${currentVersion}-bin.tar.gz}} | {{{http://www.apache.org/dist/maven/binaries/maven-${currentVersion}-bin.tar.gz.md5} maven-${currentVersion}-bin.tar.gz.md5}} | {{{http://www.apache.org/dist/maven/binaries/maven-${currentVersion}-bin.tar.gz.asc} maven-${currentVersion}-bin.tar.gz.asc}} |
+*-------------------------+---------+----------+-----------+
+| Maven ${currentVersion} (zip)         | {{{http://www.apache.org/dyn/closer.cgi/maven/binaries/maven-${currentVersion}-bin.zip} maven-${currentVersion}-bin.zip}} | {{{http://www.apache.org/dist/maven/binaries/maven-${currentVersion}-bin.zip.md5} maven-${currentVersion}-bin.zip.md5}} | {{{http://www.apache.org/dist/maven/binaries/maven-${currentVersion}-bin.zip.asc} maven-${currentVersion}-bin.zip.asc}} |
+*-------------------------+---------+----------+-----------+
+| Maven Ant Tasks 2.0.7                 | {{{http://www.apache.org/dyn/closer.cgi/maven/binaries/maven-ant-tasks-2.0.7.jar} maven-ant-tasks-2.0.7.jar}} | {{{http://www.apache.org/dist/maven/binaries/maven-ant-tasks-2.0.7.jar.md5} maven-ant-tasks-2.0.7.jar.md5}} | {{{http://www.apache.org/dist/maven/binaries/maven-ant-tasks-2.0.7.jar.asc} maven-ant-tasks-2.0.7.jar.asc}} |
+*-------------------------+---------+----------+-----------+
+
+* Previous Releases
+
+  All previous releases of Maven can be found in the {{{http://archive.apache.org/dist/maven/binaries/}archives}}.
+
+* System {Requirements}
+
+*----------------------+---------------------------------------------------------------------------------------------+
+| <<JDK>>              | 1.4 or above (this is to execute Maven - it still allows you to build against 1.3 and prior JDK's)
+*----------------------+---------------------------------------------------------------------------------------------+
+| <<Memory>>           | No minimum requirement
+*----------------------+---------------------------------------------------------------------------------------------+
+| <<Disk>>             | No minimum requirement. Approximately 100MB will be used for your local repository, however this will vary depending on usage and can be removed and redownloaded at any time.
+*----------------------+---------------------------------------------------------------------------------------------+
+| <<Operating System>> | No minimum requirement. On Windows, Windows NT and above or Cygwin is required for the startup scripts. Tested on Windows XP, Fedora Core and Mac OS X.
+*----------------------+---------------------------------------------------------------------------------------------+
+
+* {Installation} Instructions
+
+** Windows 2000/XP
+
+  [[1]] Unzip <<<maven-${currentVersion}-bin.zip>>> to the directory you wish to install Maven ${currentVersion}. These instructions
+        assume you chose <<<C:\Program Files\Apache Software Foundation\maven-${currentVersion}>>>
+
+  [[2]] Add the <<<bin>>> directory to your path, by opening up the system properties (WinKey + Pause),
+        selecting the "Advanced" tab, and the "Environment Variables" button, then editing the <<<PATH>>>
+        variable in the user variables. eg.
+        <<<"C:\Program Files\Apache Software Foundation\maven-${currentVersion}\bin";%PATH%>>>
+
+  [[3]] In the same dialog, make sure that <<<JAVA_HOME>>> is set to the location of your JDK,
+        eg. <<<C:\Program Files\Java\jdk1.5.0_02>>>
+
+  [[4]] Run <<<mvn --version>>> to verify that it is correctly installed.
+
+** Unix-based Operating Systems (Linux, Solaris and Mac OS X)
+
+  [[1]] Extract the distribution archive to the directory you wish to install Maven ${currentVersion}. These instructions
+        assume you chose <<</usr/local/maven-${currentVersion}>>>. The directory <<<maven-${currentVersion}>>> will be created from
+        the archive.
+
+  [[2]] Add the <<<bin>>> directory to your path, eg. <<<export
+PATH=/usr/local/maven-${currentVersion}/bin:$PATH>>>
+
+  [[3]] Make sure that <<<JAVA_HOME>>> is set to the location of your JDK, eg.
+        <<<export JAVA_HOME=/usr/java/jdk1.5.0_02>>>
+
+  [[4]] Run <<<mvn --version>>> to verify that it is correctly installed.
+
diff --git a/doxia-modules/doxia-module-xhtml/src/test/resources/file.with.dot.in.name.xml b/doxia-modules/doxia-module-xhtml/src/test/resources/file.with.dot.in.name.xml
new file mode 100644
index 0000000..7d5c8da
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/resources/file.with.dot.in.name.xml
@@ -0,0 +1 @@
+<nothing/>
\ No newline at end of file
diff --git a/doxia-modules/doxia-module-xhtml/src/test/resources/fun.html b/doxia-modules/doxia-module-xhtml/src/test/resources/fun.html
new file mode 100644
index 0000000..d8f2cd3
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/resources/fun.html
@@ -0,0 +1,66 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<html>
+  <head>
+    <title>This is fun!</title>
+  </head>
+  <body>
+	<h1>This is the first first-level section</h1>
+    <p>
+		This paragraph belongs to section one.
+    </p>
+    	<h2>This is the first second-level section</h2>
+	    <p>
+			<a name="para1" />This paragraph belongs to subsection one.
+	    </p>
+	    <h2>This is the second second-level section</h2>
+	    <p>
+	    	This paragraph belongs to subsection two.
+	    </p>
+	    	<h4>This is the first third-level section</h4>
+	    	<p>
+	    		To make the <em>task</em> <i>even</i> harder,
+	    		the <tt>h3</tt>-heading was dropped, but we
+	    		expect the parser to recognize this section
+	    		as level 3, not level 4.
+	    	</p>
+	    <h2>This is the third second-level section</h2>
+	    <p>
+	    	This paragraph belongs to subsection three.
+	    </p>
+    <h1>This is the second first-level section</h1>
+    <p>
+    	This paragraph belongs to section two.
+    </p>
+    <pre>
+    	//what is source code?
+    </pre>
+    <p>
+    	This is <em>also</em> a <strong>paragraph</strong>. Take a look
+    	at the <a href="#para1">other paragraph</a>.
+    </p>
+    <p>
+    	Just introduce some <img src="http://maven.apache.org/images/logos/maven-feather.png"/>
+    	images. <img src="http://maven.apache.org/images/logos/maven-feather.png" alt="maven feather"/>
+    	<img src="http://maven.apache.org/images/logos/maven-feather.png" alt="maven feather"
+    	title="built by: maven"/>
+    </p>
+  </body>
+</html>
diff --git a/doxia-modules/doxia-module-xhtml/src/test/resources/index.xml.vm b/doxia-modules/doxia-module-xhtml/src/test/resources/index.xml.vm
new file mode 100644
index 0000000..72faf43
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/resources/index.xml.vm
@@ -0,0 +1,211 @@
+<?xml version="1.0"?>
+<document>
+  <properties>
+    <title>Welcome to Maven</title>
+    <author email="brett at apache.org">Brett Porter</author>
+    <author email="jason at sonatype.com">Jason van Zyl</author>
+  </properties>
+  <body>
+
+    <!-- TODO: news? -->
+    <div id="downloadbox">
+    <h5>Search Maven Sites</h5>
+<!-- Google CSE Search Box Begins  -->
+<form action="http://www.google.com/cse" id="searchbox_006660305041243700248:hyqtfwsewpm">
+  <input type="hidden" name="cx" value="006660305041243700248:hyqtfwsewpm" />
+  <input type="text" name="q" size="25" />
+  <input type="submit" name="sa" value="Search" />
+</form>
+<script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=searchbox_006660305041243700248%3Ahyqtfwsewpm"></script>
+<!-- Google CSE Search Box Ends -->
+
+	<h5>Get Maven ${currentVersion}</h5>
+      <span style="display: block; text-align: right; font-size: smaller">Released: 20 June 2007</span>
+      <p>
+        <a href="download.html">
+          <img src="images/folder-open.gif" border="0" alt="" title="Download Maven ${currentVersion}"/>
+          Maven ${currentVersion}
+        </a>
+        <small>(1.3Mb)</small>
+        <span style="font-size: smaller">
+          <br/>
+          <a href="download.html#Requirements">System Requirements</a>,
+          <a href="download.html#Installation">Installation Instructions</a>,
+          <a href="release-notes.html">Release Notes</a>
+        </span>
+      </p>
+      <p>
+        <a href="download.html">
+          <img src="images/folder-open.gif" border="0" alt="" title="Download Maven Tasks for Ant 2.0.7"/>
+          Maven Tasks for Ant 2.0.7
+        </a>
+        <small>(938k)</small>
+        <span style="font-size: smaller">
+          <br/>
+          <a href="ant-tasks.html">Documentation</a>,
+          <a href="ant-tasks-release-notes.html">Release Notes</a>
+        </span>
+      </p>
+      <!-- TODO: what about downloads for Continuum, etc.? Would be good to have one big download page -->
+
+      <h5>Other Maven Projects</h5>
+      <p>
+        <a href="/continuum/">
+          <img src="images/continuum.png" border="0" width="76" height="32" style="float: left; margin-right: 1em;" alt="" title="Continuum" />
+        Continuum
+        </a>
+        continuous integration server
+      </p>
+      <!--
+      
+      You can't be promoting Archiva when it's never been released, it's completely alpha and this is misleading.
+      When it's released I think it can rightfully be put here.
+      
+      <p>
+        <a href="archiva">
+          <img src="images/archiva.png" border="0" width="76" height="32" style="float: left; margin-right: 1em;" alt="" title="Archiva" />
+        Archiva
+        </a>
+        repository and artifact management server 
+      </p>
+      -->
+      <p>
+        <a href="/maven-1.x/">
+          <img src="images/maven-1.x.png" border="0" width="76" height="32" style="float: left; margin-right: 1em;" alt="" title="Maven 1.x" />
+        Maven 1.x
+        </a>
+        All stories start at the beginning...
+      </p>
+
+      <!-- TODO: we should use the SSI instead, but two things prevent it: a) the SSI's aren't working on apache.org yet so I can't test it; b) SSI's get eliminated from xdoc. For some reason even inside CDATA they are escaped. -->
+      <iframe src="http://www.apache.org/ads/bannerbar.html"
+style="margin-left: -10px; padding: 0;" frameborder="0" scrolling="no"
+width="244" height="68"></iframe>
+      <div>
+ <a href="http://www.ossummit.com"><img src="http://www.ossummit.com/ads/ossummit_button_2.jpg" alt="OS Summit Asia" border="0" width="234" height="60" /></a>
+      </div>
+    </div>
+    <section name="Welcome to Maven">
+
+      <!-- TODO: I reckon it's time for a new description -->
+      <p>
+        Maven is a software project management and comprehension tool. Based on the concept of a project object model
+        (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
+      </p>
+      <p>
+        If you think that Maven could help your project, you can find out more information about in the "About Maven"
+        section of the navigation. This includes an in-depth description of <a href="what-is-maven.html">what Maven is</a>,
+        a <a href="maven-features.html">list of some of its main features</a>, and a set of <a href="general.html">frequently
+        asked questions about what Maven is</a>.
+      </p>
+      <h3>Learning about Maven</h3>
+      <p>
+        <!-- TODO: this could be the big button type thing instead of a list of links -->
+        This site is separated into the following sections, depending on how you'd like to use Maven:
+      </p>
+      <!-- TODO: use CSS -->
+      <ul>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="run-maven/index.html">Run Maven</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those needing to build a project that uses Maven
+          </span>
+        </li>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="users/index.html">Use Maven</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those wanting to use Maven to build their project, including a "10 minute test" that gives a
+            practical overview of Maven's main features in just 10 minutes
+          </span>
+        </li>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="plugin-developers/index.html">Write Maven Plugins</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those who may or may not be using Maven, but want to provide a plugin for shared
+            functionality or to accompany their own product or toolset
+          </span>
+        </li>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="repository/index.html">Improve the Maven Repository</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those who may or may not use, but are interested in getting project metadata into the
+            repository
+          </span>
+        </li>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="developers/index.html">Develop Maven</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those who are currently developers, or interested in becoming developers of the Maven
+            project itself
+          </span>
+        </li>
+      </ul>
+      <p>
+        Each guide is divided into a number of trails to get you started on a particular topic, and includes a
+        reference area and a "cookbook" of common examples.
+      </p>
+      <p>
+        You can access the guides at any time from the left navigation.
+      </p>
+      <h3>Documentation Index</h3>
+      <p>
+        If you are looking for a quick reference, you can use the <a href="guides/index.html">documentation index.</a>
+<!-- TODO
+        If you are looking for a quick reference, you can use the documentation index. It is available in both
+        <a href="todo.html">alphabetical</a> and <a href="todo.html">categorical</a> listing formats.
+-->
+      </p>
+      <h3>Plugins</h3>
+<!-- TODO
+      <p>
+        Maven functionality is provided by plugins. For an explanation of how plugins work, and basic information on how
+        to use a plugin, see the <a href="todo.html">introduction to plugins</a> in the Users Centre.
+      </p>
+-->
+      <p>
+        For detailed information on just some of the plugins available for Maven, see the
+        <a href="plugins/index.html">plugin list</a>.
+      </p>
+<!-- TODO: Should these be here, or just in the user centre?
+      <h3>Converting from a different Build System</h3>
+      <p>
+        If you are currently using a different build system, there are options for converting from that to Maven 2
+        either partially or completely. These guides also give an overview of the differences between Maven and the
+        other build system. The following guides are available in the Users Centre:
+      </p>
+      <ul>
+        <li><a href="todo.html">Converting from Ant to Maven 2</a></li>
+        <li><a href="todo.html">Converting from Maven 1.x to Maven 2</a></li>
+        <li><a href="todo.html">Adding Maven 2 to an IDE based build</a></li>
+      </ul>
+-->
+      <h3>How to Get Support</h3>
+      <p>
+        Support for Maven is available in a variety of different forms.
+      </p>
+      <p>
+        To get started, search the documentation, the <a href="http://docs.codehaus.org/display/MAVENUSER">wiki</a>,
+        <a href="issue-tracking.html">issue tracker</a>, or the <a href="mail-lists.html">mailing list archives</a> to
+        see if the problem has been solved or reported before.
+      </p>
+      <p>
+        If the problem has not been reported before, the recommended way to get help is to
+        subscribe to the <a href="mail-lists.html">Maven Users Mailing list</a>. Many other users and Maven developers
+        will answer your questions there, and the answer will be archived for others in the future.
+      </p>
+      <p>
+        You can also reach the Maven developers on <a href="community.html">IRC</a>.
+      </p>
+    </section>
+  </body>
+</document>
diff --git a/doxia-modules/doxia-module-xhtml/src/test/resources/test.xhtml b/doxia-modules/doxia-module-xhtml/src/test/resources/test.xhtml
new file mode 100644
index 0000000..e5413d2
--- /dev/null
+++ b/doxia-modules/doxia-module-xhtml/src/test/resources/test.xhtml
@@ -0,0 +1,130 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+  <title>Title</title>
+  <meta name="author" content="Author" />
+  <meta name="date" content="Date" />
+</head>
+
+<body>
+
+<p>Paragraph 1, line 1. Paragraph 1, line 2.</p>
+<p>Paragraph 2, line 1. Paragraph 2, line 2.</p>
+
+<div class="section"><h2>Section title</h2>
+<div class="section"><h3>Sub-section title</h3>
+<div class="section"><h4>Sub-sub-section title</h4>
+<div class="section"><h5>Sub-sub-sub-section title</h5>
+<div class="section"><h6>Sub-sub-sub-sub-section title</h6>
+
+<ul>
+  <li>List item 1.</li>
+  <li>List item 2.<p>Paragraph contained in list item 2.</p>
+    <ul>
+      <li>Sub-list item 1.</li>
+      <li>Sub-list item 2.</li>
+    </ul>
+  </li>
+  <li>List item 3. Force end of list:</li>
+</ul>
+
+<div class="source"><pre>Verbatim text not contained in list item 3</pre></div>
+
+<ol type="1">
+  <li>Numbered item 1.
+    <ol type="A">
+      <li>Numbered item A.</li>
+      <li>Numbered item B.</li>
+    </ol>
+  </li>
+  <li>Numbered item 2.</li>
+</ol>
+
+<p>List numbering schemes: [[1]], [[a]], [[A]], [[i]], [[I]].</p>
+
+<dl>
+  <dt>Defined term 1</dt>
+  <dd>of definition list.</dd>
+  <dt>Defined term 2</dt>
+  <dd>of definition list.<div class="source"><pre>Verbatim text
+                        in a box        </pre></div></dd>
+</dl>
+
+<p>--- instead of +-- suppresses the box around verbatim text.</p>
+
+<div class="figure">
+  <p align="center"><img src="figure.png" alt="figure.png" /></p>
+  <p align="center"><i>Figure caption</i></p>
+</div>
+
+<table align="center" border="1" class="bodyTable">
+  <caption>Table caption</caption>
+    <tr class="a">
+      <th align="center">Centered<br />cell 1,1</th>
+      <th align="left">Left-aligned<br />cell 1,2</th>
+      <th align="right">Right-aligned<br />cell 1,3</th>
+    </tr>
+    <tr class="b">
+      <td align="center">cell 2,1</td>
+      <td align="left">cell 2,2</td>
+      <td align="right">cell 2,3</td>
+    </tr>
+</table>
+
+<p>No grid, no caption:</p>
+
+<table align="center" border="0" class="bodyTable">
+    <tr class="a">
+      <td align="center">cell</td>
+      <td align="center">cell</td>
+    </tr>
+    <tr class="b">
+      <td align="center">cell</td>
+      <td align="center">cell</td>
+    </tr>
+</table>
+
+<p>Horizontal line:</p><hr />
+
+<!-- PB -->
+<p>New page.</p>
+
+<p><i>Italic</i> font. <b>Bold</b> font. <tt>Monospaced</tt> font.</p>
+
+<p>
+  <a name="Anchor">Anchor</a>.
+  Link to <a href="#Anchor">Anchor</a>.
+  Link to <a href="http://www.pixware.fr" class="externalLink">http://www.pixware.fr</a>.
+  Link to <a href="#Anchor">showing alternate text</a>.
+  Link to <a href="http://www.pixware.fr" class="externalLink">Pixware home page</a>.
+</p>
+
+<p>Force line<br />break.</p>
+
+<p>Non breaking space.</p>
+
+<p>Escaped special characters:<br />
+  ~<br />
+  =<br />
+  -<br />
+  +<br />
+  *<br />
+  [<br />
+  ]<br />
+  <<br />
+  ><br />
+  {<br />
+  }<br />
+  \
+</p>
+
+<p>Copyright symbol: ©, ©, ©.</p>
+
+<!-- A comment! -->
+
+</div></div></div></div></div>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/doxia-modules/pom.xml b/doxia-modules/pom.xml
new file mode 100644
index 0000000..eed8abc
--- /dev/null
+++ b/doxia-modules/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-modules</artifactId>
+
+  <name>Doxia :: Modules</name>
+  <packaging>pom</packaging>
+
+  <description>Doxia modules for several markup languages.</description>
+
+  <modules>
+    <module>doxia-module-apt</module>
+    <module>doxia-module-confluence</module>
+    <module>doxia-module-docbook-simple</module>
+    <module>doxia-module-fml</module>
+    <module>doxia-module-fo</module>
+    <module>doxia-module-itext</module>
+    <module>doxia-module-latex</module>
+    <module>doxia-module-rtf</module>
+    <module>doxia-module-twiki</module>
+    <module>doxia-module-xdoc</module>
+    <module>doxia-module-xhtml</module>
+  </modules>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-sink-api</artifactId>
+    </dependency>
+
+    <!-- test -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/doxia-modules/src/site/site.xml b/doxia-modules/src/site/site.xml
new file mode 100644
index 0000000..446c80e
--- /dev/null
+++ b/doxia-modules/src/site/site.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="modules"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-sink-api/pom.xml b/doxia-sink-api/pom.xml
new file mode 100644
index 0000000..268c8d7
--- /dev/null
+++ b/doxia-sink-api/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>doxia</artifactId>
+    <groupId>org.apache.maven.doxia</groupId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-sink-api</artifactId>
+  <name>Doxia :: Sink API</name>
+  <description>Doxia Sink API.</description>
+
+  <dependencies>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-logging-api</artifactId>
+      </dependency>
+  </dependencies>
+</project>
diff --git a/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Sink.java b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Sink.java
new file mode 100644
index 0000000..e62ba15
--- /dev/null
+++ b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Sink.java
@@ -0,0 +1,1372 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.logging.LogEnabled;
+
+/**
+ * A <i>Sink</i> consumes Doxia events to produce a resultant output format
+ * (eg Docbook, PDF, XHTML...).
+ * <p>
+ *   Doxia allows you to transform any supported input document format (ie for which a Parser exists)
+ *   into any supported output document format (ie for which a Sink exists).
+ * </p>
+ * <p>
+ *   A parser is responsible for reading an input document and emitting a sequence of Doxia events
+ *   which can then be consumed by a Doxia Sink. Thus, you can parse any front- end format
+ *   (eg APT, FML, Xdoc, ...) and have them all contribute to a final XHTML version of a web site.
+ *   All documents being parsed result in a stream of Doxia events (eg paragraph, bold, italic,
+ *   text,...), which are then fed into a XHTML Sink to produce a set of XHTML pages.
+ * </p>
+ * <p>
+ *   A Sink is ultimately responsible for the final format and structure of the output document.
+ *   For example, you can take a collection of APT documents, let a Parser emit a series of Doxia
+ *   events and have that be fed into a Sink to produce a single PDF, a book, a site, or a
+ *   Word document. The Sink is fully responsible for the final output.
+ * </p>
+ * <p>
+ *   You can easily integrate any custom (XML, Wiki,...) format by creating a Doxia Parser which
+ *   reads your input document and produces a proper sequence of Doxia events.
+ *   Those can then be fed into an arbitrary Sink to produce any desired final output.
+ * </p>
+ * <p>
+ * <b>Note</b>: All implemented sink <b>should</b> use UTF-8 as encoding.
+ * </p>
+ *
+ * @since 1.0-alpha-6
+ * @author <a href="mailto:jason at maven.org">Jason van Zyl</a>
+ * @author <a href="mailto:vincent.siveton at gmail.com">Vincent Siveton</a>
+ * @author ltheussl
+ * @version $Id: Sink.java 905077 2010-01-31 17:24:47Z hboutemy $
+ */
+public interface Sink
+    extends LogEnabled
+{
+    /** The Plexus Sink Role. */
+    String ROLE = Sink.class.getName();
+
+    /**
+     * A numbering to handle a number list.
+     * @see #numberedList(int,SinkEventAttributes)
+     */
+    int NUMBERING_DECIMAL = 0;
+
+    /**
+     * A numbering to handle a lower alpha list.
+     * @see #numberedList(int,SinkEventAttributes)
+     */
+    int NUMBERING_LOWER_ALPHA = 1;
+
+    /**
+     * A numbering to handle a upper alpha list.
+     * @see #numberedList(int,SinkEventAttributes)
+     */
+    int NUMBERING_UPPER_ALPHA = 2;
+
+    /**
+     * A numbering to handle a lower roman list.
+     * @see #numberedList(int,SinkEventAttributes)
+     */
+    int NUMBERING_LOWER_ROMAN = 3;
+
+    /**
+     * A numbering to handle a upper roman list.
+     * @see #numberedList(int,SinkEventAttributes)
+     */
+    int NUMBERING_UPPER_ROMAN = 4;
+
+    /**
+     * A level 1 section (section).
+     * @see #section(int,SinkEventAttributes)
+     */
+    int SECTION_LEVEL_1 = 1;
+
+    /**
+     * A level 2 section (subsection).
+     * @see #section(int,SinkEventAttributes)
+     */
+    int SECTION_LEVEL_2 = 2;
+
+    /**
+     * A level 3 section (sub-subsection).
+     * @see #section(int,SinkEventAttributes)
+     */
+    int SECTION_LEVEL_3 = 3;
+
+    /**
+     * A level 4 section (sub-sub-subsection).
+     * @see #section(int,SinkEventAttributes)
+     */
+    int SECTION_LEVEL_4 = 4;
+
+    /**
+     * A level 5 section (sub-sub-sub-subsection).
+     * @see #section(int,SinkEventAttributes)
+     */
+    int SECTION_LEVEL_5 = 5;
+
+    /**
+     * Center alignment for table cells.
+     * @see #tableRows(int[], boolean)
+     */
+    int JUSTIFY_CENTER = 0;
+
+    /**
+     * Left alignment for table cells.
+     * @see #tableRows(int[], boolean)
+     */
+    int JUSTIFY_LEFT = 1;
+
+    /**
+     * Right alignment for table cells.
+     * @see #tableRows(int[], boolean)
+     */
+    int JUSTIFY_RIGHT = 2;
+
+    /**
+     * Starts the head element.
+     *
+     * @see #head(SinkEventAttributes)
+     */
+    void head();
+
+    /**
+     * Starts the head element.
+     *
+     * <p>
+     *   This contains information about the current document, (eg its title) that is not
+     *   considered document content. The head element is optional but if it exists, it has to be
+     *   unique within a sequence of Sink events that produces one output document, and it has
+     *   to come before the {@link #body(SinkEventAttributes)} element.
+     * </p>
+     * <p>
+     *   The canonical sequence of events for the head element is:
+     * </p>
+     * <pre>
+     *   sink.head();
+     *
+     *   sink.title();
+     *   sink.text( "Title" );
+     *   sink.title_();
+     *
+     *   sink.author();
+     *   sink.text( "Author" );
+     *   sink.author_();
+     *
+     *   sink.date();
+     *   sink.text( "Date" );
+     *   sink.date_();
+     *
+     *   sink.head_();
+     * </pre>
+     * <p>
+     *   but none of the enclosed events is required.  However, if they exist they have to occur
+     *   in the order shown, and the title() and date() events have to be unique (author() events
+     *   may occur any number of times).
+     * </p>
+     * <p>
+     *   Supported attributes are:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#PROFILE PROFILE}, {@link SinkEventAttributes#LANG LANG}.
+     * </blockquote>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void head( SinkEventAttributes attributes );
+
+    /**
+     * Ends the head element.
+     */
+    void head_();
+
+    /**
+     * Starts the title element.
+     *
+     * @see #title(SinkEventAttributes)
+     */
+    void title();
+
+    /**
+     * Starts the title element. This is used to identify the document.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     * @see #head(SinkEventAttributes)
+     */
+    void title( SinkEventAttributes attributes );
+
+    /**
+     * Ends the title element.
+     */
+    void title_();
+
+    /**
+     * Starts an author element.
+     *
+     * @see #author(SinkEventAttributes)
+     */
+    void author();
+
+    /**
+     * Starts an author element. This is used to identify the author of the document.
+     *
+     * <p>
+     *   Supported attributes are: {@link SinkEventAttributes#EMAIL EMAIL}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     * @see #head(SinkEventAttributes)
+     */
+    void author( SinkEventAttributes attributes );
+
+    /**
+     * Ends an author element.
+     */
+    void author_();
+
+    /**
+     * Starts the date element.
+     * <br/>
+     * The date is recommended (but it is not a requirement) to be align to the
+     * <a href="http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=26780">ISO-8601</a>
+     * standard, i.e.:
+     * <pre>
+     * YYYY-MM-DD
+     * </pre>
+     * where
+     * <ul>
+     * <li>YYYY is the year in the Gregorian calendar</li>
+     * <li>MM is the month of the year between 01 (January) and 12 (December)</li>
+     * <li>and DD is the day of the month between 01 and 31</li>
+     * </ul>
+     *
+     * @see #date(SinkEventAttributes)
+     */
+    void date();
+
+    /**
+     * Starts the date element. This is used to identify the date of the document.
+     *
+     * <p>
+     *   Supported attributes are: none.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     * @see #head(SinkEventAttributes)
+     */
+    void date( SinkEventAttributes attributes );
+
+    /**
+     * Ends the date element.
+     */
+    void date_();
+
+    /**
+     * Starts the body of a document.
+     *
+     * @see #body(SinkEventAttributes)
+     */
+    void body();
+
+    /**
+     * Starts the body of a document. This contains the document's content.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     * @see #head(SinkEventAttributes)
+     */
+    void body( SinkEventAttributes attributes );
+
+    /**
+     * Ends the body element.
+     */
+    void body_();
+
+    /**
+     * Starts a title heading element.
+     */
+    void sectionTitle();
+
+    /**
+     * Ends a title heading element.
+     */
+    void sectionTitle_();
+
+    /**
+     * Starts a first heading element which contains the topic of the section.
+     *
+     * @see #section(int,SinkEventAttributes)
+     */
+    void section1();
+
+    /**
+     * Ends a first heading element.
+     */
+    void section1_();
+
+    /**
+     * Starts a first title heading element. This element is optional, but if it exists,
+     * it has to be contained, and be the first element, within a {@link #section1()} element.
+     *
+     * @see #sectionTitle(int,SinkEventAttributes)
+     */
+    void sectionTitle1();
+
+    /**
+     * Ends a first title heading element.
+     */
+    void sectionTitle1_();
+
+    /**
+     * Starts a second heading element which contains the topic of the section.
+     * This has to be contained within a {@link #section1()} element.
+     *
+     * @see #section(int,SinkEventAttributes)
+     */
+    void section2();
+
+    /**
+     * Ends a second heading element.
+     */
+    void section2_();
+
+    /**
+     * Starts a second title heading element. This element is optional, but if it exists,
+     * it has to be contained, and be the first element, within a {@link #section2()} element.
+     *
+     * @see #sectionTitle(int,SinkEventAttributes)
+     */
+    void sectionTitle2();
+
+    /**
+     * Ends a second title heading element.
+     */
+    void sectionTitle2_();
+
+    /**
+     * Starts a third heading element which contains the topic of the section.
+     * This has to be contained within a {@link #section2()} element.
+     *
+     * @see #section(int,SinkEventAttributes)
+     */
+    void section3();
+
+    /**
+     * Ends a third heading element.
+     */
+    void section3_();
+
+    /**
+     * Starts a third title heading element. This element is optional, but if it exists,
+     * it has to be contained, and be the first element, within a {@link #section3()} element.
+     *
+     * @see #sectionTitle(int,SinkEventAttributes)
+     */
+    void sectionTitle3();
+
+    /**
+     * Ends a third title heading element.
+     */
+    void sectionTitle3_();
+
+    /**
+     * Starts a 4th heading element which contains the topic of the section.
+     * This has to be contained within a {@link #section3()} element.
+     *
+     * @see #section(int,SinkEventAttributes)
+     */
+    void section4();
+
+    /**
+     * Ends a 4th heading element.
+     */
+    void section4_();
+
+    /**
+     * Starts a 4th title heading element. This element is optional, but if it exists,
+     * it has to be contained, and be the first element, within a {@link #section4()} element.
+     *
+     * @see #sectionTitle(int,SinkEventAttributes)
+     */
+    void sectionTitle4();
+
+    /**
+     * Ends a 4th title heading element.
+     */
+    void sectionTitle4_();
+
+    /**
+     * Starts a 5th heading element which contains the topic of the section.
+     * This has to be contained within a {@link #section4()} element.
+     *
+     * @see #section(int,SinkEventAttributes)
+     */
+    void section5();
+
+    /**
+     * Ends a 5th heading element.
+     */
+    void section5_();
+
+    /**
+     * Starts a 5th title heading element. This element is optional, but if it exists,
+     * it has to be contained, and be the first element, within a {@link #section5()} element.
+     *
+     * @see #sectionTitle(int,SinkEventAttributes)
+     */
+    void sectionTitle5();
+
+    /**
+     * Ends a 5th title heading element.
+     */
+    void sectionTitle5_();
+
+    /**
+     * Start a new section at the given level.
+     *
+     * <p>
+     *   Sections with higher level have to be entirely contained within sections of lower level.
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param level the section level.
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void section( int level, SinkEventAttributes attributes );
+
+    /**
+     * Ends a section at the given level.
+     *
+     * @param level the section level.
+     * @since 1.1
+     */
+    void section_( int level );
+
+    /**
+     * Start a new section title at the given level.
+     *
+     * <p>
+     *    This element is optional, but if it exists, it has to be contained, and be the first
+     *    element, within a corresponding {@link #section(int,SinkEventAttributes) section}
+     *    element of the same level.
+     * </p>
+     * <p>
+     *   <b>NOTE:</b> It is strongly recommended not to make section titles implicit anchors.
+     *   Neither Parsers nor Sinks should insert any content that is not explicitly present
+     *   in the original source document, as this would lead to undefined behaviour for
+     *   multi-format processing chains. However, while Parsers <b>must never</b> emit anchors
+     *   for section titles, some specialized Sinks may implement such a feature if the resulting
+     *   output documents are not going to be further processed (and this is properly documented).
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus
+     *   {@link SinkEventAttributes#ALIGN ALIGN}.
+     * </p>
+     *
+     * @param level the section title level.
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void sectionTitle( int level, SinkEventAttributes attributes );
+
+    /**
+     * Ends a section title at the given level.
+     *
+     * @param level the section title level.
+     * @since 1.1
+     */
+    void sectionTitle_( int level );
+
+    /**
+     * Starts an unordered list element.
+     *
+     * @see #list(SinkEventAttributes)
+     */
+    void list();
+
+    /**
+     * Starts an unordered list.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void list( SinkEventAttributes attributes );
+
+    /**
+     * Ends an unordered list element.
+     */
+    void list_();
+
+    /**
+     * Starts a list item element within an unordered list.
+     *
+     * @see #listItem(SinkEventAttributes)
+     */
+    void listItem();
+
+    /**
+     * Starts a list item element within an unordered list.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void listItem( SinkEventAttributes attributes );
+
+    /**
+     * Ends a list item element within an unordered list.
+     */
+    void listItem_();
+
+    /**
+     * Starts an ordered list element.
+     *
+     * @param numbering the numbering style.
+     * @see #numberedList(int,SinkEventAttributes)
+     */
+    void numberedList( int numbering );
+
+    /**
+     * Starts an ordered list element.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param numbering the numbering style.
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     * @see #NUMBERING_DECIMAL
+     * @see #NUMBERING_LOWER_ALPHA
+     * @see #NUMBERING_LOWER_ROMAN
+     * @see #NUMBERING_UPPER_ALPHA
+     * @see #NUMBERING_UPPER_ROMAN
+     */
+    void numberedList( int numbering, SinkEventAttributes attributes );
+
+    /**
+     * Ends an ordered list element.
+     */
+    void numberedList_();
+
+    /**
+     * Starts a list item element within an ordered list.
+     *
+     * @see #numberedListItem(SinkEventAttributes)
+     */
+    void numberedListItem();
+
+    /**
+     * Starts a list item element within an ordered list.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void numberedListItem( SinkEventAttributes attributes );
+
+    /**
+     * Ends a list item element within an ordered list.
+     */
+    void numberedListItem_();
+
+    /**
+     * Starts a definition list element.
+     *
+     * @see #definitionList(SinkEventAttributes)
+     */
+    void definitionList();
+
+    /**
+     * Starts a definition list.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void definitionList( SinkEventAttributes attributes );
+
+    /**
+     * Ends a definition list element.
+     */
+    void definitionList_();
+
+    /**
+     * Starts a list item element within a definition list.
+     *
+     * @see #definitionListItem(SinkEventAttributes)
+     */
+    void definitionListItem();
+
+    /**
+     * Starts a list item element within a definition list.
+     *
+     * <p>
+     *   Every definitionListItem has to contain exactly one {@link #definedTerm(SinkEventAttributes)}
+     *   and one {@link #definition(SinkEventAttributes)}, in this order.
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void definitionListItem( SinkEventAttributes attributes );
+
+    /**
+     * Ends a list item element within a definition list.
+     */
+    void definitionListItem_();
+
+    /**
+     * Starts a definition element within a definition list.
+     *
+     * @see #definition(SinkEventAttributes)
+     */
+    void definition();
+
+    /**
+     * Starts a definition element within a definition list.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void definition( SinkEventAttributes attributes );
+
+    /**
+     * Ends a definition element within a definition list.
+     */
+    void definition_();
+
+    /**
+     * Starts a definition term element within a definition list.
+     *
+     * @see #definedTerm(SinkEventAttributes)
+     */
+    void definedTerm();
+
+    /**
+     * Starts a definition term element within a definition list.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void definedTerm( SinkEventAttributes attributes );
+
+    /**
+     * Starts a definition term element within a definition list.
+     */
+    void definedTerm_();
+
+    /**
+     * Starts a basic image embedding element.
+     *
+     * @see #figure(SinkEventAttributes)
+     */
+    void figure();
+
+    /**
+     * Starts a basic image embedding element.
+     *
+     * <p>
+     *   The canonical sequence of events for the figure element is:
+     * </p>
+     * <pre>
+     *   sink.figure();
+     *
+     *   sink.figureGraphics( "figure.png" );
+     *
+     *   sink.figureCaption();
+     *   sink.text( "Figure caption",);
+     *   sink.figureCaption_();
+     *
+     *   sink.figure_();
+     * </pre>
+     * <p>
+     *   where the figureCaption element is optional.
+     * </p>
+     * <p>
+     *   However, <strong>NOTE</strong> that the order of figureCaption and
+     *   figureGraphics events is arbitrary,
+     *   ie a parser may emit the figureCaption before or after the figureGraphics.
+     *   Implementing sinks should be prepared to handle both possibilities.
+     * </p>
+     * <p>
+     *   <strong>NOTE</strong> also that the figureGraphics() event does not have to be embedded
+     *   inside figure(), in particular for in-line images the figureGraphics() should be used
+     *   stand-alone (in HTML language, figureGraphics() produces a <code><img></code>
+     *   tag, while figure() opens a paragraph- or <code><div></code>- like environment).
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void figure( SinkEventAttributes attributes );
+
+    /**
+     * Ends a basic image embedding element.
+     */
+    void figure_();
+
+    /**
+     * Starts a caption of an image element.
+     *
+     * @see #figureCaption(SinkEventAttributes)
+     */
+    void figureCaption();
+
+    /**
+     * Starts a figure caption.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     * @see #figure(SinkEventAttributes)
+     */
+    void figureCaption( SinkEventAttributes attributes );
+
+    /**
+     * Ends a caption of an image.
+     */
+    void figureCaption_();
+
+    /**
+     * Adding a source of a graphic.
+     *
+     * @param name the source
+     */
+    void figureGraphics( String name );
+
+    /**
+     * Adds a graphic element.
+     *
+     * <p>
+     *   The <code>src</code> parameter should be a valid link, ie it can be an absolute
+     *   URL or a link relative to the current source document.
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#SRC SRC}, {@link SinkEventAttributes#ALT ALT},
+     *   {@link SinkEventAttributes#WIDTH WIDTH}, {@link SinkEventAttributes#HEIGHT HEIGHT},
+     *   {@link SinkEventAttributes#ALIGN ALIGN}, {@link SinkEventAttributes#BORDER BORDER},
+     *   {@link SinkEventAttributes#HSPACE HSPACE}, {@link SinkEventAttributes#VSPACE VSPACE},
+     *   {@link SinkEventAttributes#ISMAP ISMAP}, {@link SinkEventAttributes#USEMAP USEMAP}.
+     * </blockquote>
+     * <p>
+     *   If the {@link SinkEventAttributes#SRC SRC} attribute is specified in SinkEventAttributes,
+     *   it will be overridden by the <code>src</code> parameter.
+     * </p>
+     *
+     * @param src the image source, a valid URL.
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     * @see #figure(SinkEventAttributes)
+     */
+    void figureGraphics( String src, SinkEventAttributes attributes );
+
+    /**
+     * Starts a table element for marking up tabular information in a document.
+     *
+     * @see #table(SinkEventAttributes)
+     */
+    void table();
+
+    /**
+     * Starts a table.
+     *
+     * <p>
+     *   The canonical sequence of events for the table element is:
+     * </p>
+     * <pre>
+     *   sink.table();
+     *
+     *   sink.tableRows( justify, true );
+     *
+     *   sink.tableRow();
+     *   sink.tableCell();
+     *   sink.text( "cell 1,1" );
+     *   sink.tableCell_();
+     *   sink.tableCell();
+     *   sink.text( "cell 1,2" );
+     *   sink.tableCell_();
+     *   sink.tableRow_();
+     *
+     *   sink.tableRows_();
+     *
+     *   sink.tableCaption();
+     *   sink.text( "Table caption" );
+     *   sink.tableCaption_();
+     *
+     *   sink.table_();
+     *
+     * </pre>
+     * <p>
+     *   where the tableCaption element is optional.
+     * </p>
+     * <p>
+     *   However, <strong>NOTE</strong> that the order of tableCaption and
+     *   {@link #tableRows(int[],boolean)} events is arbitrary,
+     *   ie a parser may emit the tableCaption before or after the tableRows.
+     *   Implementing sinks should be prepared to handle both possibilities.
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#ALIGN ALIGN}, {@link SinkEventAttributes#BGCOLOR BGCOLOR},
+     *   {@link SinkEventAttributes#BORDER BORDER}, {@link SinkEventAttributes#CELLPADDING CELLPADDING},
+     *   {@link SinkEventAttributes#CELLSPACING CELLSPACING}, {@link SinkEventAttributes#FRAME FRAME},
+     *   {@link SinkEventAttributes#RULES RULES}, {@link SinkEventAttributes#SUMMARY SUMMARY},
+     *   {@link SinkEventAttributes#WIDTH WIDTH}.
+     * </blockquote>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void table( SinkEventAttributes attributes );
+
+    /**
+     * Ends a table element.
+     */
+    void table_();
+
+    /**
+     * Starts an element that contains rows of table data.
+     *
+     * @param justification the default justification of columns.
+     * This can be overridden by individual table rows or table cells.
+     * If null a left alignment is assumed by default. If this array
+     * has less elements than there are columns in the table then the value of
+     * the last array element will be taken as default for the remaining table cells.
+     * @param grid true to provide a grid, false otherwise.
+     * @see #table(SinkEventAttributes)
+     * @see #JUSTIFY_CENTER
+     * @see #JUSTIFY_LEFT
+     * @see #JUSTIFY_RIGHT
+     */
+    void tableRows( int[] justification, boolean grid );
+
+    /**
+     * Ends an element that contains rows of table data.
+     */
+    void tableRows_();
+
+    /**
+     * Starts a row element which acts as a container for a row of table cells.
+     *
+     * @see #tableRow(SinkEventAttributes)
+     */
+    void tableRow();
+
+    /**
+     * Starts a table row.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#ALIGN ALIGN}, {@link SinkEventAttributes#BGCOLOR BGCOLOR},
+     *   {@link SinkEventAttributes#VALIGN VALIGN}.
+     * </blockquote>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void tableRow( SinkEventAttributes attributes );
+
+    /**
+     * Ends a row element.
+     */
+    void tableRow_();
+
+    /**
+     * Starts a cell element which defines a cell that contains data.
+     *
+     * @see #tableCell(SinkEventAttributes)
+     */
+    void tableCell();
+
+    /**
+     * Starts a cell element which defines a cell that contains data.
+     *
+     * @param width the size of the cell.
+     * @deprecated Use #tableCell(SinkEventAttributes) instead.
+     */
+    void tableCell( String width );
+
+    /**
+     * Starts a table cell.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#ABBRV ABBRV}, {@link SinkEventAttributes#ALIGN ALIGN},
+     *   {@link SinkEventAttributes#AXIS AXIS}, {@link SinkEventAttributes#BGCOLOR BGCOLOR},
+     *   {@link SinkEventAttributes#COLSPAN COLSPAN}, {@link SinkEventAttributes#HEADERS HEADERS},
+     *   {@link SinkEventAttributes#HEIGHT HEIGHT}, {@link SinkEventAttributes#NOWRAP NOWRAP},
+     *   {@link SinkEventAttributes#ROWSPAN ROWSPAN}, {@link SinkEventAttributes#SCOPE SCOPE},
+     *   {@link SinkEventAttributes#VALIGN VALIGN}, {@link SinkEventAttributes#WIDTH WIDTH}.
+     * </blockquote>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void tableCell( SinkEventAttributes attributes );
+
+    /**
+     * Ends a cell element.
+     */
+    void tableCell_();
+
+    /**
+     * Starts a cell element which defines a cell that contains header information.
+     *
+     * @see #tableHeaderCell(SinkEventAttributes)
+     */
+    void tableHeaderCell();
+
+    /**
+     * Starts a cell element which defines a cell that contains header information.
+     *
+     * @param width the size of the header cell.
+     * @deprecated Use #tableHeaderCell(SinkEventAttributes) instead.
+     */
+    void tableHeaderCell( String width );
+
+    /**
+     * Starts a table header cell.
+     *
+     * <p>
+     *   Supported attributes are the same as for {@link #tableCell(SinkEventAttributes) tableCell}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void tableHeaderCell( SinkEventAttributes attributes );
+
+    /**
+     * Ends a cell header element.
+     */
+    void tableHeaderCell_();
+
+    /**
+     * Starts a caption element of a table.
+     *
+     * @see #tableCaption(SinkEventAttributes)
+     */
+    void tableCaption();
+
+    /**
+     * Starts a table caption.
+     *
+     * <p>
+     *   Note that the order of tableCaption and
+     *   {@link #tableRows(int[],boolean)} events is arbitrary,
+     *   ie a parser may emit the tableCaption before or after the tableRows.
+     *   Implementing sinks should be prepared to handle both possibilities.
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}
+     *   plus {@link SinkEventAttributes#ALIGN ALIGN}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     * @see #table(SinkEventAttributes)
+     */
+    void tableCaption( SinkEventAttributes attributes );
+
+    /**
+     * Ends a caption element of a table.
+     */
+    void tableCaption_();
+
+    /**
+     * Starts an element which represents a paragraph.
+     *
+     * @see #paragraph(SinkEventAttributes)
+     */
+    void paragraph();
+
+    /**
+     * Starts a paragraph.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}
+     *   plus {@link SinkEventAttributes#ALIGN ALIGN}.
+     * </p>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void paragraph( SinkEventAttributes attributes );
+
+    /**
+     * Ends a paragraph element.
+     */
+    void paragraph_();
+
+    /**
+     * Starts an element which indicates that whitespace in the enclosed text has semantic relevance.
+     *
+     * @param boxed true to add a box, false otherwise
+     * @deprecated Use #verbatim(SinkEventAttributes) instead.
+     */
+    void verbatim( boolean boxed );
+
+    /**
+     * Starts a verbatim block, ie a block where whitespace has semantic relevance.
+     *
+     * <p>
+     *   Text in a verbatim block must only be wrapped at the linebreaks in the source,
+     *   and spaces should not be collapsed. It should be displayed in a fixed-width font to
+     *   retain the formatting but the overall size may be chosen by the implementation.
+     * </p>
+     *
+     * <p>
+     *   Most Sink events may be emitted within a verbatim block, the only elements explicitly
+     *   forbidden are font-changing events and figures. Also, verbatim blocks may not be nested.
+     * </p>
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#DECORATION DECORATION} (value: "boxed"),
+     *   {@link SinkEventAttributes#ALIGN ALIGN}, {@link SinkEventAttributes#WIDTH WIDTH}.
+     * </blockquote>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void verbatim( SinkEventAttributes attributes );
+
+    /**
+     * Ends a verbatim element.
+     */
+    void verbatim_();
+
+    /**
+     * Adding a separator of sections from a text to each other.
+     *
+     * @see #horizontalRule(SinkEventAttributes)
+     */
+    void horizontalRule();
+
+    /**
+     * Adds a horizontal separator rule.
+     *
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#ALIGN ALIGN}, {@link SinkEventAttributes#NOSHADE NOSHADE},
+     *   {@link SinkEventAttributes#SIZE SIZE}, {@link SinkEventAttributes#WIDTH WIDTH}.
+     * </blockquote>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void horizontalRule( SinkEventAttributes attributes );
+
+    /**
+     * Adding a new page separator.
+     */
+    void pageBreak();
+
+    /**
+     * Starts an element which defines an anchor.
+     *
+     * @param name the name of the anchor.
+     * @see #anchor(String,SinkEventAttributes)
+     */
+    void anchor( String name );
+
+    /**
+     * Starts an element which defines an anchor.
+     *
+     * <p>
+     *   The <code>name</code> parameter has to be a valid SGML NAME token.
+     *   According to the <a href="http://www.w3.org/TR/html4/types.html#type-name">
+     *   HTML 4.01 specification section 6.2 SGML basic types</a>:
+     * </p>
+     * <p>
+     *   <i>ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
+     *   followed by any number of letters, digits ([0-9]), hyphens ("-"),
+     *   underscores ("_"), colons (":"), and periods (".").</i>
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes}.
+     *   If {@link SinkEventAttributes#NAME NAME} is specified in the SinkEventAttributes,
+     *   it will be overwritten by the <code>name</code> parameter.
+     * </p>
+     *
+     * @param name the name of the anchor. This has to be a valid SGML NAME token.
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void anchor( String name, SinkEventAttributes attributes );
+
+    /**
+     * Ends an anchor element.
+     */
+    void anchor_();
+
+    /**
+     * Starts an element which defines a link.
+     *
+     * @param name the name of the link.
+     * @see #link(String,SinkEventAttributes)
+     */
+    void link( String name );
+
+    /**
+     * Starts a link.
+     *
+     * <p>
+     *   The <code>name</code> parameter has to be a valid html <code>href</code>
+     *   parameter, ie for internal links (links to an anchor within the same source
+     *   document), <code>name</code> should start with the character "#".
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#CHARSET CHARSET}, {@link SinkEventAttributes#COORDS COORDS},
+     *   {@link SinkEventAttributes#HREF HREF}, {@link SinkEventAttributes#HREFLANG HREFLANG},
+     *   {@link SinkEventAttributes#REL REL}, {@link SinkEventAttributes#REV REV},
+     *   {@link SinkEventAttributes#SHAPE SHAPE}, {@link SinkEventAttributes#TARGET TARGET},
+     *   {@link SinkEventAttributes#TYPE TYPE}.
+     * </blockquote>
+     * <p>
+     *   If {@link SinkEventAttributes#HREF HREF} is specified in the
+     *   SinkEventAttributes, it will be overwritten by the <code>name</code> parameter.
+     * </p>
+     *
+     * @param name the name of the link.
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void link( String name, SinkEventAttributes attributes );
+
+    /**
+     * Ends a link element.
+     */
+    void link_();
+
+    /**
+     * Starts an italic element.
+     *
+     * Alternatively one may use {@link #text(String,SinkEventAttributes)} with
+     *              {@link SinkEventAttributes#STYLE STYLE} instead.
+     */
+    void italic();
+
+    /**
+     * Ends an italic element.
+     *
+     * Alternatively one may use {@link #text(String,SinkEventAttributes)} with
+     *              {@link SinkEventAttributes#STYLE STYLE} instead.
+     */
+    void italic_();
+
+    /**
+     * Starts a bold element.
+     *
+     * Alternatively one may use {@link #text(String,SinkEventAttributes)} with
+     *              {@link SinkEventAttributes#STYLE STYLE} instead.
+     */
+    void bold();
+
+    /**
+     * Ends a bold element.
+     *
+     * Alternatively one may use {@link #text(String,SinkEventAttributes)} with
+     *              {@link SinkEventAttributes#STYLE STYLE} instead.
+     */
+    void bold_();
+
+    /**
+     * Starts a monospaced element.
+     *
+     * Alternatively one may use {@link #text(String,SinkEventAttributes)} with
+     *              {@link SinkEventAttributes#STYLE STYLE} instead.
+     */
+    void monospaced();
+
+    /**
+     * Ends a monospaced element.
+     *
+     * Alternatively one may use {@link #text(String,SinkEventAttributes)} with
+     *              {@link SinkEventAttributes#STYLE STYLE} instead.
+     */
+    void monospaced_();
+
+    /**
+     * Adds a line break.
+     *
+     * @see #lineBreak(SinkEventAttributes)
+     */
+    void lineBreak();
+
+    /**
+     * Adds a line break.
+     *
+     * <p>
+     *   Supported attributes are:
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#ID ID}, {@link SinkEventAttributes#CLASS CLASS},
+     *   {@link SinkEventAttributes#TITLE TITLE}, {@link SinkEventAttributes#STYLE STYLE}.
+     * </blockquote>
+     *
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void lineBreak( SinkEventAttributes attributes );
+
+    /**
+     * Adding a non breaking space, <i>ie</i> a space without any special formatting operations.
+     */
+    void nonBreakingSpace();
+
+    /**
+     * Adding a text.
+     *
+     * @param text The text to write.
+     * @see #text(String,SinkEventAttributes)
+     */
+    void text( String text );
+
+    /**
+     * Adds a text.
+     *
+     * <p>
+     *   The <code>text</code> parameter should contain only real content, ie any
+     *   ignorable/collapsable whitespace/EOLs or other pretty-printing should
+     *   be removed/normalized by a parser.
+     * </p>
+     * <p>
+     *   If <code>text</code> contains any variants of line terminators, they should
+     *   be normalized to the System EOL by an implementing Sink.
+     * </p>
+     * <p>
+     *   Supported attributes are the {@link SinkEventAttributes base attributes} plus
+     * </p>
+     * <blockquote>
+     *   {@link SinkEventAttributes#VALIGN VALIGN} (values "sub", "sup"),
+     *   {@link SinkEventAttributes#DECORATION DECORATION} (values "underline", "overline", "line-through"),
+     *   {@link SinkEventAttributes#STYLE STYLE} (values "italic", "bold", "monospaced").
+     * </blockquote>
+     *
+     * @param text The text to write.
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void text( String text, SinkEventAttributes attributes );
+
+    /**
+     * Adding a raw text, <i>ie</i> a text without any special formatting operations.
+     *
+     * @param text The text to write.
+     */
+    void rawText( String text );
+
+    /**
+     * Add a comment.
+     *
+     * @param comment The comment to write.
+     * @since 1.1
+     */
+    void comment( String comment );
+
+    /**
+     * Add an unknown event. This may be used by parsers to notify a general Sink about
+     * an event that doesn't fit into any event defined by the Sink API.
+     * Depending on the parameters, a Sink may decide whether or not to process the event,
+     * emit it as raw text, as a comment, log it, etc.
+     *
+     * @param name The name of the event.
+     * @param requiredParams An optional array of required parameters to the event.
+     * May be <code>null</code>.
+     * @param attributes A set of {@link SinkEventAttributes}, may be <code>null</code>.
+     * @since 1.1
+     */
+    void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes );
+
+    /**
+     * Flush the writer or the stream, if needed.
+     * Flushing a previously-flushed Sink has no effect.
+     */
+    void flush();
+
+    /**
+     * Close the writer or the stream, if needed.
+     * Closing a previously-closed Sink has no effect.
+     */
+    void close();
+}
diff --git a/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/SinkEventAttributes.java b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/SinkEventAttributes.java
new file mode 100644
index 0000000..59dea12
--- /dev/null
+++ b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/SinkEventAttributes.java
@@ -0,0 +1,359 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.MutableAttributeSet;
+
+/**
+ * A set of attributes for a sink event.
+ * <p>
+ * All sink methods that produce some presentation-level output should have at least
+ * one form that allows to pass in a Set of SinkEventAttributes. For instance in
+ * <pre>void text( String text, SinkEventAttributes attributes );</pre>
+ * the <code>attributes</code> parameter can be used to specify some text styling
+ * options, or other optional parameters.
+ * </p>
+ * <p>
+ * What kind of attributes are supported depends on the event and the sink
+ * implementation. The sink API just specifies a list of suggested attribute
+ * names, that sinks are expected to recognize, and parsers are expected to use
+ * preferably when emitting events.
+ * </p>
+ * <p>
+ * It is recommended that for simple attributes, both keys and values should be
+ * lower-case Strings, but this is not mandatory. One example of an exception is
+ * the {@link #STYLE} attribute, whose value may itself be an AttributeSet again.
+ * </p>
+ * <p>
+ * The <b>base attributes</b> that are supported by almost all events are
+ * {@link #CLASS}, {@link #ID}, {@link #LANG}, {@link #STYLE} and {@link #TITLE}.
+ * </p>
+ *
+ * @author ltheussl
+ * @version $Id: SinkEventAttributes.java 733395 2009-01-10 23:09:40Z ltheussl $
+ * @since 1.1
+ */
+public interface SinkEventAttributes
+    extends MutableAttributeSet
+{
+    // base
+
+    /**
+     * The class of the event element.
+     */
+    String CLASS = "class";
+
+    /**
+     * A unique id for the event element.
+     */
+    String ID = "id";
+
+    /**
+     * The language code for the event element.
+     */
+    String LANG = "lang";
+
+    /**
+     * An inline style definition.
+     *
+     * <p>
+     *   Generally supported values are "italic", "bold", "monospaced" and AttributeSets.
+     * </p>
+     * <p>
+     *   If the value of this Attribute is itself an AttributeSet, it is interpreted as a
+     *   sequence of CSS properties. For instance, the HTML paragraph opening
+     * </p>
+     * <pre>
+     *   <p style="color: red; margin-left: 20px">
+     * </pre>
+     * <p>
+     *   can be produced by an HTML Sink via the event
+     *   <code>{@link Sink#paragraph(SinkEventAttributes)}</code>, where the value of the
+     *   SinkEventAttribute is an AttributeSet with two Attributes ("<code>color</code>" and
+     *   "<code>margin-left</code>" with values "<code>red</code>" and "<code>20px</code>",
+     *   respectively).
+     * </p>
+     */
+    String STYLE = "style";
+
+    /**
+     * A text to display in a tool tip.
+     */
+    String TITLE = "title";
+
+    // head
+
+    /**
+     * A space separated list of URL's that contains meta data information about the document.
+     */
+    String PROFILE = "profile";
+
+    /**
+     * An electronic mail address.
+     */
+    String EMAIL = "email";
+
+
+    // img
+
+    /**
+     * Specifies the alignment of the event element within its parent element.
+     *
+     * <p>
+     *   Generally supported values are "left", "right", "center", "justify".
+     * </p>
+     */
+    String ALIGN = "align";
+
+    /**
+     * Defines a short description of the event element.
+     */
+    String ALT = "alt";
+
+    /**
+     * Defines a border around an event element.
+     */
+    String BORDER = "border";
+
+    /**
+     * Defines the height of an event element.
+     */
+    String HEIGHT = "height";
+
+    /**
+     * Defines white space on the left and right side of an event element.
+     */
+    String HSPACE = "hspace";
+
+    /**
+     * Defines an image as a server-side image map. Only used by the figureGraphics Sink event.
+     */
+    String ISMAP = "ismap";
+
+    /**
+     * The URL of an external resource, eg an image.
+     */
+    String SRC = "src";
+
+    /**
+     * Defines an image as a client-side image map.
+     */
+    String USEMAP = "usemap";
+
+    /**
+     * Defines white space on the top and bottom of the event element.
+     */
+    String VSPACE = "vspace";
+
+    /**
+     * Sets the width of  an event element.
+     */
+    String WIDTH = "width";
+
+    // hr
+
+    /**
+     * Used to indicate that an element comes with a shadow.
+     */
+    String NOSHADE = "noshade";
+
+    /**
+     * Specifies the size, or thickness, or height of an event element.
+     */
+    String SIZE = "size";
+
+    // anchor
+
+    /**
+     * Specifies the name of an anchor.
+     */
+    String NAME = "name";
+
+    // link
+
+    /**
+     * Specifies the character encoding of text associated with an event element.
+     */
+    String CHARSET = "charset";
+
+    /**
+     * May be used in conjunction with {@link #SHAPE}.
+     *
+     * <p>
+     *   Valid values are the same as for the corresponding HTML attributes.
+     * </p>
+     */
+    String COORDS = "coords";
+
+    /**
+     * The target URL of an event element, eg a link.
+     */
+    String HREF = "href";
+
+    /**
+     * Specifies the base language of the target URL.
+     *
+     * <p>
+     *   Used in conjunction with {@link #HREF}.
+     * </p>
+     */
+    String HREFLANG = "hreflang";
+
+    /**
+     * For references to external resourcs, specifies the relationship between
+     * the current document and the target URL.
+     *
+     * <p>
+     *   Valid values are the same as for the corresponding HTML attribute.
+     * </p>
+     */
+    String REL = "rel";
+
+    /**
+     * For references to external resourcs, specifies the relationship between
+     * the target URL and the current document.
+     *
+     * <p>
+     *   Valid values are the same as for the corresponding HTML attribute.
+     * </p>
+     */
+    String REV = "rev";
+
+    /**
+     * Defines the type of region to be defined for a mapping.
+     *
+     * <p>
+     *   Used with the {@link #COORDS} attribute.
+     * </p>
+     */
+    String SHAPE = "shape";
+
+    /**
+     * Where to open the target URL.
+     *
+     * <p>
+     *   Valid values are the same as for the corresponding HTML attribute.
+     * </p>
+     */
+    String TARGET = "target";
+
+    /**
+     * Specifies the MIME (Multipurpose Internet Mail Extensions) type of an
+     * external resource URL, eg a link.
+     */
+    String TYPE = "type";
+
+    // table
+
+    /**
+     * Specifies the background color of an event element.
+     */
+    String BGCOLOR = "bgcolor";
+
+    /**
+     * Specifies the space between cell walls and contents.
+     */
+    String CELLPADDING = "cellpadding";
+
+    /**
+     * Specifies the space between cells.
+     */
+    String CELLSPACING = "cellspacing";
+
+    /**
+     * Specifies which sides of a border surrounding an element should be visible.
+     *
+     * <p>
+     *   Valid values are the same as for the corresponding HTML attribute.
+     * </p>
+     */
+    String FRAME = "frame";
+
+    /**
+     * Specifies horizontal/vertical divider lines between certain elements, eg table cells.
+     */
+    String RULES = "rules";
+
+    /**
+     * Specifies a summary of an event attribute for speech-synthesizing/non-visual target output.
+     */
+    String SUMMARY = "summary";
+
+    // table cell
+
+    /**
+     * Specifies an abbreviated version of the content in an element.
+     */
+    String ABBRV = "abbrv";
+
+    /**
+     * Defines a name for a cell.
+     */
+    String AXIS = "axis";
+
+    /**
+     * Indicates the number of columns a cell should span. Used in tables.
+     */
+    String COLSPAN = "colspan";
+
+    /**
+     * A space-separated list of cell IDs that supply header information for the cell.
+     */
+    String HEADERS = "headers";
+
+    /**
+     * Whether to disable or enable automatic text wrapping for an element.
+     */
+    String NOWRAP = "nowrap";
+
+    /**
+     * Indicates the number of rows a cell should span. Used in tables.
+     */
+    String ROWSPAN = "rowspan";
+
+    /**
+     * A general scope parameter. In Particular, for table cells this
+     * specifies if the cell provides header information for the rest of the
+     * row that contains it ("row"), or for the rest of the column ("col"),
+     * or for the rest of the row group that contains it ("rowgroup"),
+     * or for the rest of the column group that contains it ("colgroup").
+     */
+    String SCOPE = "scope";
+
+    /**
+     * Specifies the vertical alignment of an element.
+     *
+     * <p>
+     *   Generally accepted values are "top", "baseline", "middle", "bottom", "sup", "sub".
+     * </p>
+     */
+    String VALIGN = "valign";
+
+    // text
+
+    /**
+     * Specifies a decoration for an element.
+     *
+     * <p>
+     *   Generally accepted values are "underline", "overline", "line-through", "boxed".
+     * </p>
+     */
+    String DECORATION = "decoration";
+}
diff --git a/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/SinkFactory.java b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/SinkFactory.java
new file mode 100644
index 0000000..2f84127
--- /dev/null
+++ b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/SinkFactory.java
@@ -0,0 +1,85 @@
+package org.apache.maven.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A factory that creates a <code>Sink</code> object.
+ *
+ * @author <a href="kenney at apache.org">Kenney Westerhof</a>
+ * @version $Id: SinkFactory.java 736010 2009-01-20 13:06:57Z vsiveton $
+ * @since 1.0-alpha-9
+ */
+public interface SinkFactory
+{
+    /** The Plexus SinkFactory Role. */
+    String ROLE = SinkFactory.class.getName();
+
+    /**
+     * Create a <code>Sink</code> into a file.
+     *
+     * @param outputDir the not-null output dir.
+     * @param outputName the not-null output name.
+     * @return a <code>Sink</code> instance with a file as output.
+     * @throws java.io.IOException if any
+     */
+    Sink createSink( File outputDir, String outputName )
+        throws IOException;
+
+    /**
+     * Create a <code>Sink</code> into a file using a specified encoding.
+     *
+     * @param outputDir the not-null output dir.
+     * @param outputName the not-null output name.
+     * @param encoding the output encoding.
+     * @return a <code>Sink</code> instance with a file as output and using specified encoding.
+     * @throws java.io.IOException if any
+     * @see #createSink(File, String)
+     * @since 1.1
+     */
+    Sink createSink( File outputDir, String outputName, String encoding )
+        throws IOException;
+
+    /**
+     * Create a <code>Sink</code> into an OutputStream.
+     *
+     * @param out not null OutputStream to write the result.
+     * @return a <code>Sink</code> instance.
+     * @throws java.io.IOException if any
+     * @since 1.1
+     */
+    Sink createSink( OutputStream out )
+        throws IOException;
+
+    /**
+     * Create a <code>Sink</code> into an OutputStream using a specified encoding.
+     *
+     * @param out not null OutputStream to write the result.
+     * @param encoding the output encoding.
+     * @return a <code>Sink</code> instance using specified encoding.
+     * @throws java.io.IOException if any
+     * @since 1.1
+     */
+    Sink createSink( OutputStream out, String encoding )
+        throws IOException;
+}
diff --git a/doxia-sink-api/src/main/java/org/codehaus/doxia/sink/Sink.java b/doxia-sink-api/src/main/java/org/codehaus/doxia/sink/Sink.java
new file mode 100644
index 0000000..fa3ab80
--- /dev/null
+++ b/doxia-sink-api/src/main/java/org/codehaus/doxia/sink/Sink.java
@@ -0,0 +1,32 @@
+package org.codehaus.doxia.sink;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Generic document processing interface.
+ *
+ * @deprecated use {@link org.apache.maven.doxia.sink.Sink} instead (since 1.0-alpha-6).
+ * @version $Id: Sink.java 712143 2008-11-07 14:50:52Z hboutemy $
+ */
+public interface Sink
+    extends org.apache.maven.doxia.sink.Sink
+{
+    // nop
+}
diff --git a/doxia-sink-api/src/main/javadoc/org/apache/maven/doxia/sink/package.html b/doxia-sink-api/src/main/javadoc/org/apache/maven/doxia/sink/package.html
new file mode 100644
index 0000000..27d1272
--- /dev/null
+++ b/doxia-sink-api/src/main/javadoc/org/apache/maven/doxia/sink/package.html
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<body>
+  <h1>Maven Doxia API.</h1>
+  <p>The Doxia API is based on <i>Sinks</i> objects: each sink consumes Doxia events to produce a resultant output
+  format (eg Docbook, PDF, XHTML...).</p>
+
+  <h2>Using Maven Doxia API</h2>
+  <p>The following snippet shows how to use a Doxia <code>Sink</code>:</p>
+
+  <div align="left" class="java">
+    <table border="0" cellpadding="3" cellspacing="0" bgcolor="#ffffff">
+      <tr>
+        <!-- start source code -->
+        <td nowrap="nowrap" valign="top" align="left"><code> <font
+          color="#ffffff">  </font><font color="#000000">File userDir = </font><font
+          color="#7f0055"><b>new </b></font><font color="#000000">File</font><font
+          color="#000000">( </font><font color="#000000">System.getProperty </font><font
+          color="#000000">( </font><font color="#2a00ff">"user.dir" </font><font
+          color="#000000">) )</font><font color="#000000">;</font><br />
+        <font color="#ffffff">  </font><font color="#000000">File outputFile = </font><font
+          color="#7f0055"><b>new </b></font><font color="#000000">File</font><font
+          color="#000000">( </font><font color="#000000">userDir, </font><font
+          color="#2a00ff">"test.html" </font><font color="#000000">)</font><font
+          color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">SinkFactory sinkFactory = </font><font
+          color="#000000">(</font><font color="#000000">SinkFactory</font><font
+          color="#000000">) </font><font color="#000000">lookup</font><font
+          color="#000000">( </font><font color="#000000">SinkFactory.ROLE, </font><font
+          color="#2a00ff">"html" </font><font color="#000000">)</font><font
+          color="#000000">; </font><font color="#3f7f5f">// Plexus lookup</font><br />
+        <font color="#ffffff">  </font><font color="#000000">Sink sink = sinkFactory.createSink</font><font
+          color="#000000">( </font><font color="#000000">outputFile.getParentFile</font><font
+          color="#000000">()</font><font color="#000000">, outputFile.getName</font><font
+          color="#000000">() ) )</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#3f7f5f">// Sink head</font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.head</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.title</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.text</font><font
+          color="#000000">( </font><font color="#2a00ff">"Title" </font><font
+          color="#000000">)</font><font color="#000000">;</font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.title_</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.author</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.text</font><font
+          color="#000000">( </font><font color="#2a00ff">"Author" </font><font
+          color="#000000">)</font><font color="#000000">;</font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.author_</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.date</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.text</font><font
+          color="#000000">( </font><font color="#2a00ff">"Date" </font><font
+          color="#000000">)</font><font color="#000000">;</font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.date_</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.head_</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff">  </font><font color="#3f7f5f">// Sink head</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.body</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">   </font><font color="#000000">sink.paragraph</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff">   </font><font color="#000000">sink.text</font><font
+          color="#000000">( </font><font color="#2a00ff">"Paragraph 1, line 1. Paragraph 1, line 2." </font><font
+          color="#000000">)</font><font color="#000000">;</font><br />
+        <font color="#ffffff">   </font><font color="#000000">sink.paragraph_</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">   </font><font color="#000000">...</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.body_</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.flush</font><font
+          color="#000000">()</font><font color="#000000">;</font><br />
+        <font color="#ffffff"></font><br />
+        <font color="#ffffff">  </font><font color="#000000">sink.close</font><font
+          color="#000000">()</font><font color="#000000">;</font></code></td>
+        <!-- end source code -->
+      </tr>
+    </table>
+  </div>
+
+  <h2>Resources</h2>
+  <ul>
+    <li><a href="http://maven.apache.org/doxia/">Maven Doxia Website</a></li>
+    <li><a href="http://plexus.codehaus.org/">Plexus</a></li>
+  </ul>
+</body>
diff --git a/doxia-sink-api/src/site/apt/index.apt b/doxia-sink-api/src/site/apt/index.apt
new file mode 100644
index 0000000..5b73257
--- /dev/null
+++ b/doxia-sink-api/src/site/apt/index.apt
@@ -0,0 +1,48 @@
+ -----
+ Doxia Sink API
+ -----
+ Hervé Boutemy
+ -----
+ 2010-05-08
+ -----
+
+Doxia Sink API
+
+  API to generate Doxia documents.
+
+* API Changes
+
+  With every Doxia release, a new <<<doxia-sink-api>>> artifact has been released even if the API itself didn't change.
+
+  Here is a summary of API changes:
+
+*--------------+--------------------------+
+|| <<doxia-sink-api version>> || <<change description>> ||
+*--------------+--------------------------+
+| 1.0-alpha-6  | added <<<org.apache.maven.doxia.sink.Sink>>> interface which supercedes <<<org.codehaus.doxia.sink.Sink>>> |
+*--------------+--------------------------+
+| 1.0-alpha-9  | added <<<org.apache.maven.doxia.sink.SinkFactory>>> interface |
+*--------------+--------------------------+
+| 1.1          | added <<<org.apache.maven.doxia.sink.SinkEventAttributes>>> interface and a dependency on <<<doxia-logging-api>>> |
+*--------------+--------------------------+
+
+  Note that <<<doxia-sink-api 1.0>>> is equivalent to <<<doxia-sink-api 1.0-alpha-9>>>.
+
+* Maven dependency
+
+  <<<doxia-sink-api>>> is included in every Maven 2 distribution. Decoupling has been done in Maven 3.
+
+*--------------------+------------------------------+
+|| <<Maven version>> || <<doxia-sink-api version>>  ||
+*--------------------+------------------------------+
+|        2.0.x       |            1.0               |
+*--------------------+------------------------------+
+|        2.1.x+      |            1.1               |
+*--------------------+------------------------------+
+|        3.0+        | <no more dependency>         |
+*--------------------+------------------------------+
+
+  Maven 2.0.8 is the first Maven 2.0.x version including <<<doxia-sink-api 1.0(-alpha-9)>>>.
+
+  To hide the difference between <<<doxia-sink-api 1.0-alpha-6>>> and <<<1.0>>> included in previous Maven 2.0.x versions,
+  <<<org.apache.maven.doxia.sink.SinkFactory>>> interface was copied into <<<maven-site-plugin 2.0-beta-6>>>.
diff --git a/doxia-sink-api/src/site/site.xml b/doxia-sink-api/src/site/site.xml
new file mode 100644
index 0000000..173f6e5
--- /dev/null
+++ b/doxia-sink-api/src/site/site.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+
+  <body>
+
+    <menu ref="parent"/>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
\ No newline at end of file
diff --git a/doxia-test-docs/pom.xml b/doxia-test-docs/pom.xml
new file mode 100644
index 0000000..2422138
--- /dev/null
+++ b/doxia-test-docs/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.doxia</groupId>
+    <artifactId>doxia</artifactId>
+    <version>1.1.4</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>doxia-test-docs</artifactId>
+  <name>Doxia :: Test Documents</name>
+  <description>Several test documents to check syntax structures under Doxia.</description>
+</project>
diff --git a/doxia-test-docs/src/main/resources/doxia-site/fml/faq.fml b/doxia-test-docs/src/main/resources/doxia-site/fml/faq.fml
new file mode 100644
index 0000000..e0ae3cf
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/doxia-site/fml/faq.fml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="How_to_handle_style_in_the_APT_markup_language">
+      <question>How to handle style in the APT markup language?</question>
+      <answer>
+        <p>
+          APT doesn't currently support style. It is in the roadmap.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How_to_export_in_PDF">
+      <question>How to export in PDF?</question>
+      <answer>
+        <p>
+          There are two modules available that can be used to generate pdf output: an
+          <a href="http://maven.apache.org/doxia/doxia/doxia-modules/doxia-module-itext/">iText module</a>
+          that uses the
+          <a href="http://www.lowagie.com/iText/">iText</a> framework, and a
+          <a href="http://maven.apache.org/doxia/doxia/doxia-modules/doxia-module-fo/">FO</a> module,
+          that can be used e.g. in conjunction with
+          <a href="http://xmlgraphics.apache.org/fop/">Apache FOP</a> to generate a pdf.
+          Unfortunately, the iText team has discontinued the XML to PDF functionalities, so probably
+          only the fo module is going to be supported in the future.
+        </p>
+        <p>
+          A pdf plugin for m2 is currently in development in the Doxia sandbox. You can get the source
+          <a href="http://svn.apache.org/repos/asf/maven/sandbox/trunk/plugins/maven-pdf-plugin/">here</a>.
+        </p>
+      </answer>
+    </faq>
+    <faq id="Is_it_possible_to_create_a_book">
+      <question>Is it possible to create a book?</question>
+      <answer>
+        <p>
+          Doxia also has a fairly simple tool for writing books. It comes complete with a Maven plugin
+          to produce PDFs, LaTeX documents and Xdoc for direct integration in your Maven site.
+          The <a href="http://svn.apache.org/repos/asf/maven/doxia/doxia/trunk/doxia-book/">Doxia Book code</a>
+          is still limited but fully functional.
+        </p>
+        <p>
+          See <a href="./book/index.html">Writing Books in Doxia</a> for more information.
+        </p>
+      </answer>
+    </faq>
+    <faq id="Why XML based sinks don't generate nicely formatted documents">
+      <question>Why XML based sinks don't generate nicely formatted documents?</question>
+      <answer>
+        <p>
+          We decided to keep pretty printing out of the core modules. So, XML based sinks like Xdoc or XHTML are
+          intentionally unformatted. You could always do this after the document generation or directly
+          by creating a specialized end-user sink (see <a href="http://jira.codehaus.org/browse/DOXIA-255">DOXIA-255</a>).
+        </p>
+      </answer>
+    </faq>
+    <!-- TODO need to publish XSD files -->
+    <faq id="doxia-xsd">
+      <question>Where are the Maven Doxia XSD schemas for Xdoc and FML files?</question>
+      <answer>
+        <p>
+          The Xdoc XSD is located <a href="http://maven.apache.org/xsd/xdoc-2.0.xsd">here</a> and
+          the FML XSD is located <a href="http://maven.apache.org/xsd/fml-1.0.1.xsd">here</a>.
+        </p>
+        <p>
+          Your favorite IDE probably supports XSD schema's for Xdoc and FML files. You need to
+          specify the following:
+        <source>
+<document xmlns="http://maven.apache.org/XDOC/2.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
+  ...
+</document></source>
+        <source>
+<faqs xmlns="http://maven.apache.org/FML/1.0.1"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/FML/1.0.1 http://maven.apache.org/xsd/fml-1.0.1.xsd">
+  ...
+</faqs></source>
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/fml-format.xml b/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/fml-format.xml
new file mode 100644
index 0000000..283e2ad
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/fml-format.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+<document>
+
+  <properties>
+    <title>The FML (FAQ Markup Language) format</title>
+    <author email="ltheussl_AT_apache_DOT_org">Lukas Theussl</author>
+  </properties>
+
+  <body>
+
+    <section name="The FML format">
+
+      <subsection name="Overview">
+
+        <p>
+          An 'fml' is an XML document conforming to a small and simple set of tags.
+          The format was first used in the <a href="http://maven.apache.org/maven-1.x/">Maven 1</a>,
+          version of the <a href="http://maven.apache.org/maven-1.x/plugins/faq/">FAQ plugin</a>.
+        </p>
+
+      </subsection>
+
+      <subsection name="The FML format">
+
+        <p>
+          The following is a sample FML document:
+        </p>
+        <source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+
+<faqs title="Frequently Asked Questions" toplink="false">
+
+  <part id="general">
+    <title>General</title>
+
+    <faq id="whats-foo">
+      <question>
+        What is Foo?
+      </question>
+      <answer>
+        <p>some markup goes here</p>
+
+        <source>some source code</source>
+
+        <p>some markup goes here</p>
+      </answer>
+    </faq>
+
+    <faq id="whats-bar">
+      <question>
+        What is Bar?
+      </question>
+      <answer>
+        <p>some markup goes here</p>
+      </answer>
+    </faq>
+  </part>
+
+  <part id="install">
+
+    <title>Installation</title>
+
+    <faq id="how-install">
+      <question>
+        How do I install Foo?
+      </question>
+      <answer>
+        <p>some markup goes here</p>
+      </answer>
+    </faq>
+
+  </part>
+
+</faqs>
+]]></source>
+
+      </subsection>
+
+    </section>
+
+  </body>
+
+</document>
diff --git a/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/index.xml b/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/index.xml
new file mode 100644
index 0000000..610a21d
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/index.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<document>
+
+  <properties>
+    <title>Doxia Markup Languages References</title>
+    <author email="ltheussl_AT_apache_DOT_org">Lukas Theussl</author>
+    <author email="vsiveton_AT_apache_DOT_org">Vincent Siveton</author>
+  </properties>
+
+  <body>
+
+    <section name="Doxia Markup Languages References">
+      <p>
+        The following table gives an overview of the markup languages currently
+        supported by Doxia. If a Parser is available for a given format, it
+        means that you can write your documentation in this language and Doxia
+        can generate output from it. If a Sink is available, it means you can
+        generate output in this format.
+      </p>
+
+      <p>
+        The source directory is the directory under which Maven expects source
+        documents in this format (e.g. <code>src/site/apt/</code> for Apt), the
+        extension is the default file extension, and the parser id is gives the
+        unique identifier that is used by plexus to lookup the corresponding
+        component.
+      </p>
+
+      <p>
+        Please consult the <a href="../modules/index.html">Modules Guide</a>
+        for links to reference documentation of external formats.
+      </p>
+
+      <p>
+
+        <table border="0">
+          <tr>
+            <th>Format</th>
+            <th>Short description</th>
+            <th align="center">Parser</th>
+            <th align="center">Sink</th>
+            <th>Source Directory</th>
+            <th>Extension</th>
+            <th>Parser Id</th>
+          </tr>
+
+          <tr>
+            <td><a href="./apt-format.html">Apt</a></td>
+            <td>Almost Plain Text</td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td><code>apt</code></td>
+            <td><code>apt</code></td>
+            <td><code>apt</code></td>
+          </tr>
+
+          <tr>
+            <td>Confluence</td>
+            <td>Confluence Enterprise Wiki</td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td><code>confluence</code></td>
+            <td><code>confluence</code></td>
+            <td><code>confluence</code></td>
+          </tr>
+
+          <tr>
+            <td>Simplified DocBook</td>
+            <td>Simplified DocBook XML Standard</td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td><code>docbook</code></td>
+            <td><code>xml</code></td>
+            <td><code>doc-book</code></td>
+          </tr>
+
+          <tr>
+            <td><a href="./fml-format.html">FML</a></td>
+            <td>FAQ Markup Language</td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_error_sml.gif" alt="Yes"/></td>
+            <td><code>fml</code></td>
+            <td><code>fml</code></td>
+            <td><code>fml</code></td>
+          </tr>
+
+          <tr>
+            <td>iText</td>
+            <td>iText PDF Library</td>
+            <td align="center"><img src="../images/icon_error_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td></td>
+            <td></td>
+            <td></td>
+          </tr>
+
+          <tr>
+            <td>LaTeX</td>
+            <td>LaTeX typesetting system</td>
+            <td align="center"><img src="../images/icon_error_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td></td>
+            <td></td>
+            <td></td>
+          </tr>
+
+          <tr>
+            <td>RTF</td>
+            <td>Microsoft Rich Text Format</td>
+            <td align="center"><img src="../images/icon_error_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td></td>
+            <td></td>
+            <td></td>
+          </tr>
+
+          <tr>
+            <td>TWiki</td>
+            <td>TWiki Structured Wiki</td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td><code>twiki</code></td>
+            <td><code>twiki</code></td>
+            <td><code>twiki</code></td>
+          </tr>
+
+          <tr>
+            <td><a href="./xdoc-format.html">Xdoc</a></td>
+            <td>XML Documentation Format</td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td><code>xdoc</code></td>
+            <td><code>xml</code></td>
+            <td><code>xdoc</code></td>
+          </tr>
+
+          <tr>
+            <td>XHTML</td>
+            <td>Extensible Hypertext Markup Language</td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td align="center"><img src="../images/icon_success_sml.gif" alt="Yes"/></td>
+            <td><code>xhtml</code></td>
+            <td><code>xhtml</code></td>
+            <td><code>xhtml</code></td>
+          </tr>
+        </table>
+
+      </p>
+
+    </section>
+
+  </body>
+
+</document>
diff --git a/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/xdoc-format.xml b/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/xdoc-format.xml
new file mode 100644
index 0000000..370dda9
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/doxia-site/xdoc/references/xdoc-format.xml
@@ -0,0 +1,293 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+<document>
+
+  <properties>
+    <title>The Xdoc format</title>
+    <author email="ltheussl_AT_apache_DOT_org">Lukas Theussl</author>
+  </properties>
+
+  <body>
+
+    <section name="The XDoc format">
+
+      <subsection name="Overview">
+
+        <p>
+          An 'xdoc' is an XML document conforming to a small and simple set of tags.
+          Xdoc was the primary documentation format in <a href="http://maven.apache.org/maven-1.x/">Maven 1</a>,
+          Maven 2 largely replaced this by <a href="apt-format.html">Apt</a>, but xdoc is still supported.
+        </p>
+
+        <p>
+          Historically, the xdoc format can be traced back to the
+          <a href="http://jakarta.apache.org/site/jakarta-site2.html">Anakia</a> format, as once used by the
+          <a href="http://jakarta.apache.org/">Apache Jakarta</a> project.
+        </p>
+
+        <p>
+          The Maven 1 Xdoc plugin introduced a few additions to the Anakia format, they are highlighted in the
+          <a href="http://maven.apache.org/maven-1.x/plugins/xdoc/reference/xdocs.html">plugin</a> documentation.
+        </p>
+
+      </subsection>
+
+      <subsection name="The XDoc format">
+
+        <p>
+          The following is a sample XDoc document:
+        </p>
+        <source><![CDATA[
+<document>
+
+  <properties>
+    <title>Page Title</title>
+    <author email="user at company.com">John Doe</author>
+  </properties>
+
+  <!-- Optional HEAD element, which is copied as is into the XHTML <head> element -->
+  <head>
+    <meta ... />
+  </head>
+
+  <body>
+
+    <!-- The body of the document contains a number of sections -->
+    <section name="section 1">
+
+      <!-- Within sections, any XHTML can be used -->
+      <p>Hi!</p>
+
+      <!-- in addition to XHTML, any number of subsections can be within a section -->
+      <subsection name="subsection 1">
+        <p>Subsection!</p>
+      </subsection>
+
+    </section>
+
+    <section name="other section">
+
+      <!-- You can also include preformatted source blocks in the document -->
+      <source>
+code line 1
+code line 2
+      </source>
+
+    </section>
+
+  </body>
+
+</document>]]></source>
+
+      </subsection>
+
+      <subsection name="Additional sectioning">
+
+        <p>
+          Doxia will produce <code><h2></code> and
+          <code><h3></code> headings for <code><section></code>
+          and <code><subsection></code> elements, respectively.
+          It is therefore perfectly valid to put some sub-headings
+          (<code><h4></code>, <code><h5></code>,
+          <code><h6></code>) inside a subsection. For instance,
+        </p>
+
+        <source><![CDATA[<h4>A subsubsection</h4>]]></source>
+
+        <p>
+          will produce:
+        </p>
+
+        <h4>A subsubsection</h4>
+
+      </subsection>
+
+      <subsection name="Referencing sections and subsections">
+
+        <p>
+          The core doxia modules do <b>not</b> construct anchors from
+          section/subsection names. If you want to reference a section,
+          you should either provide an explicit anchor:
+        </p>
+
+        <source><![CDATA[<a name="Section1"/>
+<section name="Section">
+
+  <a name="SubSection1"/>
+  <subsection name="SubSection">
+  </subsection>
+
+</section>]]></source>
+
+        <p>
+          or use an <code>id</code> attribute for section and subsections
+          (note that <code>id</code>'s have to be unique within one xdoc
+          source document):
+        </p>
+
+        <source><![CDATA[<section name="Section" id="Section1">
+
+  <subsection name="SubSection" id="SubSection1">
+  </subsection>
+
+</section>]]></source>
+
+        <p>
+          <b>Note</b> that this differs from previous behavior, where anchors
+          were constructed from section/subsection names, replacing special
+          characters by underscores. This behavior presents two shortcomings:
+        </p>
+
+        <ul>
+
+          <li>
+            If two sections or subsections have identical names
+            (within one source document), you will get an ambiguity when
+            referencing them. Also the resulting html document will not be
+            valid XHTML. For other output formats (eg pdf), it might even be impossible
+            to generate the target document.
+          </li>
+
+          <li>
+            For long section titles, this leads to rather cumbersome anchor names.
+          </li>
+
+        </ul>
+
+        <p>
+          If automatic anchor generation is desired for a particular output format,
+          it should be implemented / overridden by the corresponding low-level Sink.
+        </p>
+
+      </subsection>
+
+    </section>
+
+    <section name="Validation">
+
+      <p>
+        Doxia is currently not able to validate your xdoc files as no schema or DTD
+        exists yet (however this is planned before the 1.0 release).
+        It is therefore necessary to check manually whether your source files are valid xdocs,
+        this should ensure that the generated html files are valid
+        <a href="http://www.w3.org/TR/xhtml1/">XHTML1-transitional</a>.
+      </p>
+
+      <p>
+        Here is a list of common mistakes to be aware of:
+      </p>
+
+      <subsection name="Don't nest block level elements">
+
+        <p>Wrong:</p>
+
+        <source><![CDATA[<p>
+  Here's a list:
+  <ul>
+    <li>item 1</li>
+    <li>item 2</li>
+  </ul>
+  of things to do.
+</p>]]></source>
+
+        <p>Correct:</p>
+
+        <source><![CDATA[<p>
+  Here's a list:
+</p>
+<ul>
+  <li>item 1</li>
+  <li>item 2</li>
+</ul>
+<p>
+  of things to do.
+</p>]]></source>
+
+        <p>
+          Typical block level elements are list elements,
+          <code><table></code>, <code><source></code>,
+          <code><div></code>, <code><p></code> and
+          <code><pre></code>.
+        </p>
+
+      </subsection>
+
+      <subsection name="Put inline elements inside block level elements">
+
+        <p>Wrong:</p>
+
+        <source><![CDATA[<section name="Downloads">
+  <a href="downloads.html">Downloads</a>
+</section>]]></source>
+
+        <p>Correct:</p>
+
+        <source><![CDATA[<section name="Downloads">
+  <p>
+    <a href="downloads.html">Downloads</a>
+  </p>
+</section>]]></source>
+
+        <p>
+          Typical inline elements are
+          <code><a></code>, <code><strong></code>,
+          <code><code></code>, <code><font></code>,
+          <code><br></code> and <code><img></code>.
+        </p>
+
+      </subsection>
+
+      <subsection name="Right order of elements in <properties>">
+
+        <p>
+          The <code><title></code> element has to come before
+          <code><author></code>.
+        </p>
+
+      </subsection>
+
+      <subsection name="Dont put <source> inside paragraphs">
+
+        <p>Wrong:</p>
+
+        <source><![CDATA[<p>
+  The following command executes the program:
+  <source>java -jar CoolApp.jar</source>
+</p>]]></source>
+
+        <p>Correct:</p>
+
+        <source><![CDATA[<p>
+  The following command executes the program:
+</p>
+<source>java -jar CoolApp.jar</source>]]></source>
+
+        <p>
+          However, you may put <code><source></code> elements inside
+          list items or table rows.
+        </p>
+
+      </subsection>
+
+    </section>
+
+  </body>
+
+</document>
diff --git a/doxia-test-docs/src/main/resources/maven-ant-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-ant-plugin/fml/faq.fml
new file mode 100644
index 0000000..5d0b406
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-ant-plugin/fml/faq.fml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="Where are the generated Ant build files">
+      <question>Where are the generated Ant build files?</question>
+      <answer>
+        <p>
+          The files are generated in the Maven <i>${basedir}</i> directory.
+        </p>
+      </answer>
+    </faq>
+    <faq id="What are the generated key tasks">
+      <question>What are the generated key tasks?</question>
+      <answer>
+        <p>
+          The key tasks are: clean, compile, compile-tests, test, javadoc, package.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-antrun-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-antrun-plugin/fml/faq.fml
new file mode 100644
index 0000000..e44c01e
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-antrun-plugin/fml/faq.fml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="Why use antrun and not the Ant program itself?">
+     <question>Why use antrun and not the Ant program itself?</question>
+     <answer>
+       <p>Maven has certain benefits over Ant. And for your Ant projects to take
+       advantage of these, you can use Maven as your project management tool and
+       use its maven-antrun-plugin to build your Ant projects. </p>
+       <p>Furthermore, if you wish to migrate from Ant to Maven, you can use
+       this plugin first, then gradually convert your Ant expressions into their
+       corresponding Maven expressions.</p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
+
diff --git a/doxia-test-docs/src/main/resources/maven-assembly-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-assembly-plugin/fml/faq.fml
new file mode 100644
index 0000000..eeba92a
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-assembly-plugin/fml/faq.fml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="deploy">
+      <question>If the Assembly Plugin is run during the package phase, do my assemblies get deployed during the deploy phase?</question>
+      <answer>
+        <p>Yes. The assemblies created by the Assembly Plugin is attached to your project so it gets deployed too.</p>
+      </answer>
+    </faq>
+    <faq id="classifier">
+      <question>Can I use an artifact created by the assembly plugin as a dependency?</question>
+      <answer>
+        <p>Yes. You can refer to it using the id of the assembly as the dependency classifier.</p>
+      </answer>
+    </faq>
+    <faq id="javadoc">
+      <question>How do I use the Assembly Plugin to package my project's javadoc files?</question>
+      <answer>
+        <p>The Javadoc Plugin can generate the javadoc files of your projects. Also, the Javadoc Plugin can package them!</p>
+        <p>Please see the <a href="http://maven.apache.org/plugins/maven-javadoc-plugin/">Javadoc Plugin Documentation</a>.</p>
+      </answer>
+    </faq>
+  </part>
+  <part id="Artifact and Directory Formatting">
+    <faq id="dashClassifier">
+      <question>
+        I have a dependencySet that includes some artifacts with classifiers, and others without classifiers. 
+        How can I setup the file mappings to handle both cases appropriately?
+      </question>
+      <answer>
+        <p>
+          The best way to handle a mixed bag of dependencies with and without classifiers is to use the
+          <b>${dashClassifier?}</b> expression, added in version 2.2-beta-2 of the assembly plugin especially
+          for this purpose. This expression will determine whether each artifact has a classifier, and if it
+          does, it will substitute the artifact's classifier - prepended by a dash - in place of the expression.
+        </p>
+        <p>
+          For example, suppose you want to include two artifacts, commons-logging-1.0.4.jar, and 
+          yourserver-1.0-client.jar (where 'client' is the classifier of the second artifact). To do this,
+          simply add the following to your dependencySet:
+        </p>
+        <pre>
+<outputFileNameMapping>${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
+        </pre>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-changelog-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-changelog-plugin/fml/faq.fml
new file mode 100644
index 0000000..d355db5
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-changelog-plugin/fml/faq.fml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="What is the difference between the Changelog plugin and the Changes plugin">
+      <question>What is the difference between the Changelog plugin and the Changes plugin?</question>
+      <answer>
+        <p>
+        The Changelog plugin generates reports based on the changes in the Software Configuration Management or SCM while
+        the Changes plugin generates reports either from a changes.xml file or from the JIRA issue management system.
+        For more information about the changes plugin, see
+        <a href="http://maven.apache.org/plugins/maven-changes-plugin/">http://maven.apache.org/plugins/maven-changes-plugin</a>
+        </p>
+      </answer>
+    </faq>
+    <faq id="What is Software Configuration Management or SCM">
+      <question>What is Software Configuration Management or SCM?</question>
+      <answer>
+        <p>
+        According to Roger Pressman (in his book) Software Engineering: A Practitioner's Approach, SCM is a <em>"set of activities
+        designed to control change by identifying the work products that are likely to change, establishing relationships among
+        them, defining mechanisms for managing different versions of these work products, controlling the changes imposed, and
+        auditing and reporting on the changes made."</em>
+        </p>
+      </answer>
+    </faq>
+    <faq id="Why do the dates look weird in the report when I use the dateFormat parameter">
+      <question>Why do the dates look weird in the report when I use the dateFormat parameter?</question>
+      <answer>
+        <p>
+          The <code>dateFormat</code> parameter is used when parsing the dates
+          from the log entries retrieved from your SCM system. It can
+          <strong>not</strong> be used to format the dates in the report. If
+          you try to do this the parsed dates will be wrong and the dates in
+          the report even more so. They can look like this
+          <code>0020-05-27</code> for a file that was changed on 14 december
+          2005.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How can I debug the SCM command">
+      <question>How can I debug the SCM command?</question>
+      <answer>
+        <p>
+          When you generate the report, you will see output like this on the
+          command line:
+<source>
+[INFO] [changelog:changelog]
+[INFO] Generating changed sets xml to: .../target/changelog.xml
+[INFO] Executing: svn --non-interactive log -v -r "{2007-06-13 22:22:09 +0000}:{2007-07-14 22:22:09 +0000}" http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin
+</source>
+          Copy the SCM command (everything after <code>Executing:</code>) and
+          try to run it by itself on the command line. If it doesn't work on
+          the command line there's probably something wrong with the
+          <code><scm></code> element in your <code>pom.xml</code>.
+        </p>
+      </answer>
+    </faq>
+    <faq id="My changelog report is blank, but it shouldn't be">
+      <question>My changelog report is blank, but it shouldn't be</question>
+      <answer>
+        <p>
+          The first thing to check is what data the changelog plugin managed to
+          pull out of your SCM system. In the file
+          <code>target/changelog.xml</code> you will find that data. Have a
+          look and see if the data in the file seems correct. If it is not
+          correct, then you are not getting the data you expected from your SCM
+          system. Please check your changelog plugin configuration.
+        </p>
+        <p>
+          If that doesn't help, you can try to run the maven-scm-plugin from
+          the command line. The maven-changelog-plugin uses Maven SCM under the
+          hood and running the maven-scm-plugin is a great way of verifying
+          that the <code><scm></code> element in your
+          <code>pom.xml</code> file is correct. Try this on the command line:
+<source>
+mvn scm:changelog
+</source>
+          It should show you the latest changes made in your SCM. It uses the
+          default settings, but you can change these by specifying parameters
+          on the command line. Read more about this in
+          <a href="http://maven.apache.org/scm/plugins/examples/scm-advance-features.html">the scm-plugin documentation</a>.
+        </p>
+      </answer>
+    </faq>
+    <faq id="Where can I find a working configuration for this plugin">
+      <question>Where can I find a working configuration for this plugin?</question>
+      <answer>
+        <p>
+          The plugin itself is configured to generate a changelog report. This
+          is done using the bare minimum of configuration. Have a look at the
+          <code><scm></code> and
+          <code><reporting>/<plugins></code> elements in the
+          <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-changelog-plugin/pom.xml">pom.xml</a>
+          of this plugin. The generated report is found
+          <a href="changelog.html">here</a>.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How do I use this plugin with Perforce?">
+      <question>How do I use this plugin with Perforce?</question>
+      <answer>
+        <p>
+          You have to specify your <code>clientspec</code> in the
+          <code><systemProperties></code> element of this plugin's
+          <configuration> element.
+<source>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-changelog-plugin</artifactId>
+        <configuration>
+          <systemProperties>
+            <property>
+              <name>maven.scm.perforce.clientspec.name</name>
+              <value>your-client-spec-name</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+</source>
+        </p>
+      </answer>
+    </faq>
+    <faq id="The Developer Activity report is blank, but the other reports are fine">
+      <question>The Developer Activity report is blank, but the other reports are fine</question>
+      <answer>
+        <p>
+          You need to add
+          <a href="http://maven.apache.org/ref/current/maven-model/maven.html#class_developer"><code><developer></code></a>
+          elements in your <code>pom.xml</code> file. Their id:s need to match
+          the userid:s that are used in your SCM system. Only checkins made by
+          a developer found in the pom will be added to the Developer Activity
+          report.
+        </p>
+      </answer>
+    </faq>
+    <faq id="I think I've found a bug in this plugin, what do I do">
+      <question>I think I've found a bug in this plugin, what do I do?</question>
+      <answer>
+        <p>
+          Please follow these steps in the order they come.
+        </p>
+        <ol>
+          <li>
+            Read all the FAQs on this page.
+          </li>
+          <li>
+            Ask a question on the user list. Please supply the necessary
+            information so that the people on the list can help you. This
+            includes the <code><scm></code> element from your
+            <code>pom.xml</code>, your plugin configuration plus a rich
+            description of what happens and what you expected to happen.
+          </li>
+          <li>
+            Create an issue in <a href="issue-tracking.html">JIRA</a>.
+          </li>
+        </ol>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-changes-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-changes-plugin/fml/faq.fml
new file mode 100644
index 0000000..205d0fd
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-changes-plugin/fml/faq.fml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="question1">
+     <question>What's the difference between the changes-maven-plugin at Mojo and this one?</question>
+     <answer>
+       <p>
+         This plugin started out at <a href="http://mojo.codehaus.org/">the Mojo project</a>.
+         In March of 2006 the plugin was moved to Apache and the Maven sandbox.
+         So it's the same plugin, but this one is newer.
+       </p>
+     </answer>
+   </faq>
+
+   <faq id="question2">
+     <question>I get a <code>org.xml.sax.SAXParseException</code>, when generating the JIRA Report. What can do about it?</question>
+     <answer>
+       <p>
+         If you have a lot of entities in the xml file returned from your JIRA
+         installation, you might get an error like this one, when you run the
+         JIRA Report:
+
+         <source>
+org.xml.sax.SAXParseException: Parser has reached the entity expansion limit "64,000" set by the Application.
+         </source>
+
+         If that happens you need to tell the xml parser to use a higher limit.
+         This can be accomplished by adding a command line parameter. In the
+         following example we have set it to double the original value:
+
+         <source>
+mvn -DentityExpansionLimit=128000 ...
+         </source>
+       </p>
+       <p>
+         Unfortunately we have not been able to set this in Java. If someone
+         knows how to do this, then please reopen
+         <a href="http://jira.codehaus.org/browse/MCHANGES-75">MCHANGES-75</a>,
+         and tell us how. It would be nicer if this could be set using a
+         parameter in the POM.
+       </p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-checkstyle-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-checkstyle-plugin/fml/faq.fml
new file mode 100644
index 0000000..1f057b4
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-checkstyle-plugin/fml/faq.fml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="How are the Checkstyle properties set">
+      <question>How are the Checkstyle properties set?</question>
+      <answer>
+        <p>
+          You can set the Checkstyle properties to be used in the plugin configuration of your pom through the
+          <strong>propertiesLocation</strong> parameter. The properties file will be resolved by the plugin based
+          on its value.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How do I set a custom ruleset">
+      <question>How do I set a custom ruleset?</question>
+      <answer>
+        <p>
+          You can set a custom ruleset through the <strong>configLocation</strong> plugin parameter. If no value is
+          specified, the plugin will use a default ruleset, which is the <code>sun_checks.xml</code>, that is bundled with the
+          plugin.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How do I include the test directory in Checkstyle">
+      <question>How do I include the test directory in Checkstyle?</question>
+      <answer>
+        <p>
+          You can include the test directory in the Checkstyle report by setting the
+          <strong>includeTestSourceDirectory</strong> plugin parameter to <strong>true</strong>.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-clean-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-clean-plugin/fml/faq.fml
new file mode 100644
index 0000000..ebc829a
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-clean-plugin/fml/faq.fml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="I already ran mvn clean but the directory (put dir name here) is still there. What should I do?">
+      <question>I already ran <i>mvn clean</i> but the directory (<i>put dir name here</i>) is still there. What should I do?</question>
+      <answer>
+        <p>
+          Some files-generating plugins can generate their files outside of the
+          default directories being deleted by the clean plugin. You should add
+          the location of such files in the clean plugin configuration or change
+          the configuration of those plugins to put their files inside the
+          <i>project.build.directory</i> which is by default, the <i>target</i>
+          directory.
+        </p>
+      </answer>
+    </faq>
+    <faq id="On Windows, I got Unable to delete directory. What s wrong?">
+      <question>On Windows, I got <i>"Unable to delete directory"</i>. What's wrong?</question>
+      <answer>
+        <p>
+          For instance, <i>clean</i> could fail if you already have opened a command
+          line with target as the current dir. Windows locks some ressources and you need
+          to close the handles on these ressources.
+          To skip these errors, you could call <i>clean</i> with the command line parameter <i>-Dmaven.clean.failOnError=false</i>.
+          For more information, refer to <a href="./examples/ignoring-errors.html">Ignoring Errors</a> page.
+        </p>
+        <p>
+          <a href="http://www.microsoft.com/technet/sysinternals/default.mspx">Sysinternals</a> produced
+          a number of utilities, like <a href="http://www.microsoft.com/technet/sysinternals/Utilities/ProcessExplorer.mspx">Process Explorer</a>
+          or <a href="http://www.microsoft.com/technet/sysinternals/utilities/handle.mspx">Handle</a>
+          that help you to deal with Windows handles.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-compiler-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-compiler-plugin/fml/faq.fml
new file mode 100644
index 0000000..7b34a14
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-compiler-plugin/fml/faq.fml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="compile-error-reports">
+      <question>Is there a way to get Maven to report the number of compile errors found?</question>
+      <answer>
+        <p>
+          Currently, this type of summary information is not built into the compiler plugin, but it
+          would be possible to add. If this feature is important to you, add your vote to
+          <a href="http://jira.codehaus.org/browse/MNG-1854">MNG-1854</a>
+          .
+        </p>
+      </answer>
+    </faq>
+    <faq id="how-to-add-generated-sources">
+      <question>How do I add my generated sources to the compile path of Maven, when using modello?</question>
+      <answer>
+        <p>Modello generate the sources in the generate-sources phase and automatically adds the
+          source directory for compilation in maven. So you don't have to copy the generated sources.
+        </p>
+        <p>You have to declare the modello-plugin in the build of your plugin for source generation
+          (in that way the sources are generated each time).
+        </p>
+        <p>The Modello website can be found
+          <a href="http://modello.codehaus.org/">here</a>
+          .
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-dependency-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-dependency-plugin/fml/faq.fml
new file mode 100644
index 0000000..d4b06f6
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-dependency-plugin/fml/faq.fml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="plugin name">
+     <question>What is the difference between dependency-maven-plugin and maven-dependency-plugin?</question>
+     <answer>
+        <p>
+            Actually, they are the same, it's just that it was moved and renamed. The dependency-maven-plugin
+            is hosted at Mojo while maven-dependency-plugin is hosted at Apache. The recommended
+            plugin to use is the maven-dependency-plugin. 
+        </p>
+     </answer>
+      </faq>
+      <faq id="cli">
+     <question>When executing mvn dependency:unpack or dependency:copy from the command line, I get "One or more required plugin parameters are invalid/missing for 'dependency:unpack'"</question>
+	 <answer>
+	 	<p>
+	 		In order for this to work, you must configure the ArtifactItems as shown <a href="examples/copying-artifacts.html#Copying From the Command Line">here</a>. Note that when executing a plugin from 
+	 		the command line, you must put the configuration tag outside of the executions.
+	 		
+	 		If you haven't done this correctly, the error will look like this:
+	 		<pre>
+[0] inside the definition for plugin: 'maven-dependency-plugin'specify the following:
+	 		</pre>
+	 	</p>
+	 </answer>	
+  </faq>
+  <faq id="missing">
+  <question>Why am I getting errors that a documented goal or parameter is missing?</question>
+  <answer><p>The latest documents are published and may preceed the actual release. Check to make sure the goal/parameter is in the most recent version.
+  	<b> -OR- </b>
+  	Maven may be resolving the older codehaus version of the dependency plugin. See next question.</p>
+  </answer>
+  </faq>
+    <faq id="question">
+  <question>Why is Maven resolving "dependency:xxx" to the older org.codehaus.mojo:dependency-maven-plugin?</question>
+  <answer><p>
+  	Due to a bug in Maven in versions prior to 2.0.7 (<a href="http://jira.codehaus.org/browse/MNG-2926">MNG-2926</a>) the search order was reversed and caused Mojo plugins to supercede ones with the same prefix at Apache.
+  	The metadata at mojo was cleaned up when the maven-dependency-plugin was released at Apache. If you are still experiencing this error, chances are you have
+  	old metadata in your local repository or in a proxy / internal repository. Removing /org/codehaus/mojo/maven-metadata.* from your repo/proxy will cause it to 
+  	be refreshed. Alternatively, you can specify the groupId explicitely in your pom (if you are using a bound goal), or on the command line, use groupId:artifactId:version:mojo, ie mvn
+org.apache.maven.plugins:maven-dependency-plugin:2.0-alpha-4:unpack
+  	</p>
+  </answer>
+  </faq>
+      <faq id="includes">
+  <question>Why am I having trouble unpacking only a specific file?</question>
+  <answer><p>
+  	The excludes will override the includes declaration. That means if you specify excludes=**/* ,includes=**/foo, you will exclude everything. If you only want foo, then just specify the includes. The plexus component used to unpack uses the following code to determing which files to unpack: return isIncluded( name ) AND !isExcluded( name );
+  	</p>
+  </answer>
+  </faq>
+ </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-deploy-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-deploy-plugin/fml/faq.fml
new file mode 100644
index 0000000..dd5facb
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-deploy-plugin/fml/faq.fml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="question">
+     <question>I get an Unsupported Protocol Error when deploying a 3rd party jar. What should I do?</question>
+     <answer>
+       <p>
+         If you are using the <code>deploy:deploy-file</code> goal and encounter
+         this error:
+       </p>
+       <p>
+         <i>"Error deploying artifact: Unsupported Protocol: 'ftp': Cannot find
+         wagon which supports the requested protocol: ftp"</i>
+       </p>
+       <p>
+         Then you need to place the appropriate wagon provider in your
+         <code>%M2_HOME%/lib</code>. In this case the provider needed is ftp,
+         so we have to place the wagon-ftp jar in the lib directory of your
+         Maven 2 installation.
+       </p>
+       <p>
+         If the error description is something like this:
+       </p>
+       <p>
+         <i>"Error deploying artifact: Unsupported Protocol: 'ftp': Cannot find
+         wagon which supports the requested protocol: ftp
+         org/apache/commons/net/ftp/FTP"</i>
+       </p>
+       <p>
+         Then you need to place the commons-net jar in
+         <code>%M2_HOME%/lib</code>.
+       </p>
+     </answer>
+   </faq>
+     <faq id="skip">
+       <question>I don't want to deploy one of the artifacts in my multi-module build.  Can I skip deployment?</question>
+       <answer>
+         <p>
+           Yes, you can skip deployment of individual modules by configuring the deploy plugin as follows:
+         </p>
+         <p>
+           <source>
+             <plugin>
+               <artifactId>maven-deploy-plugin</artifactId>
+               <version>X.Y</version>
+               <configuration>
+                 <skip>true</skip>
+               </configuration>
+             </plugin>
+           </source>
+         </p>
+       </answer>
+     </faq>
+ </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-doap-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-doap-plugin/fml/faq.fml
new file mode 100644
index 0000000..0071554
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-doap-plugin/fml/faq.fml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="What is DOAP">
+      <question>What is DOAP?</question>
+      <answer>
+        <p>DOAP stands for "Description of a Project" and you can find out everything there is to
+          know about DOAP in its <a href="http://usefulinc.com/doap">homepage</a> and in this <a
+            href="http://www.ibm.com/developerworks/xml/library/x-osproj3/">article</a>.</p>
+      </answer>
+    </faq>
+    <faq id="What are the benefits of DOAP">
+      <question>What are the benefits of DOAP?</question>
+      <answer>
+        <p>The <a href="http://en.wikipedia.org/wiki/Semantic_web">Semantic Web</a> provides
+          mechanisms to process data provided in a form that is easily processed by machines. Thus,
+          a DOAP is a machine readable document which facilitates projects research: it becomes much
+          easier to seek information in the mass of data of the Web, since the data have a given
+          foreseeable format.</p>
+      </answer>
+    </faq>
+    <faq id="Why would I use DOAP when I have a POM">
+      <question>Why would I use DOAP when I have a POM?</question>
+      <answer>
+        <p>That's a very good question! The answer is that generating a DOAP file should take no
+          effort if you are using Maven DOAP Plugin and it helps disseminate project information
+          which can only be a good thing. Cataloging tools like <a href="http://swik.net/">SWiK</a>
+          or like <a href="http://doapstore.org/">DoapStore</a> can benefit from you generating DOAP
+          files and that can also only be a good thing. Even so, it is still important to spread as
+          much information about projects around as possible so there is no downside to creating
+          DOAP files.</p>
+      </answer>
+    </faq>
+    <faq id="What to do with the generated DOAP file">
+      <question>What to do with the generated DOAP file?</question>
+      <answer>
+        <p>Maven DOAP plugin has generated a DOAP file, what's next? See <a
+            href="./examples/doap-in-use.html">DOAP in Use</a> part.</p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-docck-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-docck-plugin/fml/faq.fml
new file mode 100644
index 0000000..b621002
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-docck-plugin/fml/faq.fml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="question1">
+     <question>Where did the standard came from?</question>
+     <answer>
+       <p>
+         The plugin documentation standard was created to address the frequent
+         complaint of lack of documentation, specifically for the Maven plugins.
+         The standard was based on suggestions made on the Maven dev mailing
+         list with some refinements. It is a community consensus of what basic
+         documentation a Maven plugin should have.
+       </p>
+     </answer>
+   </faq>
+   <faq id="question2">
+     <question>Why do we need a documentation standard?</question>
+     <answer>
+       <p>
+         The standard is not a set of rules but a guide to help plugin
+         developers document their plugins better, for the benefit of the users
+         of the plugin. The standard also reminds the plugin developers of the
+         important details that needs to be documented, to help speed up the
+         adoption of the plugin.
+       </p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-ear-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-ear-plugin/fml/faq.fml
new file mode 100644
index 0000000..51b2e1a
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-ear-plugin/fml/faq.fml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="what-is-ear">
+     <question>What is an EAR archive?</question>
+     <answer>
+       <p>
+         An EAR archive is used to deploy standalone EJBs, usually separated from the web application. Thus, there is no
+         need for a web application to access these EJBs. The EJBs are still accessible though using EJB clients.
+       </p>
+     </answer>
+   </faq>
+   <faq id="har-files">
+     <question>
+       The EAR Plugin throws an exception when encountering artifact types it is unfamiliar with. Is this a bug?
+     </question>
+     <answer>
+       <p>
+         The exception can be prevented by adding your custom artifact type to the artifactTypeMappings configuration.
+         There is a mini-guide on how to do that in the <a href="modules.html#Custom Artifact Types">modules
+         configuration</a> section.
+       </p>
+     </answer>
+   </faq>   
+ </part>
+</faqs>
+
diff --git a/doxia-test-docs/src/main/resources/maven-eclipse-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-eclipse-plugin/fml/faq.fml
new file mode 100644
index 0000000..eaefd1a
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-eclipse-plugin/fml/faq.fml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="eclipse-plugin">
+     <question>Is this the Eclipse Plugin that allows me to run maven commands from within Eclipse IDE?</question>
+     <answer>
+       <p>No. This is a maven plugin to generate Eclipse files for a maven project. This is not an Eclipse Plugin for Maven.</p>
+     </answer>
+   </faq>
+   <faq id="import-project">
+     <question>I already did <i>mvn eclipse:eclipse</i> but when I open Eclipse, my project is not there. Where is it?</question>
+     <answer>
+       <p>You may also need to import the project created by Maven Eclipse Plugin. More information can be found <a href="usage.html#Simple Project">here</a>.</p>
+     </answer>
+   </faq>
+   <faq id="use-add-maven-repo">
+     <question>After running <i>mvn eclipse:eclipse</i>, the generated dependencies are pointing to a non-existing file. What needs to be done?</question>
+     <answer>
+       <p>Before you can setup any maven project to your Eclipse IDE, you need to define first the location of your local repository to Eclipse. This is done by using: <a href="usage.html#Maven%20repository">eclipse:add-maven-repo</a></p>
+     </answer>
+   </faq>
+   <faq id="import-javadoc">
+     <question>Can I make the Eclipse plugin download and attach javadocs to my libraries?</question>
+     <answer>
+      <p>As of version 2.4, there is a new flag, downloadJavadocs that behaves exactly like downloadSources except for javadocs. See more information here: <a href="examples/attach-library-sources.html">Attach Library Sources</a>. </p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-ejb-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-ejb-plugin/fml/faq.fml
new file mode 100644
index 0000000..157fdd7
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-ejb-plugin/fml/faq.fml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="How can I specify a Class-Path: entry in the manifest of an EJB jar?">
+      <question>How can I specify a Class-Path: entry in the manifest of an EJB jar?</question>
+      <answer>
+        <p>
+          You just have to configure it:
+          <source>
+<project>
+  ...
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-ejb-plugin</artifactId>
+        ...
+        <configuration>
+          <archive>
+            <manifest>
+              <addClasspath>true</addClasspath>
+            </manifest>
+          </archive>
+        </configuration>
+        ...
+      </plugin>
+    </plugins>
+  </build>
+  ...
+</project>
+          </source>
+        </p>
+      </answer>
+    </faq>
+   <faq id="classifieruse">
+     <question>How does the classifier affect artifacts in my EJB project?</question>
+     <answer>
+       <p>When used, the copy of the artifact in your project will have the classifier appended to its
+       filename. This can be used to differentiate duplicate artifacts.</p>
+     </answer>
+   </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-gpg-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-gpg-plugin/fml/faq.fml
new file mode 100644
index 0000000..d179b18
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-gpg-plugin/fml/faq.fml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="question1">
+     <question>What is GnuPG?</question>
+     <answer>
+       <p>
+         You can read more about GnuPG at <a href="http://www.gnupg.org/">their web site</a>.
+       </p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-help-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-help-plugin/fml/faq.fml
new file mode 100644
index 0000000..67435ff
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-help-plugin/fml/faq.fml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="What is a Mojo">
+      <question>What is a Mojo</question>
+      <answer>
+        <p>
+          See the Maven FAQ: <a href="http://maven.apache.org/general.html#What_is_a_Mojo">What is a Mojo</a>.
+        </p>
+      </answer>
+    </faq>
+    <faq id="Why mvn help:active-profiles won't show the active profiles under Maven 2.1">
+      <question>Why <code>mvn help:active-profiles</code> won't show the active profiles under Maven 2.1?</question>
+      <answer>
+        <p>
+          See <a href="http://jira.codehaus.org/browse/MPH-38">MPH-38</a> for more info.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-idea-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-idea-plugin/fml/faq.fml
new file mode 100644
index 0000000..ea86619
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-idea-plugin/fml/faq.fml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="idea-plugin">
+     <question>Is this the IDEA Plugin that allows me to run Maven commands from within IntelliJ IDEA?</question>
+     <answer>
+       <p>No. This is a Maven plugin to generate IntelliJ IDEA files for a Maven project. This is not an IDEA Plugin for Maven.</p>
+     </answer>
+   </faq>
+   <faq id="multi-project-handling">
+     <question>How does the IDEA plugin work on multi-projects?</question>
+     <answer>
+       <p>
+         Maven IDEA Plugin, by default, creates the project file (*.ipr) and
+         the workspace file (*.iws) from where the root pom is located. Then
+         module files (*.iml) are created for each of the projects inside the
+         reactor.
+       </p>
+       <p>
+         If some projects depend on another project in the reactor, then the
+         reference to the project is given instead of the hard-references to
+         the local repository copies of the artifacts.
+       </p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-install-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-install-plugin/fml/faq.fml
new file mode 100644
index 0000000..fe48bff
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-install-plugin/fml/faq.fml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="question">
+     <question>Can I use the <i>install:install-file</i> goal to install artifacts to my remote repository?</question>
+     <answer>
+       <p>
+            No. You may want to use
+            <a href="http://maven.apache.org/plugins/maven-deploy-plugin/"><i>deploy:deploy-file</i></a> instead.
+       </p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-invoker-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-invoker-plugin/fml/faq.fml
new file mode 100644
index 0000000..5424316
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-invoker-plugin/fml/faq.fml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="question1">
+     <question>Why do I need to use this plugin?</question>
+     <answer>
+       <p>
+         It is essential that you provide some form of integration testing for your projects and the Invoker Plugin
+         tries to make is easy for you to create integration tests for your Maven Plugins, new Lifecycles, or any other
+         type of Maven component that you've created. Currently the Invoker Plugin forks Maven to execute the specified
+         projects, but it is hoped that soon we will integrate the Maven Embedder into the mix to allow you run your
+         projects in process for great speed.
+       </p>
+     </answer>
+   </faq>
+   <faq id="question2">
+     <question>How can I assert that the build of an IT project fails?</question>
+     <answer>
+       <p>
+         Sometimes you might want to test that error conditions are properly dealt with, i.e. fail a build. To assert
+         a failure for a particular IT project, put the following setting into the properties file denoted by the
+         plugin's <a href="run-mojo.html#invokerPropertiesFile"><code>invokerPropertiesFile</code></a> parameter:
+         <pre>invoker.buildResult=failure</pre>
+         Now, the failure of the IT build will be interpreted as a test success. Likewise, a successful IT build will
+         be considered a test failure.
+       </p>
+     </answer>
+   </faq>
+   <faq id="question3">
+     <question>How can I share common code between the pre-/post-build scripts?</question>
+     <answer>
+       <p>
+         If you want to avoid copy&paste of lengthy code snippets within the hook scripts, you can move this code
+         into a regular Java class. More precisely, you would place this utility code somewhere in your test source
+         tree and set the plugin parameter <a href="run-mojo.html#addTestClassPath"><code>addTestClassPath</code></a>
+         to <code>true</code>. For more details, please see the example
+         <a href="examples/access-test-classes.html">Accessing Test Classes</a>.
+       </p>
+     </answer>
+   </faq>
+   <faq id="question4">
+     <question>How can I invoke multiple Maven builds on the same IT project?</question>
+     <answer>
+       <p>
+         This is not supported in the plugin configuration but rather on a per project basis by means of the invoker
+         properties. This way, you use properties like <code>invoker.goals.2</code> to configure the goals for a second
+         invocation of Maven. Have a look at the example about using
+         <a href="examples/invoker-properties.html">Invoker Properties</a>.
+       </p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-jar-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-jar-plugin/fml/faq.fml
new file mode 100644
index 0000000..dfeef2b
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-jar-plugin/fml/faq.fml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="question1">
+      <question>Why is there no documentation for the archive parameter?</question>
+      <answer>
+        <p>
+          The archive parameter is a class that resides outside of this plugin,
+          in the Maven Archiver component. You can find the documentation for
+          Maven Archiver
+          <a href="http://maven.apache.org/shared/maven-archiver/index.html">here</a>.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-javadoc-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-javadoc-plugin/fml/faq.fml
new file mode 100644
index 0000000..12218a9
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-javadoc-plugin/fml/faq.fml
@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="What are the Javadoc options supported by the Maven Javadoc Plugin">
+      <question>What are the Javadoc options supported by the Maven Javadoc Plugin?</question>
+      <answer>
+        <p>
+          All options provided by Sun on the Javadoc homepages are wrapped in the Maven Javadoc Plugin. This
+          plugin supports Javadoc 1.4, 1.5 and 6.0 options. Refer you to the
+          <a href="./apidocs/org/apache/maven/plugin/javadoc/package-summary.html">Javadoc Package Summary</a>
+          for more information.
+        </p>
+      </answer>
+    </faq>
+    <faq id="Where in the pom.xml do I configure the Javadoc Plugin">
+      <question>Where in the pom.xml do I configure the Javadoc Plugin?</question>
+      <answer>
+        <p>
+          Like all other site report plugins, the Javadoc Plugin goes in the <i><reporting/></i> section
+          of your pom.xml. In this case, you will need to call <code>mvn site</code> to run reports.
+        </p>
+        <p>
+          You could also configure it in the <plugins/> or <pluginsManagement/> in <build/> tag
+          of your pom.xml. In this case, you will need to call <code>mvn javadoc:javadoc</code> to run the main
+          report.
+        </p>
+      </answer>
+    </faq>
+    <faq id="Where do I put javadoc resources like HTML files or images">
+      <question>Where do I put Javadoc resources like HTML files or images?</question>
+      <answer>
+        <p>
+          All javadoc resources like HTML files, images could be put in the
+          <i>${basedir}/src/main/javadoc</i> directory.
+        </p>
+        <p>
+          See <a href="examples/javadoc-resources.html">Using Javadoc Resources</a> for more information.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to know exactly the Javadoc command line">
+      <question>How to know exactly the Javadoc command line?</question>
+      <answer>
+        <p>
+          The Javadoc Plugin calls the Javadoc tool with
+          <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/javadoc.html#argumentfiles">argument files</a>,
+          i.e. files called 'options', 'packages' and 'argfile' (or 'files' with Jdk < 1.4):
+          <source>
+${project.reporting.outputDirectory}/apidocs/javadoc.exe(or .sh) \
+    @options \
+    @packages | @argfile</source>
+        </p>
+        <p>
+          These argument files are generated at runtime depending the Javadoc Plugin configuration and are deleted
+          when the Javadoc Plugin ended.
+        </p>
+        <p>
+          To preserve them, just add <debug>true</debug> in your Javadoc Plugin configuration or just call
+          <code>mvn javadoc:javadoc -Ddebug=true</code> or <code>mvn javadoc:javadoc -X</code>.
+          In this case, an additional script file (javadoc.bat (or .sh) will be created in the <code>apidocs</code>
+          directory.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to add additional Javadoc parameters">
+      <question>How to add additional Javadoc parameters?</question>
+      <answer>
+        <p>
+          You could need to add more Javadoc parameters to be process by the Javadoc Tool (i.e. for doclet).
+        </p>
+        <p>
+          For this, you should use the <i><additionalparam/></i> parameter in your Javadoc Plugin configuration.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to add additional Javadoc options">
+      <question>How to add additional Javadoc options?</question>
+      <answer>
+        <p>
+          You could need to add more J options (i.e. runtime system java options that runs Javadoc tool like -J-Xss)
+          to be process by the Javadoc Tool. For this, you should use the <i><additionalJOption/></i> parameter
+          in your Javadoc Plugin configuration.
+        </p>
+        <p>
+          The Javadoc Plugin calls the Javadoc tool with J options, i.e.:
+          <source>
+${project.reporting.outputDirectory}/apidocs/javadoc.exe(or .sh) \
+    -J-Xss128m \
+    @options \
+    @packages | @argfile</source>
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to increase Javadoc heap size">
+      <question>How to increase Javadoc heap size?</question>
+      <answer>
+        <p>
+          If you need to increase the Javadoc heap size, you should use the <i><minmemory/></i> and
+          <i><maxmemory/></i> parameters in your Javadoc Plugin configuration. For instance:
+          <source>
+<project>
+  ...
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          ...
+          <minmemory>128m</minmemory>
+          <maxmemory>1g</maxmemory>
+          ...
+        </configuration>
+      </plugin>
+    </plugins>
+    ...
+  </reporting>
+  ...
+</project></source>
+        </p>
+        <p>
+          <b>Note:</b> The memory unit depends on the JVM used. The units supported could be: <code>k</code>,
+          <code>kb</code>, <code>m</code>, <code>mb</code>, <code>g</code>, <code>gb</code>, <code>t</code>,
+          <code>tb</code>. If no unit specified, the default unit is <code>m</code>.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to add proxy support">
+      <question>How to add proxy support?</question>
+      <answer>
+        <p>
+          To specify a proxy in the Javadoc tool, you need to configure an active proxy in your <i>settings.xml</i>. Read the
+          <a href="http://maven.apache.org/guides/mini/guide-proxies.html">Configuring a proxy</a> guide for more information.
+        </p>
+        <p>
+          The Javadoc Plugin calls the Javadoc tool with networking J options, i.e.:
+          <source>
+${project.reporting.outputDirectory}/apidocs/javadoc.exe(or .sh) \
+    -J-Dhttp.proxySet=true \
+    -J-Dhttp.proxyHost=http://localhost \
+    -J-Dhttp.proxyPort=80 \
+    -J-Dhttp.nonProxyHosts="www.google.com|*.somewhere.com" \
+    -J-Dhttp.proxyUser="toto" \
+    -J-Dhttp.proxyPassword="toto" \
+    @options \
+    @packages | @argfile</source>
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to have less output">
+      <question>How to have less output?</question>
+      <answer>
+        <p>
+          Just set the <i><quiet/></i> parameter to <i>true</i> in your Javadoc Plugin configuration.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to remove test Javadocs report">
+      <question>How to remove test Javadocs report?</question>
+      <answer>
+        <p>
+          You need to configure the <i><reportSets/></i> parameter. Read the
+          <a href="examples/selective-javadocs-report.html">Selective Javadocs Reports</a> part for more information.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to deploy Javadoc jar file">
+      <question>How to deploy Javadoc jar file?</question>
+      <answer>
+        <p>
+          Basically, you need to call <i>mvn clean javadoc:jar deploy</i>. If you want to include the javadoc jar
+          in a release process, you need to attach it in the release profile, for instance:
+          <source>
+<project>
+  ...
+  <profiles>
+    <profile>
+      <id>release</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>attach-javadocs</id>
+                <goals>
+                  <goal>jar</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    ...
+  </profiles>
+  ...
+</project></source>
+        </p>
+        <p>
+          To deploy the Javadoc jar on a given Maven repository, you could call:
+          <source>
+mvn deploy:deploy-file \
+    -DgroupId=<group-id> \
+    -DartifactId=<artifact-id> \
+    -Dversion=<version> \
+    -Dfile=<path-to-file> \
+    -Dpackaging=jar \
+    -DrepositoryId=<repository-id> \
+    -Durl=dav:http://www.myrepository.com/m2 \
+    -Dclassifier=javadoc</source>
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to include additional source code directories in aggregate mode">
+      <question>How to include additional source code directories in aggregate mode?</question>
+      <answer>
+        <p>
+          If you use the Javadoc report in the aggregate mode, i.e. using the <code>aggregate</code> parameter, and if the
+          Javadoc report does not include additional source code directories defined using the
+          <a href="http://mojo.codehaus.org/build-helper-maven-plugin/add-source-mojo.html">build-helper:add-source</a> goal,
+          you need to use the <code>javadoc:aggregate</code> goal instead of <code>javadoc:javadoc</code> goal. Read the
+          <a href="examples/aggregate.html">Aggregating Javadocs for Multi-Projects</a> part for more information.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to use links option in Standard Doclet">
+      <question>How to use links option in Standard Doclet?</question>
+      <answer>
+        <p>
+          You need to configure the <i><links/></i> parameter. Also, you need to correctly write references in
+          your Javadoc, i.e.:
+          <ul>
+            <li><code>@see MyMojo</code> or <code>{@link MyMojo}</code> will <b>NOT work</b>.</li>
+            <li><code>@see com.mycompany.plugin.myplugin.MyMojo</code> or <code>{@link com.mycompany.myplugin.MyMojo}</code> will <b>work</b>.</li>
+          </ul>
+        </p>
+      </answer>
+    </faq>
+    <faq id="On Windows with Sun JDK, why javadoc:javadoc goal blows up due to an IllegalArgumentException in sun.net.www.ParseUtil.decode()">
+      <question>On Windows with Sun JDK, why <i>javadoc:javadoc</i> goal blows up due to an IllegalArgumentException in <i>sun.net.www.ParseUtil.decode()</i>?</question>
+      <answer>
+        <p>
+          You are on Windows XP with Sun JDK 5 or JDK 6 freshly installed, and when you run <i>mvn javadoc:javadoc</i>,
+          you are stick by an <i>sun.net.www.ParseUtil.decode()</i> exception like this
+          <a href="javascript:;" onclick="toggleException( '_6219854' );" style="cursor: pointer;vertical-align:text-bottom;">one</a>.
+          <div id="_6219854" style="display:none">
+            <source>
+...
+[INFO] ------------------------------------------------------------------------
+[ERROR] BUILD ERROR
+[INFO] ------------------------------------------------------------------------
+[INFO] An error has occurred in JavaDocs report generation:Exit code: 1 - java.lang.IllegalArgumentException
+  at sun.net.www.ParseUtil.decode(ParseUtil.java:189)
+  at sun.misc.URLClassPath$FileLoader.<init>(URLClassPath.java:953)
+  at sun.misc.URLClassPath$3.run(URLClassPath.java:326)
+  at java.security.AccessController.doPrivileged(Native Method)
+  at sun.misc.URLClassPath.getLoader(URLClassPath.java:320)
+  at sun.misc.URLClassPath.getLoader(URLClassPath.java:297)
+  at sun.misc.URLClassPath.findResource(URLClassPath.java:144)
+  at java.net.URLClassLoader$2.run(URLClassLoader.java:362)
+  at java.security.AccessController.doPrivileged(Native Method)
+  at java.net.URLClassLoader.findResource(URLClassLoader.java:359)
+  at java.lang.ClassLoader.getResource(ClassLoader.java:977)
+  at java.lang.ClassLoader.getResourceAsStream(ClassLoader.java:1159)
+  at javax.xml.parsers.SecuritySupport$4.run(SecuritySupport.java:96)
+  at java.security.AccessController.doPrivileged(Native Method)
+  at javax.xml.parsers.SecuritySupport.getResourceAsStream(SecuritySupport.java:89)
+  at javax.xml.parsers.FactoryFinder.findJarServiceProvider(FactoryFinder.java:250)
+  at javax.xml.parsers.FactoryFinder.find(FactoryFinder.java:223)
+  at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:128)
+  at com.sun.tools.doclets.internal.toolkit.builders.LayoutParser.parseXML(LayoutParser.java:72)
+  at com.sun.tools.doclets.internal.toolkit.builders.ClassBuilder.build(ClassBuilder.java:108)
+  at com.sun.tools.doclets.formats.html.HtmlDoclet.generateClassFiles(HtmlDoclet.java:155)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.generateClassFiles(AbstractDoclet.java:164)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.startGeneration(AbstractDoclet.java:106)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.start(AbstractDoclet.java:64)
+  at com.sun.tools.doclets.formats.html.HtmlDoclet.start(HtmlDoclet.java:42)
+  at com.sun.tools.doclets.standard.Standard.start(Standard.java:23)
+  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+  at java.lang.reflect.Method.invoke(Method.java:597)
+  at com.sun.tools.javadoc.DocletInvoker.invoke(DocletInvoker.java:215)
+  at com.sun.tools.javadoc.DocletInvoker.start(DocletInvoker.java:91)
+  at com.sun.tools.javadoc.Start.parseAndExecute(Start.java:340)
+  at com.sun.tools.javadoc.Start.begin(Start.java:128)
+  at com.sun.tools.javadoc.Main.execute(Main.java:41)
+  at com.sun.tools.javadoc.Main.main(Main.java:31)
+  com.sun.tools.doclets.internal.toolkit.util.DocletAbortException
+  at com.sun.tools.doclets.internal.toolkit.builders.LayoutParser.parseXML(LayoutParser.java:79)
+  at com.sun.tools.doclets.internal.toolkit.builders.ClassBuilder.build(ClassBuilder.java:108)
+  at com.sun.tools.doclets.formats.html.HtmlDoclet.generateClassFiles(HtmlDoclet.java:155)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.generateClassFiles(AbstractDoclet.java:164)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.startGeneration(AbstractDoclet.java:106)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.start(AbstractDoclet.java:64)
+  at com.sun.tools.doclets.formats.html.HtmlDoclet.start(HtmlDoclet.java:42)
+  at com.sun.tools.doclets.standard.Standard.start(Standard.java:23)
+  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+  at java.lang.reflect.Method.invoke(Method.java:597)
+  at com.sun.tools.javadoc.DocletInvoker.invoke(DocletInvoker.java:215)
+  at com.sun.tools.javadoc.DocletInvoker.start(DocletInvoker.java:91)
+  at com.sun.tools.javadoc.Start.parseAndExecute(Start.java:340)
+  at com.sun.tools.javadoc.Start.begin(Start.java:128)
+  at com.sun.tools.javadoc.Main.execute(Main.java:41)
+  at com.sun.tools.javadoc.Main.main(Main.java:31)
+  com.sun.tools.doclets.internal.toolkit.util.DocletAbortException
+  at com.sun.tools.doclets.formats.html.HtmlDoclet.generateClassFiles(HtmlDoclet.java:159)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.generateClassFiles(AbstractDoclet.java:164)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.startGeneration(AbstractDoclet.java:106)
+  at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.start(AbstractDoclet.java:64)
+  at com.sun.tools.doclets.formats.html.HtmlDoclet.start(HtmlDoclet.java:42)
+  at com.sun.tools.doclets.standard.Standard.start(Standard.java:23)
+  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+  at java.lang.reflect.Method.invoke(Method.java:597)
+  at com.sun.tools.javadoc.DocletInvoker.invoke(DocletInvoker.java:215)
+  at com.sun.tools.javadoc.DocletInvoker.start(DocletInvoker.java:91)
+  at com.sun.tools.javadoc.Start.parseAndExecute(Start.java:340)
+  at com.sun.tools.javadoc.Start.begin(Start.java:128)
+  at com.sun.tools.javadoc.Main.execute(Main.java:41)
+  at com.sun.tools.javadoc.Main.main(Main.java:31)
+
+  Command line was:C:\Java\jdk1.6.0_03\jre\..\bin\javadoc.exe @options @packages
+  ...</source>
+            </div>
+        </p>
+        <p>
+          Your <i>CLASSPATH</i> environement variable is probably corrupted, i.e. something like:
+          <source>CLASSPATH=.;C:\Java\jdk1.6.0_03\jre\lib\ext\QTJava.zip;%JAVAHOME%</source>
+          with <code>%JAVAHOME%</code> not a valid environment variable.
+        </p>
+        <p>
+          To resolve it, just reset the <i>CLASSPATH</i> environement variable, i.e.: <source>set CLASSPATH=</source>
+          or set a new <i>CLASSPATH</i> environement variable, i.e.:
+          <source>set CLASSPATH=C:\Java\jdk1.6.0_03\jre\lib\ext\QTJava.zip</source>
+          or fix the wrong environment variable.
+        </p>
+        <p>
+          Refer you to
+          <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6219854">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6219854</a>
+          from Sun for more details.
+        </p>
+
+        <script language="javascript" type="text/javascript">
+          function toggleException( divId )
+          {
+            var div = document.getElementById( divId );
+            if( div.style.display == '' )
+            {
+              div.style.display = 'none';
+            }
+            else
+            {
+              div.style.display = '';
+            }
+          }
+        </script>
+      </answer>
+    </faq>
+    <faq id="What are the values of encoding, docencoding and charset parameters">
+      <question>What are the values of <code>encoding</code>, <code>docencoding</code> and <code>charset</code> parameters?</question>
+      <answer>
+        <p>
+          By default, these parameters have the following values:
+          <dl>
+            <dt><code>encoding</code></dt>
+            <dd>Value of <code>${project.build.sourceEncoding}</code> property or the value of the
+              <code>file.encoding</code> system property if not specified.</dd>
+            <dt><code>docencoding</code></dt>
+            <dd>Value of <code>${project.reporting.outputEncoding}</code> property or <code>UTF-8</code> if not specified.</dd>
+            <dt><code>charset</code></dt>
+            <dd>Value of <code>docencoding</code> parameter if not specified.</dd>
+          </dl>
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-one-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-one-plugin/fml/faq.fml
new file mode 100644
index 0000000..0953e8a
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-one-plugin/fml/faq.fml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="question1">
+      <question>Will this plugin allow me to use Maven 1.x plugins in Maven 2?</question>
+      <answer>
+        <p>
+          No it will not, but it will allow you to package, install and deploy a
+          Maven 1.x plugin using Maven 2.
+        </p>
+      </answer>
+    </faq>
+    <faq id="question2">
+      <question>
+        What's the difference between <code>one:deploy-maven-one-repository</code>
+        and <code>deploy:deploy-file</code> with repositoryLayout set to legacy?
+      </question>
+      <answer>
+        <p>
+          If you'd use <code>deploy:deploy-file</code> then you'd need to
+          configure the file, groupId, artifactId, version for it, whereas with
+          this plugin you don't need to configure anything but the repository,
+          as it gets the rest from the pom. It's more like
+          <code>deploy:deploy</code> than it is <code>deploy:deploy-file</code>.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-patch-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-patch-plugin/fml/faq.fml
new file mode 100644
index 0000000..dbb4404
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-patch-plugin/fml/faq.fml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="What would a patch plugin even be used for">
+      <question>What would a patch plugin even be used for?</question>
+      <answer>
+        <p>
+          In certain environments direct source modification is not allowed so to make alterations to the source you
+          need some patch application process. This plugin seeks to address those situations.
+        </p>
+      </answer>
+    </faq>
+    <faq id="Why doesn't this work on Windows">
+      <question>Why doesn't this work on Windows?</question>
+      <answer>
+        <p>
+          This plugin wraps the <a href="http://www.gnu.org/software/patch/">GNU patch tool</a>, which must be in your
+          <tt>PATH</tt> in order to function properly. A Windows installation usually does not ship with this tool so
+          you will need to install it manually. You might be able to get this plugin working within
+          <a href="http://cygwin.com/">Cygwin</a> or by using the port
+          <a href="http://gnuwin32.sourceforge.net/packages/patch.htm">Patch for Windows</a>.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-pmd-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-pmd-plugin/fml/faq.fml
new file mode 100644
index 0000000..765866a
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-pmd-plugin/fml/faq.fml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="Is there any way to skip the PMD or CPD reports temporarily">
+      <question>
+        The PMD report takes a long time to generate. Is there any way to skip
+        the PMD or CPD reports temporarily?
+      </question>
+      <answer>
+        <p>
+          Yes, each report supports a skip parameter which you can pass on the
+          command line, <code>-Dpmd.skip=true</code> and
+          <code>-Dcpd.skip=true</code> respectively.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-project-info-reports-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-project-info-reports-plugin/fml/faq.fml
new file mode 100644
index 0000000..ca6f990
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-project-info-reports-plugin/fml/faq.fml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="Is it possible to customize the labels of the Project Reports Menu">
+      <question>Is it possible to customize the labels of the Project Reports Menu?</question>
+      <answer>
+        <p>
+        Not yet. See the related issue: <a href="http://jira.codehaus.org/browse/MPIR-35">MPIR-35</a>
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="How to exclude SCM anonymous access info">
+      <question>How to exclude SCM access info?</question>
+      <answer>
+        <p>
+          See the <a href="examples/scm-report.html#How_to_exclude_SCM_anonymous_access_info">SCM report documentation</a>.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="How to hide developer/contributor addresses">
+      <question>How to hide developer/contributor email addresses?</question>
+      <answer>
+        <p>
+          You could use services such as <a href="http://mailhide.recaptcha.net/">reCAPTCHA Mailhide</a>.
+          In this case, just replace email by the given URL, for instance:
+          <source>
+<project>
+  ...
+  <developers>
+    <developer>
+      <id>foo</id>
+      <email>http://mailhide.recaptcha.net/d?k=01ebFB9eM2hZL-T96IpUgRmA==&c=AI-pouLFNTr5xMTBVDaj8iKeIFI5dy9Wj5cxSY0Nuhw=</email>
+        ...
+    </developer>
+    ...
+  </developers>
+  ...
+</project></source>
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-rar-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-rar-plugin/fml/faq.fml
new file mode 100644
index 0000000..b904f1b
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-rar-plugin/fml/faq.fml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="question">
+     <question>Is the rar file generated by the maven-rar-plugin is the same with the rar file(compressed file) of WinRar?</question>
+     <answer>
+       <p>
+            No. They are entirely different from each other.
+       </p>
+       <p>
+            A Resource Adapter Archive (RAR) file is a Java archive (JAR) file used to package a resource adapter for the Java 2 Connector (J2C) Architecture
+            <br/>
+            <br/>
+            A RAR file can contain the following:
+            <br/>
+              <ul>
+                <li>Enterprise information system (EIS) supplied resource adapter implementation code in the form of JAR files or other runnable components, such as dynamic link lists.</li>
+                <li>Utility classes.</li>
+                <li>Static documents, such as HTML files, images, and sound files.</li>
+              </ul>
+       </p>
+       <p>
+            While RAR(Roshal ARchive) is the native format of WinRAR archiver. Like other archives, RAR files
+            are data containers, they store one or several files in the compressed form.
+            After you downloaded RAR file from Internet, you need to unpack its contents in order to use it.
+       </p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-remote-resources-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-remote-resources-plugin/fml/faq.fml
new file mode 100644
index 0000000..e5df0c7
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-remote-resources-plugin/fml/faq.fml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="question1">
+      <question>Why do I need to use this plugin?</question>
+      <answer>
+        <p>
+          This plugin greatly reduces the pain associated with consistent packaging concerns across
+          a large set of projects, or an entire organization. Any project can specify the use of a
+          remote resource bundle and have the resources incorporated into their packaging. This means
+          that you can create standard settings in a parent POM somewhere in the project hierarchy and
+          have all projects use packaged common resources in a standard way like licenses, other legal
+          notices and disclaimers, or anything else that may be common.
+        </p>
+      </answer>
+    </faq>
+    <faq id="question2">
+      <question>The generated files have a lot of missing information.  Looking at the poms from
+        the dependencies, the information isn't there either.  What can I do?</question>
+      <answer>
+        <p>
+          There are two solutions:
+          <ol>
+            <li>
+              File bugs with the projects that produced those artifacts to get them to fix them.  Also,
+              file bugs with the
+              <a href="http://jira.codehaus.org/browse/MEV">Maven Evangelism</a>
+              project to have the Maven people enhance the metadata in the repository.
+            </li>
+            <li>
+              Use a supplemental data file.  You can create a file that contains the missing metadata.
+              For example:
+              <source>
+                <supplementalDataModels>
+                  <supplement>
+                    <project>
+                      <groupId>com.sun.xml.bind</groupId>
+                      <artifactId>jaxb-impl</artifactId>
+                      <name>Sun JAXB Reference Implementation Runtime</name>
+                      <organization>
+                        <name>Sun Microsystems</name>
+                        <url>http://www.sun.com/</url>
+                      </organization>
+                      <licenses>
+                        <license>
+                          <name>COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0</name>
+                          <url>http://www.sun.com/cddl/cddl.html</url>
+                        </license>
+                      </licenses>
+                    </project>
+                  </supplement>
+                </supplementalDataModels>
+              </source>
+              That location for that file can then be configured in the
+              <code>supplementalModels</code> configuration element for the process mojo.  The
+              supplemental information is merged into the information provided from the repository.
+            </li>
+          </ol>
+        </p>
+
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-repository-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-repository-plugin/fml/faq.fml
new file mode 100644
index 0000000..7dc18fb
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-repository-plugin/fml/faq.fml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="including-sources">
+      <question>How can I create an upload bundle containing sources and javadoc for a project?</question>
+      <answer>
+        <p>
+          You can do that by running the <code>javadoc:jar</code> and
+          <code>sources:jar</code> goals before <code>repository:bundle-create</code>:
+        </p>
+        <source>
+mvn sources:jar javadoc:jar repository:bundle-create
+        </source>
+      </answer>
+    </faq>
+    <faq id="bundle-pack">
+      <question>How can I create an upload bundle for a project that I don't build using maven?</question>
+      <answer>
+        <p>
+          You can manually add the artifact to your local repository, creating a POM and optionally adding source and
+          javadoc jars. Then you can run <code>mvn repository:bundle-pack</code> that will prompt you for <code>groupId</code>, <code>artifactId</code> and
+          <code>version</code> and will generate a bundle that you can use for an upload request.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-ressources-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-ressources-plugin/fml/faq.fml
new file mode 100644
index 0000000..7552938
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-ressources-plugin/fml/faq.fml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="What are resources?">
+      <question>
+        What are resources?
+      </question>
+      <answer>
+        <p>Resources are non-source code files used by your project. Examples of
+        these are properties files, images and XML files.</p>
+      </answer>
+    </faq>
+    <faq id="When should I use the resouces plugin's goal outside a lifecycle?">
+      <question>
+        When should I use the Resources Plugin's goal outside a lifecycle?
+      </question>
+      <answer>
+        <p>The Maven Resource Plugin simply copies resources from your source to
+        your build output (with optional filtering). So if that's the only
+        operation you are interested in, you can skip the other phases such as
+        compilation and testing and simply do</p>
+
+        <source>mvn resources:resources</source>
+
+        <p>For example, if you just debugged your configuration file and you
+        want to manually test it in your container if it works, you can simply
+        do</p>
+
+        <source>mvn resources:resources</source>
+
+        <p>This will produce those configuration files on your
+        output thus skipping the other phases which may eat up a huge amount of
+        your time.</p>
+      </answer>
+    </faq>
+    <faq id="Do my main resources go to my test resources as well?">
+      <question>
+        Do my main resources go to my test resources as well?
+      </question>
+      <answer>
+        <p>No. Your main resources and your test resources are separated from
+        each other.</p>
+
+        <p>Your test resources should only be used by your tests. Thus, they are
+        separated from the main to avoid any side effects that may occur.</p>
+      </answer>
+    </faq>
+    <faq id="What encoding values are allowed?">
+      <question>
+        What encoding values are allowed?
+      </question>
+      <answer>
+        <p>The Maven Resource Plugin only allows encoding values representing
+        the charsets supported by the Java platform, namely <code>US-ASCII</code>,
+        <code>ISO-8859-1</code>, <code>UTF-8</code>, <code>UTF-16BE</code>, <code>UTF-16LE</code> and <code>UTF-16</code>.</p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-site-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-site-plugin/fml/faq.fml
new file mode 100644
index 0000000..d9a73b0
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site-plugin/fml/faq.fml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="What is the difference between mvn site and mvn site:site">
+      <question>What is the difference between <i>mvn site</i> and <i>mvn site:site</i>?</question>
+      <answer>
+        <p>
+          <dl>
+            <dt>mvn site</dt>
+            <dd>
+              Calls the <i>site</i> lifecycle with the associate phases (i.e. pre-site, site, post-site, site-deploy).
+              See <a href="http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference">Lifecycle Reference</a>.</dd>
+            <dt>mvn site:site</dt>
+            <dd>Calls the <i>site</i> goal from the site plugin.
+              See <a href="site-mojo.html">site:site</a>.</dd>
+          </dl>
+        </p>
+      </answer>
+    </faq>
+    <faq id="How do I Integrate static (X)HTML pages into my Maven site">
+      <question>How do I Integrate static (X)HTML pages into my Maven site?</question>
+      <answer>
+        <p>
+        You can integrate your static pages by following these steps:
+        </p>
+        <ul>
+          <li>Put your static pages in the resources directory, <code>${basedir}/src/site/resources</code></li>
+          <li>Create your <code>site.xml</code> and put it in <code>${basedir}/src/site</code></li>
+          <li>Link to the static pages by modifying the menu section, create items and map them to the filenames of the static pages</li>
+        </ul>
+      </answer>
+    </faq>
+    <faq id="Why do my absolute links get translated into relative links">
+      <question>Why do my absolute links get translated into relative links?</question>
+      <answer>
+        <p>
+          This happens because the Site Plugin tries to make all URLs relative,
+          when possible. If you have something like this defined in your
+          <code>pom.xml</code>:
+          <source>
+<url>http://www.your.site.com/</url></source>
+          and create links in your <code>site.xml</code> (just an example) like
+          this:
+          <source>
+<links>
+  <item name="Your Site" href="http://www.your.site.com/"/>
+  <item name="Maven 2" href="http://maven.apache.org/maven2/"/>
+</links></source>
+          You will see that the link to "Your site" will be a relative one, but
+          that the link to "Maven 2" will be an absolute link.
+        </p>
+        <p>
+          There is an
+          <a href="http://jira.codehaus.org/browse/MSITE-159">issue for this in JIRA</a>,
+          where you can read more about this.
+        </p>
+      </answer>
+    </faq>
+    <faq id="Why don't the links between parent and child modules work when I run "mvn site"?">
+      <question>Why don't the links between parent and child modules work when I run "<code>mvn site</code>"?</question>
+      <answer>
+        <p>
+          What "<code>mvn site</code>" will do for you, in a multi-project
+          build, is to run "<code>mvn site</code>" for the parent and all its
+          modules <b>individually</b>. The links between parent and child will
+          <b>not</b> work here. They <b>will</b> however work when you deploy
+          the site.
+        </p>
+        <p>
+          If you want to test this, prior to deployment, you can run the
+          <a href="stage-mojo.html"><code>site:stage</code></a> goal as
+          described in the <a href="usage.html">usage documentation</a>
+          instead.
+        </p>
+      </answer>
+    </faq>
+    <faq id="How to include a custom Doxia module, like Twiki">
+      <question>How to include a custom Doxia module, like Twiki?</question>
+      <answer>
+        <p>
+          The site plugin handles out-of-box apt, xdoc and fml formats. If you
+          want to use a custom format like Twiki, you need to specify the
+          Doxia Twiki dependency, i.e.:
+          <source>
+<project>
+  ...
+  <build>
+    <plugins>
+      ...
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-site-plugin</artifactId>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.maven.doxia</groupId>
+            <artifactId>doxia-module-twiki</artifactId>
+            <version>1.0-alpha-11</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+    </plugins>
+  </build>
+  ...
+</project>
+          </source>
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-site-plugin/xdoc/i18n.xml b/doxia-test-docs/src/main/resources/maven-site-plugin/xdoc/i18n.xml
new file mode 100644
index 0000000..66b3f2b
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site-plugin/xdoc/i18n.xml
@@ -0,0 +1,399 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document>
+  <properties>
+    <title>Internationalization</title>
+    <author email="vincent.siveton at gmail.com">Vincent Siveton</author>
+    <author email="dennisl at apache.org">Dennis Lundberg</author>
+  </properties>
+  <body>
+    <section name="Internationalization">
+      <p>
+        The site generation done by the Site Plugin and the Project Info
+        Reports Plugin is fully internationalized. This means that adapting them
+        to another language, a process known as localization, is very easy. All
+        that is needed is to download a couple of properties files and start
+        translating the texts in them. If you want to provide a patch for an
+        unsupported language, there are
+        <a href="#Instructions for Translators">detailed instructions</a> below.
+      </p>
+
+      <p>
+        There are currently three files that needs to be localized to support a
+        new language. The following table summarizes the currently supported
+        languages.
+      </p>
+
+      <p>
+        <b>Note:</b> The files linked to below are the files used in the latest
+        development code. So the files may be newer than the ones included in
+        the latest release.
+      </p>
+
+      <subsection name="Supported Languages">
+        <p>
+          <table>
+            <tr>
+              <th>Languages available</th>
+              <th>Site Plugin</th>
+              <th>Project Info Reports Plugin</th>
+              <th>Doxia Tools</th>
+            </tr>
+            <tr>
+              <td>Brazilian Portuguese</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_pt_BR.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_pt_BR.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_pt_BR.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Catalan</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_ca.properties">See</a>
+              </td>
+              <td>
+                N/A
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_ca.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Chinese simplified</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_zh_CN.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_zh_CN.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_zh_CN.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Czech</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_cs.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_cs.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_cs.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Danish</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_da.properties">See</a>
+              </td>
+              <td>
+                N/A
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_da.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Dutch</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_nl.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_nl.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_nl.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>English (Default)</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>French</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_fr.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_fr.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_fr.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>German</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_de.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_de.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_de.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Hungarian</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_hu.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_hu.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_hu.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Italian</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_it.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_it.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_it.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Japanese</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_ja.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_ja.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_ja.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Korean</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_ko.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_ko.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_ko.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Norwegian</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_no.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_no.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_no.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Polish</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_pl.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_pl.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_pl.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Portuguese</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_pt.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_pt.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_pt.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Slovak</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_sk.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_sk.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_sk.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Spanish</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_es.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_es.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_es.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Swedish</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_sv.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_sv.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_sv.properties">See</a>
+              </td>
+            </tr>
+            <tr>
+              <td>Turkish</td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-site-plugin/src/main/resources/site-plugin_tr.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-project-info-reports-plugin/src/main/resources/project-info-report_tr.properties">See</a>
+              </td>
+              <td>
+                <a href="http://svn.apache.org/repos/asf/maven/shared/trunk/maven-doxia-tools/src/main/resources/site-tool_tr.properties">See</a>
+              </td>
+            </tr>
+          </table>
+        </p>
+
+        <p>
+          To see the current level of localization support you can have a look
+          at the L10n Status Report for each one of them:
+          <ul>
+            <li><a href="l10n-status.html">Site Plugin</a></li>
+            <li><a href="http://maven.apache.org/plugins/maven-project-info-reports-plugin/l10n-status.html">Project Info Reports Plugin</a></li>
+            <li><a href="http://maven.apache.org/shared/maven-doxia-tools/l10n-status.html">Doxia Tools</a></li>
+          </ul>
+        </p>
+      </subsection>
+
+      <subsection name="Instructions for Translators">
+        <p>
+          If you want to contribute a localization, follow these steps:
+          <ol>
+            <li>
+              Download each of the three properties files linked above as
+              the base for your translation. Pick files from a language that you
+              understand well, for instance English.
+            </li>
+            <li>
+              Rename the files to the wanted locale. For example,
+              <code>site-plugin_de.properties</code> for a new German
+              translation for the Maven Site Plugin.
+            </li>
+            <li>
+              Translate the files contents using your preferred text editor.
+              The files must use US-ASCII encoding. For characters that are not
+              included in US-ASCII you must use Unicode escapes, like "\u8ff0".
+              See the tools section below for examples of tools that can help
+              you to convert your texts to use Unicode escapes. 
+            </li>
+            <li>
+              To test your localization
+              <ul>
+                <li>Checkout the latest source for
+                  <a href="source-repository.html">Site Plugin</a>,
+                  <a href="http://maven.apache.org/plugins/maven-project-info-reports-plugin/source-repository.html">Project Info Reports Plugin</a> and
+                  <a href="http://maven.apache.org/shared/maven-doxia-tools/source-repository.html">Doxia Tools</a></li>
+                <li>Include your files in <code>src/main/resources</code> for each one</li>
+                <li>Run "<code>mvn install</code>" for each one</li>
+                <li>Configure a project to produce a site in several locales</li>
+                <li>Make sure that it is using the latest SNAPSHOT version of
+                  each of the above artifacts</li>
+                <li>Run "<code>mvn site</code>" on that project to test it</li>
+              </ul>
+            </li>
+            <li>
+              When you are happy with it, create a new issue in
+              <a href="http://jira.codehaus.org/browse/MSITE">JIRA</a>
+              and attach your files there.
+            </li>
+          </ol>
+        </p>
+      </subsection>
+
+      <subsection name="References and Tools">
+        <p>
+          Please refer to the
+          <a target="_blank" href="http://java.sun.com/j2se/corejava/intl/index.jsp">
+            Java Internationalization home page</a> for an introduction to the topic.
+        </p>
+
+        <p>
+          Here you can find some useful tools to help you with charset
+          questions and conversions:
+          <ul>
+            <li>
+              <b>Tools to find out the charset of a file</b>:
+              Unix <i>file</i> command,
+              <a href="http://cpdetector.sourceforge.net/">cpdetector</a>,
+              <a href="http://plugins.intellij.net/plugin/?id=24">IntelliJ IDEA ShowEncodingPlugin</a>,
+              <a href="http://notepad-plus.sourceforge.net/">Notepad++</a>, ...
+            </li>
+            <li>
+              <b>Tools to write a file in a given charset</b>:
+              any editor like Notepad, Eclipse, IntelliJ IDEA, ...
+            </li>
+            <li>
+              <b>Tools to convert a file from one encoding to another encoding</b>:
+              Unix <i>iconv</i> command, Notepad++.
+            </li>
+            <li>
+              <b>IDE plugins</b>: <a href="http://propedit.sourceforge.jp/index_en.html">Properties Editor Eclipse Plugin</a>.
+            </li>
+          </ul>
+        </p>
+        <p>
+          You can also refer to this Sun FAQ:
+          <a href="http://developers.sun.com/global/technology/standards/reference/faqs/determining-file-encoding.html">
+            How Can I Determine the Encoding of a File?</a>.
+        </p>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/doxia-test-docs/src/main/resources/maven-site/fml/about.fml b/doxia-test-docs/src/main/resources/maven-site/fml/about.fml
new file mode 100644
index 0000000..ee16083
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/fml/about.fml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<faqs title="About Maven 2.0">
+
+  <part id="about">
+    <faq id="m2-goals">
+      <question>Tell me about the goals of Maven 2.0</question>
+      <answer>
+        <p>
+          In addition to understanding the
+          <a href="./what-is-maven.html">Goals of Maven</a>
+          there have also been questions about why Maven 2.0 is rewritten from the successful Maven 1.0.
+          In summary, the main goals of the new Maven 2.0 architecture are:
+        </p>
+        <ul>
+          <li>To be simple to use - it should be more obvious and consistent about how things are working</li>
+          <li>Fast - a new architecture and smaller memory footprint make it as fast as possible</li>
+          <li>To be able to implement the features demanded were not possible under the Maven 1.0 architecture</li>
+        </ul>
+        <p>
+          Unfortunately, to reach these goals we've had to sacrifice backwards compatibility. Instead of
+          making many small incremental changes that would break compatibility often over time, we decided to build on a
+          new, solid base that can be reliable for the future, and also to maintain the existing Maven 1.x product to
+          ensure that existing users are not left out in the cold.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="get-involved">
+      <question>Can I get involved?</question>
+      <answer>
+        <p>
+          The Maven project welcomes anyone that wishes to contribute to do so by providing patches to the source code,
+          participating in design discussions, or to help out on the users mailing list by answering questions.
+        </p>
+        <p>
+          Frequent contributors recognised by existing committers to the project may be asked if they would like to
+          join the project.
+        </p>
+        <p>
+          For instructions on checking out and building Maven 2.0, see
+          <a href="./guides/development/guide-building-m2.html">Building Maven 2.0</a>
+          .
+        </p>
+        <p>
+          For more information, please see
+          <a href="http://maven.apache.org/guides/development/guide-helping.html">How to Help</a>
+          .
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="scripting-languages">
+      <question>What plugin languages will Maven 2.0 support? What about [insert language here]?</question>
+      <answer>
+        <p>
+          As of the current release, Maven supports pure Java and Beanshell. Java is the preferred
+          language for it's familiarity and speed.
+        </p>
+        <p>
+          We get asked a lot whether Maven 2.0 will support other languages, in particular Groovy.
+          We will allow the use of Groovy - and virtually any other scripting
+          language (if there is demand) if someone can commit a small amount of time to implementing a
+          factory for it.
+        </p>
+        <p>
+          We would recommend waiting for Groovy to have a 1.0 release so the API
+          (and language!) is stable. What we will not be doing is actively
+          supporting it (in terms of answering questions about how to use it)
+          like we currently do for Jelly in Maven 1.x.
+        </p>
+        <p>
+          Beanshell is more mature, and we want a language that users will find
+          answers for when they look, and that when they find bugs, it is
+          clearly defined where they actually are.
+        </p>
+        <p>
+          We may consider emphasising a different language if there are compelling technical reasons
+          for doing so - if you have a suggestion, feel free to contact the development list.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="get-help">
+      <question>Where do I get help with Maven 2.0?</question>
+      <answer>
+        <p>
+          Help for both Maven 1.0 and Maven 2.0 can be obtained by subscribing and posting to the
+          <a href="http://maven.apache.org/mail-lists.html">Maven Users List</a>.
+        </p>
+        <p>
+          You can also join us on IRC (Internet Relay Chat) at <code>irc.codehaus.org</code>
+          on <code>#maven</code>. This is available both
+          over <a href="irc://irc.codehaus.org/#maven">IRC</a>
+          and <a href="http://irc.codehaus.org/">HTTP</a>
+          for those behind firewalls (enter <code>#maven</code> in the <i>Channel</i> box).
+          You could also browse the IRC logs <a href="http://dev.rectang.com/logs/codehaus/%23maven/">here</a>.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
+
diff --git a/doxia-test-docs/src/main/resources/maven-site/fml/general.fml b/doxia-test-docs/src/main/resources/maven-site/fml/general.fml
new file mode 100644
index 0000000..20891f4
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/fml/general.fml
@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<faqs title="Frequently Asked Technical Questions">
+  <part id="faq">
+    <faq id="scope-provided">
+      <question>How do I prevent including JARs in WEB-INF/lib? I need a "compile only" scope!</question>
+      <answer>
+        <p>
+          The scope you should use for this is
+          <code>provided</code>. This indicates to Maven that the dependency will be
+          provided at run time by its container or the JDK, for example.
+        </p>
+        <p>
+          Dependencies with this scope will not be passed on transitively, nor will they be bundled in an package such
+          as a WAR, or included in the runtime classpath.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="available-plugins">
+      <question>How do I list available plugins?</question>
+      <answer>
+        <p>
+          The "Available Plugins" page lists them,
+          and provides additional information to browse the Maven 2 repository.
+          See <a href="http://maven.apache.org/plugins/">http://maven.apache.org/plugins</a>
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="plugin-version">
+      <question>How do I determine what version of a plugin I am using?</question>
+      <answer>
+        <p>
+          You can use the Maven Help Plugin's <code>describe</code> goal. For example, to find out the version
+          of the install plugin:
+        </p>
+        <source>mvn -Dplugin=install help:describe</source>
+        <p>
+          Note that you must give the plugin prefix as the argument to plugin, not it's artifact ID.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="using-ant-tasks">
+      <question>How can I use Ant tasks in Maven 2?</question>
+      <answer>
+        <p>
+          There are currently 2 alternatives:
+        </p>
+        <ul>
+          <li>
+            For use in a plugin written in Java, Beanshell or other Java-like scripting language, you can construct
+            the Ant tasks using the
+            <a href="http://ant.apache.org/manual/antexternal.html">instructions given in the Ant
+              documentation</a>
+          </li>
+          <li>
+            If you have very small amounts of Ant script specific to your project, you can use the
+            <a href="http://maven.apache.org/plugins/maven-antrun-plugin/index.html">AntRun plugin</a>.
+          </li>
+        </ul>
+      </answer>
+    </faq>
+
+    <faq id="Compiling-J2SE-5">
+      <question>How do I set up Maven so it will compile with a target and source JVM of my choice?</question>
+      <answer>
+        <p>
+          You must configure the source and target parameters in your pom. For example, to set the source and
+          target JVM to 1.5, you should have in your pom:
+        </p>
+        <source>
+  ...
+  <build>
+  ...
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>2.0.2</version>
+        <configuration>
+          <source>1.5</source>
+          <target>1.5</target>
+        </configuration>
+      </plugin>
+    </plugins>
+  ...
+  </build>
+  ...</source>
+      </answer>
+    </faq>
+
+    <faq id="dir-struct">
+      <question>Is it possible to create my own directory structure?</question>
+      <answer>
+        <p>
+          Absolutely yes!
+        </p>
+        <p>
+          By configuring <sourceDirectory>, <resources> and other elements of
+          the <build> section.
+        </p>
+        <p>
+          In addition, you may need to change the plugin configuration if you are
+          not using plugin defaults for their files/directories.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="m2-source-code">
+      <question>Where is the source code? I couldn't seem to find a link anywhere on the Maven2 site.</question>
+      <answer>
+        <p>
+          The source code can be found in subversion: <a href="http://svn.apache.org/repos/asf/maven/components/trunk">http://svn.apache.org/repos/asf/maven/components/trunk</a>.
+        </p>
+        <p>
+          For more information, see <a href="guides/development/guide-building-m2.html">Building Maven 2.0</a>.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="proxy-needed">
+      <question>Maven can't seem to download the dependencies. Is my installation correct?</question>
+      <answer>
+        <p>
+          You most probably need to configure Maven to use a proxy. Please see the information on
+          <a href="guides/mini/guide-proxies.html">Configuring a proxy</a> for information on how to configure your proxy
+          for Maven.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="importing-jars">
+      <question>I have a jar that I want to put into my local repository. How can I copy it in?</question>
+      <answer>
+        <p>
+          If you understand the layout of the maven repository, you can copy the jar directly into where it
+          is meant to go. Maven will find this file next time it is run.
+        </p>
+
+        <p>
+          If you are not confident about the layout of the maven repository, then you can
+          adapt the following command to load in your jar file, all on one line.
+        </p>
+
+        <source><![CDATA[
+mvn install:install-file
+  -Dfile=<path-to-file>
+  -DgroupId=<group-id>
+  -DartifactId=<artifact-id>
+  -Dversion=<version>
+  -Dpackaging=<packaging>
+  -DgeneratePom=true
+
+Where: <path-to-file>  the path to the file to load
+       <group-id>      the group that the file should be registered under
+       <artifact-id>   the artifact name for the file
+       <version>       the version of the file
+       <packaging>     the packaging of the file e.g. jar
+     ]]></source>
+        <p>
+          This should load in the file into the maven repository, renaming it as needed.
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="unsubscribing-from-mailing-lists">
+      <question>How do I unsubscribe from Maven mailing lists?</question>
+      <answer>
+        <p>
+        To unsubscribe from a Maven mailing list you simply send a message to
+        <source>[mailing-list]-unsubscribe at maven.apache.org</source>
+        </p>
+        <p>
+        So, if you have subscribed to <code>users at maven.apache.org</code> then you would
+        send a message to <code>users-unsubscribe at maven.apache.org</code> in order to
+        get off the list.
+        People tend to have problems when they subscribe with one address and
+        attempt to unsubscribe with another. So make sure that you are using the
+        same address when unsubscribing that you used to subscribe before
+        asking for help.
+        </p>
+        <p>
+        If you find you still cannot get off a list then
+        send a message to <code>[mailing-list]-help at maven.apache.org</code>. These
+        instructions are also appended to every message sent out on a maven mailing
+        list ...
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="skip-test">
+      <question>How do I skip the tests?</question>
+      <answer>
+        Add the parameter <code>-Dmaven.test.skip=true</code> in the command line
+      </answer>
+    </faq>
+
+    <faq id="run-one-test">
+      <question>How can I run a single unit test?</question>
+      <answer>
+        Use the parameter <code>-Dtest=MyTest</code> at the command line.
+        NB: do not specify the entire package (org.apache.x.y.MyTest)
+      </answer>
+    </faq>
+
+    <faq id="special-characters-site">
+      <question>Handle special characters in site</question>
+      <answer>
+        <p>Configure your ide to use the correct encoding. With eclipse, add
+        <code>-Dfile.encoding=ISO-8859-1</code> in eclipse.ini file</p>
+        <p>Configure the output encoding in your pom
+      <source>
+  ...
+  <plugin>
+    <groupId>org.apache.maven.plugins</groupId>
+    <artifactId>maven-site-plugin</artifactId>
+    <version>2.0-beta-6</version>
+    <configuration>
+      <outputEncoding>UTF-8</outputEncoding>
+    </configuration>
+  </plugin>
+  ...</source>
+        </p>
+        <p>
+          Configure the file encoding use by mvn.
+          add to MAVEN_OPTS the encoding (same as the ide).
+          This can be made with adding <code>MAVEN_OPTS="-Dfile.encoding=ISO-8859-1"</code> in $HOME/.profile
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="tools-jar-dependency">
+      <question>How do I include <code>tools.jar</code> in my dependencies?</question>
+      <answer>
+        <p>
+          The following code includes <code>tools.jar</code> for JDKs on Windows, Linux and Solaris (it is already
+          included in the runtime for Mac OS X and some free JDKs).
+        </p>
+        <source>
+...
+  <profiles>
+    <profile>
+      <id>default-tools.jar</id>
+      <activation>
+        <property>
+          <name>java.vendor</name>
+          <value>Sun Microsystems Inc.</value>
+        </property>
+      </activation>
+      <dependencies>
+        <dependency>
+          <groupId>com.sun</groupId>
+          <artifactId>tools</artifactId>
+          <version>1.4.2</version>
+          <scope>system</scope>
+          <systemPath>${java.home}/../lib/tools.jar</systemPath>
+        </dependency>
+      </dependencies>
+    </profile>
+  </profiles>
+  ...</source>
+     </answer>
+    </faq>
+
+    <faq id="test-property-name">
+      <question>Maven compiles my test classes but doesn't run them?</question>
+      <answer>
+        <p>
+          Tests are run by the surefire plugin.  The surefire plugin can be configured to run certain test classes and
+          you may have unintentionally done so by specifying a value to ${test}.
+
+          Check your settings.xml and pom.xml for a property named "test" which would like this:
+        </p>
+        <source>
+  ...
+  <properties>
+    <property>
+      <name>test</name>
+      <value>some-value</value>
+     </property>
+  </properties>
+  ...</source>
+        <p>
+          or
+        </p>
+        <source>
+  ...
+  <properties>
+    <test>some-value</test>
+  </properties>
+  ...</source>
+      </answer>
+    </faq>
+
+    <faq id="snapshot-artifacts">
+      <question>Where are Maven SNAPSHOT artifacts?</question>
+      <answer>
+        <p>
+          If you are trying to build a development version of Maven or plugins,
+          you may need to access the maven snapshot repositories.
+        </p>
+
+        <p>
+          You need to update your settings.xml file using the
+          <a href="guides/development/guide-testing-development-plugins.html">
+          Guide to Plugin Snapshot Repositories</a>
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="maven-xsd">
+      <question>Where are the Maven XSD schemas?</question>
+      <answer>
+        <p>
+          The Maven XSD is located <a href="http://maven.apache.org/maven-v4_0_0.xsd">here</a> and the Maven
+          Settings XSD is located <a href="http://maven.apache.org/xsd/settings-1.0.0.xsd">here</a>.
+        </p>
+        <p>
+          Your favorite IDE probably supports XSD schema's for pom.xml and settings.xml editing. You need to
+          specify the following:
+        <source>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                      http://maven.apache.org/maven-v4_0_0.xsd">
+
+  ...
+</project></source>
+        <source>
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
+                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
+  ...
+</settings></source>
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="getting-help">
+      <question>Maven doesn't work, how do I get help?</question>
+      <answer>
+        <p>
+          We have compiled a list of available resources on the
+          <a href="users/getting-help.html">getting help page</a>
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="How_to_produce_execution_debug_output_or_error_messages">
+      <question>How to produce execution debug output or error messages?</question>
+      <answer>
+        <p>
+          You could call Maven with <i>-X</i> parameter or <i>-e</i> parameter. For more information,
+          run:
+          <source>mvn --help</source>
+        </p>
+      </answer>
+    </faq>
+
+    <faq id="What_is_a_Mojo">
+      <question>What is a Mojo?</question>
+      <answer>
+        <p>
+          A mojo is a <b>M</b>aven plain <b>O</b>ld <b>J</b>ava <b>O</b>bject. Each mojo is an executable
+          <i>goal</i> in Maven, and a plugin is a distribution of one or more related mojos.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-site/fml/maven1.fml b/doxia-test-docs/src/main/resources/maven-site/fml/maven1.fml
new file mode 100644
index 0000000..771f9f6
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/fml/maven1.fml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<faqs title="Information for Maven 1.0 Users">
+
+  <part id="maven1">
+    <faq id="changed">
+      <question>What's Changed?</question>
+      <answer>
+        <p>
+          Maven 2.0 will feel very different to a Maven 1.0 user - and perhaps a little strange. But it is a lot simpler
+          to work with and closer to how Maven was always meant to be! The key changes from Maven 1.0 are:
+        </p>
+        <ul>
+          <li>
+            <i>Faster and smaller</i>
+            - The Maven core no longer uses Ant, Jelly or Xerces making it much smaller, has
+            fewer dependencies, and is perfect for embedding in other tools.
+          </li>
+          <li>
+            <i>Defined build lifecycle</i>
+            - No more
+            <code>prereqs</code>
+            ,
+            <code>preGoals</code>
+            and
+            <code>postGoals</code>
+            .
+            The build is a series of well defined phases. This also means that the normal goal names are not used -
+            <code>compile</code>
+            ,
+            <code>test</code>
+            and
+            <code>install</code>
+            work for any project type.
+          </li>
+          <li>
+            <i>Built-in multiple project handling</i>
+            - Use the same goals on a set of projects, and aggregate the
+            results.
+          </li>
+          <li>
+            <i>Improved
+              <code>SNAPSHOT</code>
+              handling
+            </i>
+            - Snapshots are now checked for updates only once per day by
+            default - though can be configured to be once per build, on a particular interval, or never. A command line
+            option can force a check - making it more like updating from an SCM.
+          </li>
+          <li>
+            <i>No more properties files</i>
+            - All plugins are now configured from the POM (which is now called
+            <code>pom.xml</code>
+            ).
+          </li>
+          <li>
+            <i>No more
+              <code>maven.xml</code>
+            </i>
+            - Plugins are now easier to build and integrate, and are the only way
+            to script your builds. (Note that additions may later be made to the POM to allow simple things that
+            scripting
+            was used for, such as goal aliasing).
+          </li>
+          <li>
+            <i>No more Jelly</i>
+            - Plugins are primarily written in Java, though there are providers for other scripting languages.
+          </li>
+          <li>
+            <i>Improved repository layout</i>
+            - Maven 2.0 supports both the existing layout, and an improved repository
+            layout that has deeper, partitioned structure making it easier to browse.
+          </li>
+        </ul>
+      </answer>
+    </faq>
+    <faq id="m1-or-m2">
+      <question>Should I use Maven 2.0, or Maven 1.0?</question>
+      <answer>
+        <p>
+          Maven 2.0 is the latest stable release, and we certainly recommend it for all new projects.
+        </p>
+        <p>
+          If you are already using Maven 1.0 - you should try out Maven 2.0, as it is very much improved.
+          However, we still continue to support the Maven 1.x releases at this time.
+        </p>
+        <p>
+          If you do use Maven 1.0, and would like to upgrade in the future, you should carefully consider following some
+          of the
+          <a href="http://maven.apache.org/maven-1.x/using/bestpractices.html">Best Practices</a>
+          listed. These will make your project a lot easier to migrate in the future.
+        </p>
+        <p>
+          The following are the known limitations in the current Maven 2.0 release compared to Maven 1.x:
+        </p>
+        <ul>
+          <li>
+            <i>Availability of plugins</i>
+            - While most of the core Maven 1.x plugins have been converted,
+            several 3rd party plugins may not be available. Maven 2.0 cannot execute Maven 1.0 plugins.
+          </li>
+        </ul>
+      </answer>
+    </faq>
+    <faq id="m1-future">
+      <question>What will happen to Maven 1.0?</question>
+      <answer>
+        <p>
+          Support for Maven 1.0.2 has been discontinued with the release of Maven 1.1.
+          While significant new features will not be added to the
+          <a href="http://maven.apache.org/maven-1.x/">Maven 1.x</a> core
+          (such as transitive dependencies), bugfixes and support continue and the
+          repository is still available.
+        </p>
+      </answer>
+    </faq>
+    <faq id="m1-plugins">
+      <question>Will my Maven 1.0 plugins be supported?</question>
+      <answer>
+        <p>
+          Not directly.
+        </p>
+        <p>
+          We recommend building your Jelly plugins as thin wrappers around Java beans that do not use Maven 1.0 API's,
+          which will allow easy migration to Maven 2.0.
+        </p>
+      </answer>
+    </faq>
+    <faq id="m1-maven-xml">
+      <question>How do I write custom scripts without a
+        <code>maven.xml</code>
+        file?
+      </question>
+      <answer>
+        <p>Taken from
+          <a href="http://mail-archives.apache.org/mod_mbox/maven-users/200504.mbox/%3c1113788711.5625.30.camel@172.16.1.36%3e">
+            this post to the Maven User's List</a>:
+        </p>
+        <blockquote>
+          Everything in m2 is a plugin so for doing any sort of custom work like
+          that you will need to make a plugin. This 1) greatly reduces the
+          complexity within m2 because we only deal with proper plugins. The
+          maven.xml file in m1 was a psuedo plugin essentially and greatly
+          complicated the internals and 2) it promotes the sharing of your work
+          right from the get go. To start with you'll probably write a plugin that
+          is only applicable to your specific requirements, or your team's work,
+          but you'll see people asking for some functionality and you'll go "hey,
+          I have something like that!" and hopefully people who have concrete
+          solutions will generalize their solutions so they can be shared with
+          others. That's what we're trying to encourage.
+        </blockquote>
+        <blockquote>
+          We estimate that there's a lot of work bound up in project's maven.xml
+          that is not being shared and we'd like to try and change that. We plan
+          to make it dead simple to share plugins and hopefully people can use
+          other project's plugins as a start for a plugin that they may need
+          themselves.
+        </blockquote>
+      </answer>
+    </faq>
+    <faq id="convert">
+      <question>How do I convert from Maven 1.x to Maven 2.x?</question>
+      <answer>
+        This is dicussed in <a href="http://maven.apache.org/guides/mini/guide-m1-m2.html">Guide to Moving from Maven 1.x to Maven 2.x</a>.
+      </answer>
+    </faq>
+  </part>
+</faqs>
+
diff --git a/doxia-test-docs/src/main/resources/maven-site/fml/project-faq.fml b/doxia-test-docs/src/main/resources/maven-site/fml/project-faq.fml
new file mode 100644
index 0000000..8551e89
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/fml/project-faq.fml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<faqs title="Frequently Asked Questions About Project Metadata">
+  <part id="faq">
+    <faq id="why-care">
+      <question>Why do I care?</question>
+      <answer>
+        <p>
+          If you're a user of Maven then you are familiar with the Project Object Model (POM) which is the basic unit of
+          work in Maven. Maven is a project-centric tool and so we attempt to capture the essence of a project in the
+          POM. This includes things like what your project is, where the project lives, where you can find the sources
+          for project, who the developers are on the project and how you can get hold of them, what you need to build
+          the project, the way your project will be built, what form your project will be distributed in, and where it
+          will be distributed from.
+        </p>
+        <p>
+          But why would a project not using Maven care?
+        </p>
+        <p>
+          Whether you want to use Maven or not, users of your project - especially if you provide a framework or
+          reusable library - may choose to use Maven. The quality of the metadata in the Maven repository is important
+          to your users as they list dependencies on your metadata, and link in the information into their own
+          projects.
+        </p>
+        <p>
+          Maintaining the metadata for your project is not hard - you can submit it to Maven at release time (and
+          for large projects it can be setup automatically), and just need to describe your project, its location,
+          version and most importantly dependencies. Not doing so, or providing incomplete or invalid information
+          leaves this responsibility to the users of your project.
+        </p>
+        <p>
+          The Maven team does work on this metadata actively to improve its quality, but with thousands of artifacts in
+          the repository and only a certain level of knowledge of other projects this is not ideal. Nobody knows your
+          project better than you.
+        </p>
+      </answer>
+    </faq>
+    <faq id="how-to-improve-metadata">
+      <question>How do I make sure my project's dependency metadata is correct?</question>
+      <answer>
+        <p>
+          It is best to get it right at the time of a release to avoid having to make difficult updates later on.
+          Your information can be submitted to Maven using the regular
+          <a href="guides/mini/guide-central-repository-upload.html">repository upload procedure</a>
+          .
+        </p>
+        <p>
+          The following information is what is best to provide:
+        </p>
+        <ul>
+          <li>Project name</li>
+          <li>Project URL</li>
+          <li>License</li>
+          <li>Description of the project</li>
+          <li>Group and Artifact ID</li>
+          <li>Packaging</li>
+          <li>Version</li>
+          <li>Dependencies</li>
+        </ul>
+        <p>
+          The group ID should resemble the package name, or reverse DNS of your web site, and can contain subgroups as
+          you see fit: for example, org.apache.maven and org.apache.maven.plugins.
+        </p>
+        <p>
+          The artifact ID is specific to each artifact and by convention should be the filename, excluding
+          extension.
+        </p>
+        <p>
+          The packaging is the type of your artifact, such as jar, war, ear, ejb, dll, etc.
+        </p>
+        <p>
+          Each dependency also contains their group ID and artifact ID, as well as version specification.
+          In particular, you should ensure that optional dependencies are marked as such, and that runtime and testing
+          only dependencies are marked with the given scope. Ranges can be used for version if that is appropriate,
+          such as commons-collections [2.0,3.0). Ensure that the dependency exists in the Maven system and matches
+          first.
+        </p>
+        <p>
+          See the format of the
+          <a href="/maven-model/maven.html">project descriptor</a>
+          for more information.
+        </p>
+      </answer>
+    </faq>
+    <faq id="how-to-automatically-sync">
+      <question>How do I ensure my latest releases automatically appear in the Maven repository?</question>
+      <answer>
+        <p>
+          If you able to publish your releases to a Maven2 style repository and make it available over rsync,
+          we can automatically publish them to ibiblio when released. This procedure requires that you take full
+          responsibility for your metadata, but also ensures the best service for your users. If this is interesting
+          to your project, contact dev at maven.apache.org.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
diff --git a/doxia-test-docs/src/main/resources/maven-site/xdoc/articles.xml b/doxia-test-docs/src/main/resources/maven-site/xdoc/articles.xml
new file mode 100644
index 0000000..b3da8cb
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/xdoc/articles.xml
@@ -0,0 +1,381 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document>
+  <properties>
+    <title>External Resources on Maven</title>
+    <author email="brett at apache.org">Brett Porter</author>
+    <author email="vmassol at apache.org">Vincent Massol</author>
+  </properties>
+  <body>
+    <section name="Books on Maven">
+<div style="float: left; margin-right: 5em;">
+<a href="http://blogs.sonatype.com/people/book/about/"><img src="images/books/definitive_guide.jpg" title="Maven: The Definitive Guide" /></a>
+</div>
+
+      <div style="min-height: 220px">
+        <p><a href="http://blogs.sonatype.com/people/book/about/"><strong>Maven: The Definitive Guide</strong></a> (Readable HTML and Free PDF Download)</p>
+        <ul>
+          <li><strong>Covers: </strong>Maven 2.0.9+</li>
+          <li><strong>Published: </strong> O'Reilly (Edition 1: October 1, 2008)</li>
+          <li><strong>Authors: </strong>
+            <a href="http://www.sonatype.com">Sonatype</a> (
+<a href="http://blogs.sonatype.com/people/jvanzyl">Jason van Zyl</a>, 
+<a href="http://blogs.sonatype.com/people/brian">Brian Fox</a>,
+<a href="http://blogs.sonatype.com/people/john">John Casey</a>,
+<a href="http://bsnyderblog.blogspot.com/">Bruce Snyder</a>,
+<a href="http://blogs.sonatype.com/people/book">Tim O'Brien</a>,
+Eric Redmond)
+          </li>
+          <li><strong>Read Online:</strong> <a href="http://blogs.sonatype.com/people/book/about/">http://blogs.sonatype.com/people/book/about/</a></li>
+          <li><strong>Buy the Book:</strong> <a href="http://www.amazon.com/Maven-Definitive-Guide-Sonatype-Company/dp/0596517335/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1226091388&sr=8-1">Amazon</a></li>
+        </ul>
+      </div>
+
+<div style="float: left; margin-right: 5em;">
+      <img src="images/books/better_builds.png" title="Better Builds with Maven" />
+</div>
+      <div style="min-height: 220px">
+        <p><a href="http://www.exist.com/better-build-maven"><strong>Better Builds with Maven</strong></a> (Free PDF Download)</p>
+        <ul>
+          <li><strong>Covers:</strong>Maven 2.0.4</li>
+          <li><strong>Published:</strong><a href="http://www.exist.com">Exist</a> (March 2006)</li>
+          <li><strong>Authors:</strong>
+            John Casey,
+            <a href="http://blogs.codehaus.org/people/vmassol/">Vincent Massol</a>,
+            <a href="http://blogs.exist.com/bporter">Brett Porter</a>,
+            <a href="http://www.jroller.com/carlossg/">Carlos Sanchez</a>
+          </li>
+          <li><strong>Read Online:</strong> <a href="http://www.exist.com/better-build-maven">http://www.exist.com/better-build-maven</a></li>
+        </ul>
+      </div>
+
+<div style="float: left; margin-right: 5em;">
+      <img src="images/books/developer_notebook.gif" title="Maven: A Developer's Notebook" />
+</div>
+      <div style="min-height: 220px">
+        <p><a href="http://www.oreilly.com/catalog/mavenadn/"><strong>Maven: A Developer's Notebook</strong></a></p>
+
+        <ul>
+          <li><strong>Covers:</strong>Maven 1.0.2</li>
+          <li><strong>Published:</strong> O'Reilly (July 2005)</li>
+          <li><strong>Authors:</strong>
+            <a href="http://blogs.codehaus.org/people/vmassol/">Vincent Massol</a>,
+            <a href="http://www.oreillynet.com/pub/au/1738">Tim O'Brien</a>
+          </li>
+        </ul>
+
+      </div>
+    </section>
+
+    <section name="Miscellaneous on Maven">
+      <p>
+        If you're interested in testing your Maven skills, check out <a href="http://www.javablackbelt.com/QuestionnaireDefDisplay.wwa?questPublicId=01559">JavaBlackBelt's Maven exam</a>.
+        This exam is being written collaboratively by the community. Feel free to add new questions, suggest improvements, etc.
+      </p>
+    </section>
+
+    <section name="Articles on Maven">
+      <p>
+        If you are writing an article on Maven we suggest contacting the developers on the mailing list as we would be happy
+        to provide feedback to help ensure accuracy in your article. Just ping us on the <a href="mail-lists.html">dev mailing list</a>
+        to get in touch.
+      </p>
+
+      <div style="margin-top: 1.5em;">
+        <table>
+          <tr>
+            <th>Title</th>
+            <th>Publisher</th>
+            <th>Author</th>
+            <th>Published</th>
+          </tr>
+          <tr>
+            <td><a href="http://www.theserverside.com/tt/articles/article.tss?l=Introductiontom2eclipse">Introduction to m2eclipse</a></td>
+            <td>TheServerSide</td>
+            <td>Tim O'Brien, Bruce Snyder, Eugene Kuleshov</td>
+            <td>July 2008</td>
+          </tr>
+          <tr>
+            <td><a href="http://msaitozen.googlepages.com/maven2.x_dokuman.pdf">Maven 2.x (in Turkish)</a></td>
+            <td>Anadolu �niversitesi</td>
+            <td>Mustafa Sait �zen</td>
+            <td>August 2007</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.theserverside.com/tt/articles/article.tss?l=SettingUpMavenRepository">Setting up the Internal Repository</a></td>
+            <td>The Server Side</td>
+            <td>Avneet Mangat</td>
+            <td>June 2007</td>
+          </tr>
+          <tr>
+            <td><a href="http://today.java.net/pub/a/today/2007/03/01/building-web-applications-with-maven-2.html">Building Web Applications with Maven 2</a></td>
+            <td>java.net</td>
+            <td>Will Iverson</td>
+            <td>1 March 2007</td>
+          </tr>
+          <tr>
+            <td><a href="http://www-128.ibm.com/developerworks/edu/j-dw-java-mavenv2.html">Introduction to Apache Maven 2</a></td>
+            <td>developerWorks</td>
+            <td>Sing Li</td>
+            <td>19 December 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.manuelrecena.com/docs/maven_061106.pdf">Maven - Menos mal que has venido (in Spanish)</a></td>
+            <td>Universidad de Sevilla</td>
+            <td>Manuel J. Recena Soto</td>
+            <td>6 November 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://java.developpez.com/faq/maven/">FAQ for Maven 2 and Continuum (in French)</a></td>
+            <td>Developpez.com</td>
+            <td>Eric Reboisson</td>
+            <td>11 October 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.devx.com/Java/Article/32386">Keep Your Maven Projects Portable Throughout the Build Cycle</a></td>
+            <td>DevX</td>
+            <td>Eric Redmond</td>
+            <td>8 September 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://www-128.ibm.com/developerworks/java/library/j-ap09056/index.html">Automation for the people: Choosing a Continuous Integration server</a></td>
+            <td>deverloperWorks</td>
+            <td>Paul Duvall</td>
+            <td>5 September 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.javaposse.com/index.php?post_id=112128">Java Posse #070 - Interview with Brett Porter of Maven</a></td>
+            <td>Java Posse</td>
+            <td>Tor Norbye, Carl Quinn, Dick Wall, Joe Nuxoll, Brett Porter</td>
+            <td>18 July 2006</td>
+          </tr>
+          <!--
+          <tr>
+            <td><a href="http://repo.maujr.org/artigos/maven2-guj/maven-2-guj.pdf">Automatizando seus projetos com o Maven 2</a></td>
+            <td></td>
+            <td>Maur�cio Linhares</td>
+            <td>7 July 2006</td>
+          </tr>
+          -->
+          <tr>
+            <td><a href="http://today.java.net/pub/a/today/2006/05/30/continuous-integration-with-continuum.html">Continuous Integration with Continuum</a></td>
+            <td>Java.net</td>
+            <td>John Ferguson Smart</td>
+            <td>30 May 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.javaworld.com/javaworld/jw-05-2006/jw-0529-maven.html">The Maven 2 POM demystified</a></td>
+            <td>JavaWorld</td>
+            <td>Eric Redmond</td>
+            <td>29 May 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.ddj.com/showArticle.jhtml?articleID=186100398">Maven: Building Complex Systems</a></td>
+            <td>Dr.Dobb's</td>
+            <td>Gigi Sayfan</td>
+            <td>21 April 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://cvs.peopleware.be/training/maven/maven2/">Working with maven 2</a></td>
+            <td>PeopleWare</td>
+            <td>Jan Dockx</td>
+            <td>13 April 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.onjava.com/pub/a/onjava/2006/03/29/maven-2-0.html">Maven 2.0: Compile, Test, Run, Deploy, and More</a></td>
+            <td>onjava</td>
+            <td>Chris Hardin</td>
+            <td>29 March 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://metaware-inc.wiki.mailxmail.com/AntMaven">Descripcion tecnica de Maven (in Spanish)</a></td>
+            <td>Metaware Inc</td>
+            <td>Juan Pablo Santos Rodr�guez</td>
+            <td>13 March 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.javaworld.com/javaworld/jw-02-2006/jw-0227-maven.html">Get the most out of Maven 2 site generation</a></td>
+            <td>JavaWorld</td>
+            <td>John Ferguson Smart</td>
+            <td>27 February 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://dcabasson.developpez.com/articles/java/maven/introduction-maven2/">An introduction to Maven 2 (in french)</a></td>
+            <td>Developpez.com</td>
+            <td>Denis Cabasson</td>
+            <td>27 January 2006</td>
+          </tr>
+          <tr>
+            <td><a href="http://blogs.codehaus.org/people/vmassol/archives/001275_javapolis_2005_slides_on_maven_2.html">Maven 2.0 - Javapolis 2005</a></td>
+            <td></td>
+            <td>Vincent Massol</td>
+            <td>15 December 2005</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.javaworld.com/javaworld/jw-12-2005/jw-1205-maven.html?lsrc=maven-users">An introduction to Maven 2</a></td>
+            <td>JavaWorld</td>
+            <td>John Ferguson Smart</td>
+            <td>5 December 2005</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.developer.com/open/article.php/10930_3552026_1">Taking the Maven 2 Plunge</a></td>
+            <td></td>
+            <td>David DeWolf</td>
+            <td>1 October 2005</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.onjava.com/pub/a/onjava/2005/09/07/maven.html">Building J2EE Projects with Maven</a></td>
+            <td>OnJava</td>
+            <td>Vincent Massol</td>
+            <td>7 September 2005</td>
+          </tr>
+          <tr>
+            <td><a href="https://sydneyjug.dev.java.net/files/documents/922/15554/sjug20050601.pdf">Maven 2.0 and Continuum SJUG Presentation</a></td>
+            <td></td>
+            <td>Brett Porter</td>
+            <td>1 June 2005</td>
+          </tr>
+          <tr>
+            <td><a href="http://www-128.ibm.com/developerworks/opensource/library/os-maven/index.html">Exploiting Maven in Eclipse</a></td>
+            <td>developerWorks</td>
+            <td>Gilles Dodinet</td>
+            <td>24 May 2005</td>
+          </tr>
+          <tr>
+            <td><a href="http://www-128.ibm.com/developerworks/websphere/library/techarticles/0503_boog/0503_boog.html?ca=dgr-lnxw09Maven">Managing WebSphere Portal V5.1 projects with Apache Maven and Rational Application Developer 6.0</a></td>
+            <td>developerWorks</td>
+            <td>Hinrich Boog</td>
+            <td>30 March 2005</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.codehaus.org/~vmassol/blog/Maven%201.0%20-%2020041216.ppt">Maven 1.0 Javapolis Presentation</a></td>
+            <td></td>
+            <td>Vincent Massol</td>
+            <td>16 December 2004</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.oracle.com/technology/pub/articles/masterj2ee/j2ee_wk2.html">Master and Commander by Julien Dubois</a></td>
+            <td>Oracle</td>
+            <td>Julien Dubois</td>
+            <td>November 2004</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.manfred-wolff.de/Maven-short.pdf">installing and working with Maven (in German)</a></td>
+            <td></td>
+            <td>Manfred Wolff</td>
+            <td>August 2004</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.internetnews.com/dev-news/article.php/3381841">Apache's Maven Comes of Age</a> (Coverage of the release of Maven 1.0)</td>
+            <td>internetnews.com</td>
+            <td>Sean Michael Kerner</td>
+            <td>15 July 2004</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.onjava.com/pub/a/onjava/2004/03/17/maven.html">Extending Maven Through Plugins by Eric Pugh</a></td>
+            <td>OnJava</td>
+            <td>Eric Pugh</td>
+            <td>17 March 2004</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.theserverside.com/tt/articles/article.tss?l=MavenMagic">Maven Magic - a tutorial on Maven and J2EE projects.</a></td>
+            <td>TheServerSide</td>
+            <td>Srikanth Shenoy</td>
+            <td>November 2003</td>
+          </tr>
+          <!--
+          <tr>
+            <td><a href="http://www.jdocentral.com/JDO_Articles_20031117.html">JDO Meets Maven</a></td>
+            <td>JDOCentral</td>
+            <td>Andy Jefferson</td>
+            <td>17 November 2003</td>
+          </tr>
+          -->
+          <tr>
+            <td><a href="http://www.onjava.com/pub/a/onjava/2003/10/22/maven.html">Developing with Maven by Rob Herbst</a></td>
+            <td>OnJava</td>
+            <td>Rob Herbst</td>
+            <td>22 October 2003</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.devx.com/java/Article/17204">Apache Maven Simplifies the Java Build Process Even More Than Ant</a></td>
+            <td>DevX</td>
+            <td>Dave Ford</td>
+            <td>2 September 2003</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.pivolis.com/pdf/J2EE_projects_Maven_V1.1.pdf">Building J2EE applications with Maven (Slides from TheServerSide Symposium)</a></td>
+            <td>TheServerSide</td>
+            <td>Vincent Massol</td>
+            <td>27 June 2003</td>
+          </tr>
+          <tr>
+            <td><a href="http://www.javaworld.com/javaworld/jw-10-2002/jw-1011-maven.html">Maven ties together tools for better code management</a></td>
+            <td>JavaWorld</td>
+            <td>Jeff Linwood</td>
+            <td>11 October 2002</td>
+          </tr>
+          <!--
+          <tr>
+            <td><a href="http://www.javausergroup.at/events/maven.pdf">The Stairway to Maven</a></td>
+            <td></td>
+            <td>Siegfried GOSCHL</td>
+            <td>26 June 2002</td>
+          </tr>
+          -->
+          <tr>
+            <td><a href="http://wiki.astrogrid.org/bin/view/Astrogrid/MakingWarWithMaven">How to get Maven to build your web service into a WAR on AstroGrid</a></td>
+            <td>Astrogrid</td>
+            <td></td>
+            <td></td>
+          </tr>
+          <tr>
+            <td><a href="http://wiki.astrogrid.org/bin/view/Astrogrid/MavenFAQ">Some Maven FAQs on AstroGrid</a></td>
+            <td>Astrogrid</td>
+            <td></td>
+            <td></td>
+          </tr>
+          <tr>
+            <td><a href="http://wiki.astrogrid.org/bin/view/Astrogrid/UsefulMavenNotes">Some Useful Maven Notes on AstroGrid</a></td>
+            <td>Astrogrid</td>
+            <td></td>
+            <td></td>
+          </tr>
+          <!--
+          <tr>
+            <td><a href="http://etudiant.univ-mlv.fr/~mvongvil/Maven_Intro.html">An Introduction to Maven (in French).</a></td>
+            <td></td>
+            <td></td>
+            <td></td>
+          </tr>
+          -->
+          <tr>
+            <td><a href="http://hotwork.sourceforge.net/hotwork/manual/maven/index.html">A tutorial for Maven, J2EE projects, and MevenIDE</a> (in Portuguese).</td>
+            <td></td>
+            <td></td>
+            <td></td>
+          </tr>
+        </table>
+      </div>
+    </section>
+  </body>
+</document>
diff --git a/doxia-test-docs/src/main/resources/maven-site/xdoc/developers/mojo-api-specification.xml b/doxia-test-docs/src/main/resources/maven-site/xdoc/developers/mojo-api-specification.xml
new file mode 100644
index 0000000..184abd3
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/xdoc/developers/mojo-api-specification.xml
@@ -0,0 +1,779 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document>
+  <properties>
+    <title>Mojo API Specification</title>
+    <author email="jdcasey at apache.org">John Casey</author>
+  </properties>
+  <body>
+    <section name="Introduction">
+      <p>Starting with Maven, plugins can be written in Java or any of a
+        number of scripting languages. Plugins consists of one or more Mojos,
+        each one being the implementation for one of the plugin's goals.
+        Maven tries to stay out of
+        the way of the programmer with its new Mojo API. This opens up the
+        opportunity for many Mojos to be reused outside of Maven, or bridged
+        into Maven from external systems like Ant.</p>
+      <p>NOTE: For now, we will limit the discussion to Java-based Mojos, since
+        each scripting language will present these same basic requirements with
+        various forms of implementation.</p>
+      <p>Although the requirements on Mojos are minimal by design, there are
+        still a very few requirements that Mojo developers must keep in mind. <!-- First, a Mojo must have a method named <code>execute</code> which
+        declares no parameters, and has a void return type. If this method
+        throws an exception, that exception must either be a derivative of
+        <code>java.lang.RuntimeException</code>, or a derivative of
+        <code>org.apache.maven.plugin.MojoExecutionException</code>. It goes
+        without saying that in the latter case, the execute method must declare
+        that it throws this exception. Additionally, Mojos must declare a field
+        for each parameter they specify, and these parameter fields will be
+        populated before execute() is called (Maven does support other
+        mechanisms for parameter injection, but they are not considered part of
+        the preferred practice, and are therefore a topic for an advanced Mojo
+        developer's guide). Finally, all Mojos must be accompanied by metadata
+        describing parameters, lifecycle bindings, etc. This descriptor will be
+        covered in more detail below.</p>
+      <p>While this will satisfy the requirements for execution as a Mojo
+        inside Maven, it is recommended that Mojos implement
+         --> Basically, these Mojo requirements are embodied by the
+        <code>org.apache.maven.plugin.Mojo</code>
+        interface, which the Mojo
+        must implement (or else extend its abstract base class counterpart
+        <code>org.apache.maven.plugin.AbstractMojo</code>
+        ). This interface
+        guarantees the correct execution contract for the Mojo: no parameters,
+        void return type, and a throws clause that allows only
+        <code>org.apache.maven.plugin.MojoExecutionException</code>
+        and its
+        derivatives. It also guarantees that the Mojo will have access to the
+        standard Maven user-feedback mechanism,
+        <code>org.apache.maven.monitor.logging.Log</code>
+        , so the Mojo can
+        communicate important events to the console or other log sink.<!-- Using the
+        Plugin/Mojo API will give that Mojo access to the Maven-integrated Log,
+        along with tighter integration into the Maven build. -->
+      </p>
+      <p>As mentioned before, each Plugin - or packaged set of Mojos - must
+        provide a descriptor called
+        <code>plugin.xml</code>
+        under the path
+        <code>META-INF/maven</code>
+        inside the Plugin jar file. Fortunately,
+        Maven also provides a set of Javadoc annotations and tools to generate
+        this descriptor, so developers don't have to worry about directly
+        authoring or maintaining a separate XML metadata file.
+      </p>
+      <p>To serve as a quick reference for the developer, the rest of this page
+        will document these features (the API, along with the annotations)
+        which are considered the best practice for developing Mojos.</p>
+    </section>
+    <section name="API Documentation">
+      <subsection name="org.apache.maven.plugin.Mojo">
+        <p>This interface forms the contract required for Mojos to interact
+          with the Maven infrastructure. It features an
+          <code>execute()</code>
+          method, which triggers the Mojo's build-process behavior, and can
+          throw a
+          <code>MojoExecutionException</code>
+          if an error condition
+          occurs. See below for a discussion on proper use of this
+          <code>Exception</code>
+          class. Also included is the
+          <code>setLog(..)</code>
+          method, which simply allows Maven to inject a
+          logging mechanism which will allow the Mojo to communicate to the
+          outside world through standard Maven channels.
+        </p>
+        <h6>Method Summary:</h6>
+        <ul>
+          <li>
+            <code>
+              void setLog( org.apache.maven.monitor.logging.Log )
+            </code>
+            <p>Inject a standard Maven logging mechanism to allow this Mojo
+              to communicate events and feedback to the user.</p>
+          </li>
+          <li>
+            <code>
+              void execute() throws org.apache.maven.plugin.MojoExecutionException
+            </code>
+            <p>Perform whatever build-process behavior this Mojo implements.
+              This is the main trigger for the Mojo inside the Maven system,
+              and allows the Mojo to communicate fatal errors by throwing an
+              instance of <code>MojoExecutionException</code>.
+            </p>
+            <p>The
+              <code>MojoExecutionException</code> (and all error
+              conditions inside the Mojo) should be handled very carefully.
+              The simple wrapping of lower-level exceptions without providing
+              any indication of a user-friendly probable cause is strictly
+              discouraged. In fact, a much better course of action is to
+              provide error handling code (try/catch stanzas) for each
+              coherent step within the Mojo's execution. Developers are then
+              in a much better position to diagnose the cause of any error,
+              and provide user-friendly feedback in the message of the
+              <code>MojoExecutionException</code>.
+            </p>
+          </li>
+        </ul>
+      </subsection>
+      <subsection name="org.apache.maven.plugin.AbstractMojo">
+        <p>Currently, this abstract base class simply takes care of managing
+          the Maven log for concrete derivations. In keeping with this, it
+          provides a
+          <code>protected</code>
+          method,
+          <code>getLog():Log</code>
+          ,
+          to furnish Log access to these concrete implementations.
+        </p>
+        <h6>Method Summary:</h6>
+        <ul>
+          <li>
+            <code>
+              public void setLog( org.apache.maven.monitor.logging.Log )
+            </code>
+            <p>
+              <b>[IMPLEMENTED]</b>
+            </p>
+            <p>Inject a standard Maven logging mechanism to allow this Mojo
+              to communicate events and feedback to the user.</p>
+          </li>
+          <li>
+            <code>protected Log getLog()</code>
+            <p>
+              <b>[IMPLEMENTED]</b>
+            </p>
+            <p>Furnish access to the standard Maven logging mechanism which
+              is managed in this base class.</p>
+          </li>
+          <li>
+            <code>
+              void execute() throws org.apache.maven.plugin.MojoExecutionException
+            </code>
+            <p>
+              <b>[ABSTRACT]</b>
+            </p>
+            <p>Perform whatever build-process behavior this Mojo implements.
+              See the documentation for
+              <code>Mojo</code>
+              above for more
+              information.
+            </p>
+          </li>
+        </ul>
+      </subsection>
+      <subsection name="org.apache.maven.monitor.logging.Log">
+        <p>This interface supplies the API for providing feedback to the user
+          from the Mojo, using standard Maven channels. There should be no big
+          surprises here, although you may notice that the methods accept
+          <code>java.lang.CharSequence</code>
+          rather than
+          <code>java.lang.String</code>
+          . This is provided mainly as a
+          convenience, to enable developers to pass things like
+          <code>StringBuffer</code>
+          directly into the logger, rather than
+          formatting first by calling
+          <code>toString()</code>
+          .
+        </p>
+        <h6>Method Summary:</h6>
+        <ul>
+          <li>
+            <code>void debug( java.lang.CharSequence )</code>
+            <p>Send a message to the user in the
+              <b>debug</b>
+              error level.
+            </p>
+          </li>
+          <li>
+            <code>
+              void debug( java.lang.CharSequence, java.lang.Throwable )
+            </code>
+            <p>Send a message (and accompanying exception) to the user in the
+              <b>debug</b>
+              error level. The error's stacktrace will be output
+              when this error level is enabled.
+            </p>
+          </li>
+          <li>
+            <code>void debug( java.lang.Throwable )</code>
+            <p>Send an exception to the user in the
+              <b>debug</b>
+              error level.
+              The stack trace for this exception will be output when this
+              error level is enabled.
+            </p>
+          </li>
+          <li>
+            <code>void info( java.lang.CharSequence )</code>
+            <p>Send a message to the user in the
+              <b>info</b>
+              error level.
+            </p>
+          </li>
+          <li>
+            <code>
+              void info( java.lang.CharSequence, java.lang.Throwable )
+            </code>
+            <p>Send a message (and accompanying exception) to the user in the
+              <b>info</b>
+              error level. The error's stacktrace will be output
+              when this error level is enabled.
+            </p>
+          </li>
+          <li>
+            <code>void info( java.lang.CharSequence )</code>
+            <p>Send an exception to the user in the
+              <b>info</b>
+              error level.
+              The stack trace for this exception will be output when this
+              error level is enabled.
+            </p>
+          </li>
+          <li>
+            <code>void warn( java.lang.CharSequence )</code>
+            <p>Send a message to the user in the
+              <b>warn</b>
+              error level.
+            </p>
+          </li>
+          <li>
+            <code>
+              void warn( java.lang.CharSequence, java.lang.Throwable )
+            </code>
+            <p>Send a message (and accompanying exception) to the user in the
+              <b>warn</b>
+              error level. The error's stacktrace will be output
+              when this error level is enabled.
+            </p>
+          </li>
+          <li>
+            <code>void warn( java.lang.CharSequence )</code>
+            <p>Send an exception to the user in the
+              <b>warn</b>
+              error level.
+              The stack trace for this exception will be output when this
+              error level is enabled.
+            </p>
+          </li>
+          <li>
+            <code>void error( java.lang.CharSequence )</code>
+            <p>Send a message to the user in the
+              <b>error</b>
+              error level.
+            </p>
+          </li>
+          <li>
+            <code>
+              void error( java.lang.CharSequence, java.lang.Throwable )
+            </code>
+            <p>Send a message (and accompanying exception) to the user in the
+              <b>error</b>
+              error level. The error's stacktrace will be output
+              when this error level is enabled.
+            </p>
+          </li>
+          <li>
+            <code>void error( java.lang.CharSequence )</code>
+            <p>Send an exception to the user in the
+              <b>error</b>
+              error level.
+              The stack trace for this exception will be output when this
+              error level is enabled.
+            </p>
+          </li>
+        </ul>
+      </subsection>
+    </section>
+    <section name="The Descriptor and Annotations">
+      <p>In addition to the normal Java requirements in terms of interfaces
+        and/or abstract base classes which need to be implemented, a plugin
+        descriptor must accompany these classes inside the plugin jar. This
+        descriptor file is used to provide metadata about the parameters and
+        other component requirements for a set of Mojos so that Maven can
+        initialize the Mojo and validate its configuration before executing
+        it. As such, the plugin descriptor has a certain set of information
+        that is required for each Mojo specification to be valid, as well as
+        requirements for the overall plugin descriptor itself.</p>
+      <p>NOTE: In the following discussion, bolded items are the descriptor's
+        element name along with a Javadoc annotation (if applicable) supporting
+        that piece of the plugin descriptor. A couple of examples are:
+        <b>someElement
+          (@annotation parameterName="parameterValue")</b>
+        or
+        <b>someOtherElement (@annotation <rawAnnotationValue>)</b>
+        .
+      </p>
+      <p>The plugin descriptor must be provided in a jar resource with the
+        path:
+        <code>META-INF/maven/plugin.xml</code>
+        , and it must contain the
+        following:
+      </p>
+      <table>
+        <tr>
+          <th>Descriptor Element</th>
+          <th>Required?</th>
+          <th>Notes</th>
+        </tr>
+        <tr>
+          <td>mojos</td>
+          <td>Yes</td>
+          <td>Descriptors for each Mojo provided by the plugin, each inside a
+            <b>mojo</b>
+            sub-element. Mojo descriptors are covered in detail
+            below. Obviously, a plugin without any declared Mojos doesn't
+            make sense, so the
+            <b>mojos</b>
+            element is required, along with
+            at least one
+            <b>mojo</b>
+            sub-element.
+          </td>
+        </tr>
+        <tr>
+          <td>dependencies</td>
+          <td>Yes</td>
+          <td>A set of dependencies which the plugin requires in order to
+            function. Each dependency is provided inside a
+            <b>dependency</b>
+            sub-element. Dependency specifications are covered below. Since
+            all plugins must have a dependency on
+            <code>maven-plugin-api</code>
+            , this element is effectively
+            required.
+            <i>Using the plugin toolset, these dependencies can be
+              extracted from the POM's dependencies.</i>
+          </td>
+        </tr>
+      </table>
+      <p>Each Mojo specified inside a plugin descriptor must provide the
+        following (annotations specified here are at the class level):</p>
+      <table>
+        <!-- Annotations listed by specific, autodetect and Javadoc, all alphabetical -->
+        <tr>
+          <th>Descriptor Element</th>
+          <th>Annotation</th>
+          <th>Required?</th>
+          <th>Notes</th>
+        </tr>
+        <tr>
+          <td>aggregator</td>
+          <td>@aggregator</td>
+          <td>No</td>
+          <td>Flags this Mojo to run it in a multi module way, i.e. aggregate the build with the set of
+            projects listed as modules.</td>
+        </tr>
+        <tr>
+          <td>configurator</td>
+          <td>@configurator <roleHint></td>
+          <td>No</td>
+          <td>The configurator type to use when injecting parameter values into this Mojo. The value is
+            normally deduced from the Mojo's implementation language, but can be specified to allow a
+            custom ComponentConfigurator implementation to be used. <i>NOTE: This will only be used in
+              very special cases, using a highly controlled vocabulary of possible values. (Elements
+              like this are why it's a good idea to use the descriptor tools.)</i>
+          </td>
+        </tr>
+        <tr>
+          <td>execute</td>
+          <td>
+            <ul>
+              <li>@execute phase="<phaseName>"
+                lifecycle="<lifecycleId>"</li>
+              <li>@execute phase="<phaseName>"</li>
+
+              <li>@execute goal="<goalName>"</li>
+            </ul>
+          </td>
+          <td>No</td>
+          <td>When this goal is invoked, it will first invoke a parallel lifecycle, ending at the given
+            phase. If a goal is provided instead of a phase, that goal will be executed in isolation.
+            The execution of either will not affect the current project, but instead make available the
+            <code>${executedProject}</code> expression if required. An alternate lifecycle can also be
+            provided: for more information see the documentation on the
+            <a href="../guides/introduction/introduction-to-the-lifecycle.html">build lifecycle</a>.</td>
+        </tr>
+        <tr>
+          <td>executionStrategy</td>
+          <td>@executionStrategy <strategy></td>
+          <td>No</td>
+          <td>Specify the execution strategy. <i>NOTE: Currently supports <b>once-per-session</b>,
+            <b>always</b>.</i>
+          </td>
+        </tr>
+        <tr>
+          <td>goal</td>
+          <td>@goal <goalName></td>
+          <td>Yes</td>
+          <td>The name for the Mojo that users will reference from the command line to execute the Mojo
+            directly, or inside a POM in order to provide Mojo-specific configuration.</td>
+        </tr>
+        <tr>
+          <td>inheritByDefault</td>
+          <td>@inheritByDefault <true|false></td>
+          <td>No. Default: <code>true</code></td>
+          <td>Specify that the Mojo is inherited.</td>
+        </tr>
+        <tr>
+          <td>instantiationStrategy </td>
+          <td>@instantiationStrategy  <per-lookup></td>
+          <td>No. Default: <code>per-lookup</code></td>
+          <td>Specify the instantiation strategy.</td>
+        </tr>
+        <tr>
+          <td>phase</td>
+          <td>@phase <phaseName></td>
+          <td>No</td>
+          <td>Binds this Mojo to a particular phase of the standard build lifecycle, if specified.
+            <i>NOTE: This is only required if this Mojo is to participate in the standard build
+              process.</i>
+          </td>
+        </tr>
+        <tr>
+          <td>requiresDependencyResolution</td>
+          <td>@requiresDependencyResolution <requiredScope></td>
+          <td>No</td>
+          <td>Flags this Mojo as requiring the dependencies in the specified scope (or an implied scope)
+            to be resolved before it can execute. Currently supports <code>compile</code>,
+              <code>runtime</code>, and <code>test</code> scopes. If this annotation is present but no scope
+              is specified, the scope defaults to <code>runtime</code>.
+          </td>
+        </tr>
+        <tr>
+          <td>requiresDirectInvocation</td>
+          <td>@requiresDirectInvocation <true|false></td>
+          <td>No. Default: <code>false</code></td>
+          <td>Flags this Mojo to be invoke directly.</td>
+        </tr>
+        <tr>
+          <td>requiresOnline</td>
+          <td>@requiresOnline <true|false></td>
+          <td>No. Default: <code>true</code></td>
+          <td>Flags this Mojo to be run in online mode.</td>
+        </tr>
+        <tr>
+          <td>requiresProject</td>
+          <td>@requiresProject <true|false></td>
+          <td>No. Default: <code>true</code></td>
+          <td>Flags this Mojo to run inside of a project.</td>
+        </tr>
+        <tr>
+          <td>requiresReports</td>
+          <td>@requiresReports <true|false></td>
+          <td>No. Default: <code>false</code></td>
+          <td>Flags this Mojo to require reports.</td>
+        </tr>
+
+        <!-- Autodetect -->
+        <tr>
+          <td>description</td>
+          <td>none (detected)</td>
+          <td>No</td>
+          <td>The description of this Mojo's functionality. <i>Using the toolset, this will be the
+            class-level Javadoc description provided. NOTE: While this is not a required part of the
+            Mojo specification, it SHOULD be provided to enable future tool support for browsing, etc.
+            and for clarity.</i>
+          </td>
+        </tr>
+        <tr>
+          <td>implementation</td>
+          <td>none (detected)</td>
+          <td>Yes</td>
+          <td>The Mojo's fully-qualified class name (or script path in the case of non-Java Mojos).</td>
+        </tr>
+        <tr>
+          <td>language</td>
+          <td>none (detected)</td>
+          <td>No. Default: <code>java</code></td>
+          <td>The implementation language for this Mojo (Java, beanshell, etc.).</td>
+        </tr>
+
+        <!-- Javadoc -->
+        <tr>
+          <td>deprecated</td>
+          <td>@deprecated <deprecated-text></td>
+          <td>No</td>
+          <td>Specify the version when the Mojo was deprecated to the API. Similar to Javadoc deprecated.
+            This will trigger a warning when a user tries to configure a parameter
+            marked as deprecated.</td>
+        </tr>
+        <tr>
+          <td>since</td>
+          <td>@since <since-text></td>
+          <td>No</td>
+          <td>Specify the version when the Mojo was added to the API. Similar to Javadoc since.</td>
+        </tr>
+      </table>
+      <p>Each Mojo specifies the parameters that it expects in order to work.
+        These parameters are the Mojo's link to the outside world, and
+        will be satisfied through a combination of POM/project values, plugin
+        configurations (from the POM and configuration defaults), and System
+        properties.</p>
+      <p>NOTE[1]: For this discussion on Mojo parameters, a single
+        annotation may span multiple elements in the descriptor's specification
+        for that parameter. Duplicate annotation declarations in this section
+        will be used to detail each parameter of an annotation separately.</p>
+      <p>NOTE[2]: In many cases, simply annotating a Mojo field with
+        <b>@parameter</b>
+        will be enough to allow injection of a value for that
+        parameter using POM configuration elements. The discussion below
+        shows advanced usage for this annotation, along with others.
+      </p>
+      <p>Each parameter for a Mojo must be specified in the
+        plugin descriptor as follows:</p>
+      <table>
+        <!-- Annotations listed by specific, autodetect and Javadoc, all alphabetical -->
+        <tr>
+          <th>Descriptor Element</th>
+          <th>Annotation</th>
+          <th>Required?</th>
+          <th>Notes</th>
+        </tr>
+        <tr>
+          <td>alias</td>
+          <td>@parameter alias="myAlias"</td>
+          <td>No</td>
+          <td>Specifies an alias which can be used to configure this parameter from the POM. This is
+            primarily useful to improve user-friendliness, where Mojo field names are not intuitive to
+            the user or are otherwise not conducive to configuration via the POM.</td>
+        </tr>
+        <tr>
+          <td>configuration</td>
+          <td>@component role="..." roleHint="..."</td>
+          <td>No</td>
+          <td> Populates the field with an instance of a Plexus component. This is like declaring a
+            <i>requirement</i> in a Plexus component. The default requirement will have a role equal
+            to the declared type of the field, and will use the default role hint. You can customise
+            either of these by providing a <code>role</code> and/or <code>roleHint</code> parameter.
+            <i>e.g.</i>
+            <code>@component role="org.apache.maven.artifact.ArtifactHandler"
+              roleHint="ear"</code><b>Note:</b> This is identical to the deprecated
+            form of parameter: <code>@parameter
+              expression="${component.yourpackage.YourComponentClass}"</code>. </td>
+        </tr>
+        <tr>
+          <td>configuration</td>
+          <td>@parameter expression="${someExpression}"
+            default-value="value"</td>
+          <td>No</td>
+          <td>Specifies the expression used to calculate the value to be injected into this parameter of
+            the Mojo at buildtime. This is commonly used to refer to specific elements in the POM, such
+            as ${project.resources}, which refers to the list of resources meant to accompany the
+            classes in the resulting jar file. The default value is used when the expression evaluates
+            to <code>null</code> . <i>NOTE: If not specified, an expression of ${<name>}
+              is assumed, which can only be satisfied from POM configuration or System properties. The
+              use of '${' and '}' is required to delimit actual expressions which may be evaluated.</i>
+          </td>
+        </tr>
+        <tr>
+          <td>editable</td>
+          <td>@readonly</td>
+          <td>No</td>
+          <td>Specifies that this parameter cannot be configured directly by the user (as in the case of
+            POM-specified configuration). This is useful when you want to force the user to use common
+            POM elements rather than plugin configurations, as in the case where you want to use the
+            artifact's final name as a parameter. In this case, you want the user to modify
+            <build><finalName/></build> rather than specifying
+            a value for finalName directly in the plugin configuration section. It is also useful to
+            ensure that - for example - a List-typed parameter which expects items of type Artifact
+            doesn't get a List full of Strings. <i>NOTE: Specification of this annotation flags the
+              parameter as non-editable; there is no true/false value.</i>
+          </td>
+        </tr>
+        <tr>
+          <td>required</td>
+          <td>@required</td>
+          <td>No</td>
+          <td>Whether this parameter is required for the Mojo to function. This is used to validate the
+            configuration for a Mojo before it is injected, and before the Mojo is executed from some
+            half-state. <i>NOTE: Specification of this annotation flags the parameter as required; there
+              is no true/false value.</i>
+          </td>
+        </tr>
+
+        <!-- Autodetect -->
+        <tr>
+          <td>description</td>
+          <td>none (detected)</td>
+          <td>No</td>
+          <td>The description of this parameter's use inside the Mojo. <i>Using the toolset, this is
+            detected as the Javadoc description for the field. NOTE: While this is not a required part
+            of the parameter specification, it SHOULD be provided to enable future tool support for
+            browsing, etc. and for clarity.</i>
+          </td>
+        </tr>
+        <tr>
+          <td>name</td>
+          <td>none (detected)</td>
+          <td>Yes</td>
+          <td>The name of the parameter, to be used in configuring this parameter from the Mojo's
+            declared defaults (discussed below) or from the POM. <i>Using the toolset, this is detected
+              as the Java field name.</i>
+          </td>
+        </tr>
+        <tr>
+          <td>type</td>
+          <td>none (detected)</td>
+          <td>Yes</td>
+          <td>The Java type for this parameter. This is used to validate the result of any expressions
+            used to calculate the value which should be injected into the Mojo for this parameter.
+            <i>Using the toolset, this is detected as the class of the Java field corresponding to
+              this parameter.</i>
+          </td>
+        </tr>
+
+        <!-- Javadoc -->
+        <tr>
+          <td>deprecated</td>
+          <td>@deprecated <deprecated-text></td>
+          <td>No</td>
+          <td>Specify the version when the Mojo was deprecated to the API. Similar to Javadoc deprecated.
+            This will trigger a warning when a user tries to configure a parameter
+            marked as deprecated.</td>
+        </tr>
+        <tr>
+          <td>since</td>
+          <td>@since <since-text></td>
+          <td>No</td>
+          <td>Specify the version when the Mojo was added to the API. Similar to Javadoc since.</td>
+        </tr>
+      </table>
+      <p>The final component of a plugin descriptor is the dependencies. This
+        enables the plugin to function independently of its POM (or at least
+        to declare the libraries it needs to run). Dependencies are taken from
+        the
+        <b>runtime</b>
+        scope of the plugin's calculated dependencies (from
+        the POM). Dependencies are specified in exactly the same manner as in
+        the POM, except for the <scope> element (all dependencies in the
+        plugin descriptor are assumed to be runtime, because this is a
+        runtime profile for the plugin).
+      </p>
+    </section>
+    <section name="Plugin Tools">
+      <p>By now, we've mentioned the plugin tools several times without telling
+        you what they are or how to use them. Instead of manually writing (and
+        maintaining) the metadata detailed above, Maven ships with some
+        tools to aid in this task. In fact, the only thing a plugin developer
+        needs to do is declare his project to be a plugin from within the POM.
+        Once this is done, Maven will call the appropriate descriptor
+        generators, etc. to produce an artifact that is ready for use within
+        Maven builds. Optional metadata can be injected via Javadoc annotation
+        (and possibly JDK5 annotations in the future) as described above,
+        enabling richer interactions between the Mojo and the user. The
+        section below describes the changes to the POM which are necessary to
+        create plugin artifacts.</p>
+    </section>
+    <section name="Project Descriptor (POM) Requirements">
+      <p>From the POM, Maven plugin projects look quite similar to any other
+        project. For pure Java plugins, the differences are even smaller than
+        for script-based plugins. The following details the POM elements
+        which are necessary to build a Maven plugin artifact.</p>
+      <table>
+        <tr>
+          <th>POM Element</th>
+          <th>Required for Java Mojos?</th>
+          <th>Sample Declaration</th>
+          <th>Notes</th>
+        </tr>
+        <tr>
+          <td>packaging</td>
+          <td>Yes</td>
+          <td>
+            <code><packaging>
+            maven-plugin
+            </packaging></code>
+          </td>
+          <td>The POM must declare a packaging element which describes this
+            project as a Maven plugin project.</td>
+        </tr>
+        <tr>
+          <td>scriptSourceDirectory</td>
+          <td>No</td>
+          <td>
+            <code><scriptSourceDirectory>
+            src/main/scripts
+            </scriptSourceDirectory></code>
+          </td>
+          <td>In the case of script-based Mojos (which are not covered in
+            detail within this document), the POM must include an additional
+            element to distinguish script sources from (optional) Java
+            supporting classes. This element is <code>scriptSourceDirectory</code>,
+            inside the <code>build</code> section. This directory is included in the list
+            of resources which accompany any compiled code in the resulting
+            artifact. It is specified separately from the resources in the
+            build section to denote its special status as an alternate source
+            directory for scripts.</td>
+        </tr>
+      </table>
+      <p>After making the changes above, the developer can simply call</p>
+      <source>mvn install</source>
+      <p>
+        to install the plugin to
+        the local repository. (Any of the other standard lifecycle targets like
+        package, deploy, etc. are also available in like fashion.)
+      </p>
+    </section>
+    <section name="IDE integration">
+      <p>If you're using JetBrains IntelliJ IDEA to develop your plugin,
+       you can use the following to configure the javadoc annotations as live
+       templates.</p>
+      <ol>
+        <li>Download <a href="./maven.xml">this file</a>,
+          and place it in $USER_HOME/.IntelliJIdea/config/templates</li>
+        <li>(re)startup IntelliJ IDEA (templates are loaded on startup)</li>
+        <li>add the following list to Settings -> IDE -> Errors -> General
+          -> Unknown javadoc tags -> Additional javadoc tags
+          <ul>
+            <li>aggregator, execute, goal, phase, requiresDirectInvocation,
+              requiresProject, requiresReports, requiresOnline, parameter,
+              component, required, readonly</li>
+          </ul>
+        </li>
+      </ol>
+    </section>
+    <section name="Resources">
+      <p>This section simply gives a listing of pointers for more
+        information.</p>
+      <ul>
+        <li>QDox Project (Javadoc annotations) [
+          <a href="http://qdox.codehaus.org">link</a>
+          ]
+        </li>
+        <li>Plexus Project (Plexus container) [
+          <a href="http://plexus.codehaus.org">link</a>
+          ]
+        </li>
+        <li>Maven Plugin Descriptor API [
+          <a href="http://maven.apache.org/ref/current/maven-plugin-descriptor/apidocs/index.html">link</a>
+          ]
+        </li>
+        <li>MojoDescriptor API [
+          <a href="http://maven.apache.org/ref/current/maven-plugin-descriptor/apidocs/org/apache/maven/plugin/descriptor/MojoDescriptor.html">link</a>
+          ]
+        </li>
+      </ul>
+    </section>
+  </body>
+</document>
diff --git a/doxia-test-docs/src/main/resources/maven-site/xdoc/docs-required.xml b/doxia-test-docs/src/main/resources/maven-site/xdoc/docs-required.xml
new file mode 100644
index 0000000..2687319
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/xdoc/docs-required.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0"?>
+<document>
+<!-- TODO: include back into site, after checking which are complete, add to instructions on how to contribute docs -->
+  <properties>
+    <title>Documentation Required</title>
+    <author email="brett at apache.org">Brett Porter</author>
+  </properties>
+  <body>
+    <section name="Documentation Required">
+      <p>
+        This page lists some of the most pressing documentation needs. If you feel something is missing, please be sure
+        to let us know at the
+        <a href="/mail-lists.html">Maven Users Mailing List</a>.
+      </p>
+      <h4>Documentation for users</h4>
+      <ul>
+        <li>new SNAPSHOT handling -
+          <i>partially done on Brett's blog</i>
+        </li>
+        <li>guide to dependencies, including:
+          <ul>
+            <li>dependency scoping - <i>especially system scope</i></li>
+            <li>version range specifications</li>
+            <li>conflict resolution</li>
+          </ul>
+        </li>
+        <li>deployment mechanism</li>
+        <li>dependency management</li>
+        <li>plugin management -
+          <i>in progress by J Matthew Pryor and John Casey</i>
+        </li>
+        <li>plugin configuration</li>
+        <li>plugin downloading -
+          <i>partial on Brett's blogs</i>
+        </li>
+        <li>report generation</li>
+        <li>project inheritence and company wide strategy</li>
+        <li>plugin documentation</li>
+        <li>using POM-properties in conjunction with plugin configuration to abstract child-POM configs</li>
+        <li>archetypes - writing and using</li>
+        <li>plugin usage guide/summary for core plugins - <i>esp. release plugin</i></li>
+        <li>strategy for migrating from Ant builds</li>
+        <li>strategy for migrating from Maven 1.x builds - <i>esp. plugins that don't convert directly</i></li>
+      </ul>
+      <h4>Documentation for plugin authors</h4>
+      <ul>
+        <li>Beanshell examples</li>
+        <li>plugin writing guide</li>
+        <li>report writing guide</li>
+        <li>site customisation guide</li>
+        <li>common tips and tricks for accessing project/build data</li>
+        <li>guide to available parameter expressions</li>
+        <li>list of standards for submitting new plugins (minimum documentation, test coverage?)</li>
+      </ul>
+      <h4>Documentation for Maven developers</h4>
+      <ul>
+        <li>Lifecycle architecture</li>
+        <li>High level architecture, components explanation</li>
+        <li>Intro to Plexus</li>
+        <li>contributors guide (add to current and consolidate, walk through some first steps)</li>
+      </ul>
+      <h4>Additional Notes</h4>
+      <pre>
+        Brett Porter wrote:
+        > The active project changes during the reactor build. At the compile
+        > stage, it refers to the target/classes directory. At the package
+        > stage, it points to the jar file. If you are implementing your own
+        > packaging goal, you need to call project.getArtifact().setFile( ... )
+        > to ensure this is used.
+
+        - multi module howto
+        - per user scm setup
+          - outline behaviour when connection and developConnection are defined
+
+           Our current URL looks like:
+           scm:cvs:pserver:@cvs.host.name:/cvsroot:module-version
+
+           Using this URL, the default is to connect to the CVS server using the
+           same userid as the current user.  This is the desired behavior and I
+           don't want to mess that up.  I just would like a means to add a userid
+           in the URL only in special circumstances like Continuum.
+
+           We might want to do something where we say it is a best practice to
+           specify a ${user} and take it from the environment and allow an override.
+
+        - testing a plugin
+        - How to get started behind an NTLM proxy.
+        - How snapshots works
+        - How do i use the lifecycle provided by a plugin:
+        - How do i disable ibiblio
+        - using version ranges
+        - plugin expressions (look at bob allison's work)
+        - overriding the central repository (use central)
+        - description of what packagings are available and how they work
+        - creating upload bundles
+        - modello example
+        - changing the snapshot policy frequency
+        - overriding central repo
+        - using POM info in applications: the POM is packaged so there is acccess
+          continuum example to get the version.
+        - quick description of scm,wagon,continuum        
+      </pre>
+      <h4>Profiles Example</h4>
+      <pre><![CDATA[
+it's possible to do it with m2 beta-1 and profiles.
+
+you declare profile in your pom like this:
+
+   <profiles>
+     <profile>
+       <id>env-test</id>
+       <activation>
+         <property>
+           <name>env</name>
+           <value>test</value>
+         </property>
+       </activation>
+       <properties>
+         <appProperties>test.properties</appProperties>
+       </properties>
+     </profile>
+     <profile>
+       <id>env-production</id>
+       <activation>
+         <property>
+           <name>env</name>
+           <value>production</value>
+         </property>
+       </activation>
+       <properties>
+         <appProperties>app.properties</appProperties>
+       </properties>
+     </profile>
+   </profiles>
+
+and you can run mvn with one of profiles like:
+mvn -P env-production clean:clean install ==> we use the profile id
+or
+mvn -Denv=test clean:clean install ==> we use the property env define in
+<activation>
+
+Emmanuel
+
+martin.kuhn at merkur.at wrote:
+> Hi,
+>
+> I'm a maven newbie and I try out maven 2.0 alpha 3.
+>
+> My questions:
+>
+> I have a project to build / deploy for three different enviroments (test,
+> integration, production).
+>
+> The difference beetween the bundles is only a properties file (there are
+> three different files: config-test.properties,
+> config-integration.properties ...).
+> In the build process I want to copy the right config file to a file with a
+> common name (config.properties -> the app should work with this config
+> file)
+>
+> Is there a solution to handle this with maven 2 or do I have to write a
+> plugin?
+>
+        ]]>
+      </pre>
+    </section>
+  </body>
+</document>
+
+
diff --git a/doxia-test-docs/src/main/resources/maven-site/xdoc/errors/404.xml b/doxia-test-docs/src/main/resources/maven-site/xdoc/errors/404.xml
new file mode 100644
index 0000000..aa2ed92
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/xdoc/errors/404.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document>
+  <properties>
+    <author email="brett at apache.org">Brett Porter</author>
+    <title>Page Not Found</title>
+  </properties>
+  <body>
+    <section name="Page Not Found">
+
+      <p>We're sorry, but the page you requested cannot be found. This may because:</p>
+    <ul>
+      <li>The page has moved, was outdated, or has not been created yet</li>
+      <li>You typed the address incorrectly</li>
+      <li>You followed a link from another site that pointed to this page.</li>
+    </ul>
+
+    <p>If you came to this page by following a broken link on our site, you can report the <a href="http://jira.codehaus.org/browse/MNGSITE">problem</a>.</p>
+
+    </section>
+  </body>
+</document>
diff --git a/doxia-test-docs/src/main/resources/maven-site/xdoc/index.xml.vm b/doxia-test-docs/src/main/resources/maven-site/xdoc/index.xml.vm
new file mode 100644
index 0000000..c318fc2
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/xdoc/index.xml.vm
@@ -0,0 +1,195 @@
+<?xml version="1.0"?>
+<document>
+  <properties>
+    <title>Welcome to Maven</title>
+    <author email="brett at apache.org">Brett Porter</author>
+    <author email="jason at sonatype.com">Jason van Zyl</author>
+  </properties>
+  <body>
+
+    <!-- TODO: news? -->
+    <div id="downloadbox">
+    <h5>Search Maven Sites</h5>
+<!-- Google CSE Search Box Begins  -->
+<form action="http://www.google.com/cse" id="searchbox_006660305041243700248:hyqtfwsewpm">
+  <input type="hidden" name="cx" value="006660305041243700248:hyqtfwsewpm" />
+  <input type="text" name="q" size="25" />
+  <input type="submit" name="sa" value="Search" />
+</form>
+<script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=searchbox_006660305041243700248%3Ahyqtfwsewpm"></script>
+<!-- Google CSE Search Box Ends -->
+
+	<h5>Get Maven ${currentVersion}</h5>
+      <span style="display: block; text-align: right; font-size: smaller">Released: 10 April 2008</span>
+      <p>
+        <a href="download.html">
+          <img src="images/folder-open.gif" border="0" alt="" title="Download Maven ${currentVersion}"/>
+          Maven ${currentVersion}
+        </a>
+        <small>(1.6Mb)</small>
+        <span style="font-size: smaller">
+          <br/>
+          <a href="download.html#Requirements">System Requirements</a>,
+          <a href="download.html#Installation">Installation Instructions</a>,
+          <a href="release-notes.html">Release Notes</a>
+        </span>
+      </p>
+      <p>
+        <a href="download.html">
+          <img src="images/folder-open.gif" border="0" alt="" title="Download Maven Tasks for Ant 2.0.9"/>
+          Maven Tasks for Ant 2.0.9
+        </a>
+        <small>(994k)</small>
+        <span style="font-size: smaller">
+          <br/>
+          <a href="ant-tasks/index.html">Documentation</a>,
+          <a href="ant-tasks/release-notes.html">Release Notes</a>
+        </span>
+      </p>
+
+      <!-- TODO: we should use the SSI instead, but two things prevent it: a) the SSI's aren't working on apache.org yet so I can't test it; b) SSI's get eliminated from xdoc. For some reason even inside CDATA they are escaped. -->
+      <h5>Looking for Artifacts?</h5>
+      <p><a href="http://repository.sonatype.org">Search Central</a> and other Public Repositories.</p>
+      <h5>Looking for Repository Managers?</h5>
+      <p><a href="http://maven.apache.org/repository-management.html">Take a look</a> at the Repository Managers available from the community.</p>
+      <h5>Looking for CI Servers?</h5>
+      <p><a href="http://maven.apache.org/continuous-integration.html">Take a look</a> at the CI Servers available from the community.</p>
+
+      <h5>Supporting Maven</h5>
+      <p>We'd like to <a href="http://www.apache.org/foundation/thanks.html">thank the sponsors of the Apache Software Foundation</a> for their support and assistance. To support the ASF, see the <a href="http://www.apache.org/foundation/sponsorship.html">sponsorship</a> page.</p>
+    </div>
+    <section name="Welcome to Maven">
+
+      <!-- TODO: I reckon it's time for a new description -->
+      <p>
+        Maven is a software project management and comprehension tool. Based on the concept of a project object model
+        (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
+      </p>
+      <p>
+        If you think that Maven could help your project, you can find out more information about in the "About Maven"
+        section of the navigation. This includes an in-depth description of <a href="what-is-maven.html">what Maven is</a>,
+        a <a href="maven-features.html">list of some of its main features</a>, and a set of <a href="general.html">frequently
+        asked questions about what Maven is</a>.
+      </p>
+      <h3>Learning about Maven</h3>
+      <p>
+        <!-- TODO: this could be the big button type thing instead of a list of links -->
+        This site is separated into the following sections, depending on how you'd like to use Maven:
+      </p>
+      <!-- TODO: use CSS -->
+      <ul>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="run-maven/index.html">Run Maven</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those needing to build a project that uses Maven
+          </span>
+        </li>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="users/index.html">Use Maven</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those wanting to use Maven to build their project, including a "10 minute test" that gives a
+            practical overview of Maven's main features in just 10 minutes
+          </span>
+        </li>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="plugin-developers/index.html">Write Maven Plugins</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those who may or may not be using Maven, but want to provide a plugin for shared
+            functionality or to accompany their own product or toolset
+          </span>
+        </li>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="repository/index.html">Improve the Maven Repository</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those who may or may not use, but are interested in getting project metadata into the
+            repository
+          </span>
+        </li>
+        <li>
+          <span style="white-space:nowrap; font-weight: bold; font-size: 1.25em">
+            <a href="developers/index.html">Develop Maven</a>
+          </span>
+          <span style="display: block; margin-bottom: 0.5em">
+            Information for those who are currently developers, or who are interested
+            in contributing to the Maven project itself
+          </span>
+        </li>
+      </ul>
+      <p>
+        Each guide is divided into a number of trails to get you started on a particular topic, and includes a
+        reference area and a "cookbook" of common examples.
+      </p>
+      <p>
+        You can access the guides at any time from the left navigation.
+      </p>
+      <h3>Documentation Index</h3>
+      <p>
+        If you are looking for a quick reference, you can use the <a href="guides/index.html">documentation index.</a>
+<!-- TODO
+        If you are looking for a quick reference, you can use the documentation index. It is available in both
+        <a href="todo.html">alphabetical</a> and <a href="todo.html">categorical</a> listing formats.
+-->
+      </p>
+      <h3>Plugins</h3>
+<!-- TODO
+      <p>
+        Maven functionality is provided by plugins. For an explanation of how plugins work, and basic information on how
+        to use a plugin, see the <a href="todo.html">introduction to plugins</a> in the Users Centre.
+      </p>
+-->
+      <p>
+        For detailed information on just some of the plugins available for Maven, see the
+        <a href="plugins/index.html">plugin list</a>.
+      </p>
+<!-- TODO: Should these be here, or just in the user centre?
+      <h3>Converting from a different Build System</h3>
+      <p>
+        If you are currently using a different build system, there are options for converting from that to Maven 2
+        either partially or completely. These guides also give an overview of the differences between Maven and the
+        other build system. The following guides are available in the Users Centre:
+      </p>
+      <ul>
+        <li><a href="todo.html">Converting from Ant to Maven 2</a></li>
+        <li><a href="todo.html">Converting from Maven 1.x to Maven 2</a></li>
+        <li><a href="todo.html">Adding Maven 2 to an IDE based build</a></li>
+      </ul>
+-->
+      <h3>How to Get Support</h3>
+      <p>
+        Support for Maven is available in a variety of different forms.
+      </p>
+      <p>
+        To get started, search the documentation, the <a href="http://docs.codehaus.org/display/MAVENUSER">wiki</a>,
+        <a href="issue-tracking.html">issue tracker</a>, or the <a href="mail-lists.html">mailing list archives</a> to
+        see if the problem has been solved or reported before.
+      </p>
+      <p>
+        If the problem has not been reported before, the recommended way to get help is to
+        subscribe to the <a href="mail-lists.html">Maven Users Mailing list</a>. Many other users and Maven developers
+        will answer your questions there, and the answer will be archived for others in the future.
+      </p>
+      <p>
+        You can also reach the Maven developers on <a href="community.html">IRC</a>.
+      </p>
+
+      <h3>How can I help?</h3>
+      <p>
+        Maven is an open source community and welcomes contributions. If you'd like to get involved, see the
+        <a href="guides/development/guide-helping.html">Guide to helping with Maven</a>.
+      </p>
+      <p>
+        Maven is a part of the Apache Software Foundation. We'd like to <a href="http://www.apache.org/foundation/thanks.html">thank
+        the sponsors</a> that provide financial assistance to the foundation. For more information on how you can support the
+        foundation, see the <a href="http://www.apache.org/foundation/sponsorship.html">sponsorship</a> page.
+      </p>
+    </section>
+  </body>
+</document>
diff --git a/doxia-test-docs/src/main/resources/maven-site/xdoc/source-repository.xml b/doxia-test-docs/src/main/resources/maven-site/xdoc/source-repository.xml
new file mode 100644
index 0000000..4a39b80
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-site/xdoc/source-repository.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<document>
+  <properties>
+    <title>Source Repository</title>
+    <author>Benjamin Bentmann</author>
+  </properties>
+  <body>
+    <section name="Source Repository">
+      <p>
+        Maven projects use <a href="http://subversion.tigris.org/">Subversion</a> to manage their source code.
+        Instructions on Subversion use can be found in the online book
+        <a href="http://svnbook.red-bean.com/">Version Control with Subversion</a>.
+      </p>
+      <subsection name="Web Access">
+        <p>
+          The following list shows the links to the online source repositories for the various development branches of
+          the Maven core:
+        </p>
+        <source>
+<a href="http://svn.apache.org/viewvc/maven/components/branches/maven-2.0.x">http://svn.apache.org/viewvc/maven/components/branches/maven-2.0.x</a>&#xA0;<!-- protected space to force line break -->
+<a href="http://svn.apache.org/viewvc/maven/components/branches/maven-2.1.x">http://svn.apache.org/viewvc/maven/components/branches/maven-2.1.x</a>&#xA0;<!-- protected space to force line break -->
+<a href="http://svn.apache.org/viewvc/maven/components/trunk">http://svn.apache.org/viewvc/maven/components/trunk</a></source>
+        <p>
+          The source repositories for the various plugins are listed in the documentation of the respective plugin,
+          reachable via the <a href="plugins/index.html">plugin index</a>.
+        </p>
+      </subsection>
+      <subsection name="Anonymous Access">
+        <p>
+          The source can be checked out anonymously from SVN with one of these commands depending on the development
+          line you are looking for:
+        </p>
+        <source>
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/branches/maven-2.0.x maven-2.0.x
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/branches/maven-2.1.x maven-2.1.x
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/trunk maven-3.0.x</source>
+      </subsection>
+      <subsection name="Developer Access">
+        <p>
+          Everyone can access the Subversion repository via HTTP, but committers must checkout the Subversion
+          repository via HTTPS to gain write access:
+        </p>
+        <source>
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/branches/maven-2.0.x maven-2.0.x
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/branches/maven-2.1.x maven-2.1.x
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/trunk maven-3.0.x</source>
+        <p>
+          To commit changes to the repository, execute the following command to commit your changes (<code>svn</code> will
+          prompt you for your password):
+        </p>
+        <source>
+$ svn commit --username your-username -m "A message"</source>
+      </subsection>
+      <subsection name="Access from behind a Firewall">
+        <p>
+          For those users who are stuck behind a corporate firewall which is blocking HTTP access to the Subversion
+          repository, you can try to access it via the developer connection:
+        </p>
+        <source>
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/branches/maven-2.0.x maven-2.0.x
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/branches/maven-2.1.x maven-2.1.x
+$ svn checkout http://svn.apache.org/repos/asf/maven/components/trunk maven-3.0.x</source>
+      </subsection>
+      <subsection name="Access through a Proxy">
+        <p>
+          The Subversion client can go through a proxy, if you configure it to do so. First, edit your <code>servers</code>
+          configuration file to indicate which proxy to use. The file's location depends on your operating system. On
+          Linux or Unix it is located in the directory <code>~/.subversion</code>. On Windows it is in <code>%APPDATA%\Subversion</code>
+          (try <code>echo %APPDATA%</code>, note this is a hidden directory).
+        </p>
+        <p>
+          There are comments in the file explaining what to do. If you don't have that file, get the latest Subversion
+          client and run any command; this will cause the configuration directory and template files to be created.
+        </p>
+        <p>
+          Example: Edit the <code>servers</code> file and add something like:
+        </p>
+        <source>
+[global]
+http-proxy-host = your.proxy.name
+http-proxy-port = 3128</source>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/doxia-test-docs/src/main/resources/maven-source-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-source-plugin/fml/faq.fml
new file mode 100644
index 0000000..37a0030
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-source-plugin/fml/faq.fml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="How can I generate a source jar of the test classes">
+      <question>How can I generate a source jar of the test classes?</question>
+      <answer>
+        <p>
+        Use the source:test-jar goal to generate a jar file that contains the project test sources.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-stage-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-stage-plugin/fml/faq.fml
new file mode 100644
index 0000000..91d7c7a
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-stage-plugin/fml/faq.fml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="Why isn't my username being picked up when I specify a repositoryId?">
+      <question>Why isn't my username being picked up when I specify a repositoryId?</question>
+      <answer>
+        <p>
+          The plugin <i>will</i> pick up the username/password from your
+          <code>settings.xml</code> file. Make sure that you are using the same
+          <code>id</code> in your <code>settings.xml</code> that you are using
+          on the command line.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-verifier-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-verifier-plugin/fml/faq.fml
new file mode 100644
index 0000000..0552a80
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-verifier-plugin/fml/faq.fml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+  <part id="General">
+    <faq id="Can it verify other things like URLs">
+      <question>Can it verify other things like URLs?</question>
+      <answer>
+        <p>
+        No.
+        </p>
+      </answer>
+    </faq>
+  </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/maven-war-plugin/fml/faq.fml b/doxia-test-docs/src/main/resources/maven-war-plugin/fml/faq.fml
new file mode 100644
index 0000000..a80bd16
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/maven-war-plugin/fml/faq.fml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<faqs id="FAQ" title="Frequently Asked Questions">
+ <part id="General">
+   <faq id="filtering">
+     <question>How is filtering done in the WAR plugin?</question>
+     <answer>
+       <p>To enable filtering of <code>web.xml</code> you must configure the war plugin as this:
+         <source><![CDATA[
+      <plugin>
+        <artifactId>maven-war-plugin</artifactId>
+        <version>2.1-alpha-2</version>
+        <configuration>
+          <filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
+        </configuration>
+      </plugin>
+]]></source>
+       </p>
+       <p>Examples can be found <a href="examples/adding-filtering-webresources.html">here</a>.</p>
+     </answer>
+   </faq>
+   <faq id="classifieruse">
+     <question>How does the classifier affect artifacts in my war project?</question>
+     <answer>
+       <p>When used, the copy of the artifact in your project will have the classifier appended to its
+       filename. This can be used to differentiate duplicate artifacts.</p>
+     </answer>
+   </faq>
+   <faq id="transitiveexclude">
+     <question>How do I exclude transitive dependencies from my project?</question>
+     <answer>
+       <p>Give it a "provided" scope.</p>
+     </answer>
+   </faq>
+   <faq id="dependentwarexclude">
+     <question>What's the difference between using dependentWarExclude and provided scope?</question>
+     <answer>
+       <p><code>dependentWarExclude</code> is used in <a href="overlays.html">war overlays</a> for excluding dependent
+       war files from being included in the exploded war.</p>
+     </answer>
+   </faq>
+   <faq id="webresourcesexclude">
+     <question>How do I exclude files in my web resources?</question>
+     <answer>
+       <p>Use the <code>webResources</code> <code>exclude</code> parameter to identify the tokens to use for the filtering.</p>
+     </answer>
+   </faq>
+   <faq id="waroverlaysexclude">
+     <question>How do I exclude files when doing war overlays?</question>
+     <answer>
+       <p>Use the <code>dependentWarExcludes</code> parameter to identify the tokens to use for the filtering.</p>
+       <p>For more information refer to <a href="examples/adding-filtering-webresources.html">Adding and Filtering
+       External Web Resources</a>.</p>
+     </answer>
+   </faq>
+   <faq id="configurationdoc">
+     <question>Where can I find the documentation for the plugin's configuration?</question>
+     <answer>
+       <p>
+         For each goal, you can use the documentation from the <a href="plugin-info.html">plugin site</a>.
+       </p>
+       <p>
+         If you need a specific version, generate it using <code>mvn site:site</code> or use:
+         <source><![CDATA[
+mvn help:describe -DgroupId=org.apache.maven.plugins -DartifactId=maven-war-plugin \
+  -Dfull=true -Dversion=<your-plugin-version-here>
+]]></source>
+       </p>
+     </answer>
+   </faq>
+   <faq id="attached">
+     <question>How do I create a jar containing the classes in my webapp?</question>
+       <answer>
+       <p>If you would simply like to package the classes and resources as a jar in <code>WEB-INF/lib</code>
+           rather than as loose files under <code>WEB-INF/classes</code>, use the following configuration:</p>
+       <p>
+         <source><![CDATA[
+       <plugin>
+         <artifactId>maven-war-plugin</artifactId>
+         <version>X.Y</version>
+         <configuration>
+           <archiveClasses>true</archiveClasses>
+         </configuration>
+       </plugin>
+]]></source>
+       </p>
+       <p>If you need to re-use this jar in another project, the recommended approach is to move the classes to a
+       separate module that builds a jar, and then declare a dependency on that jar from your webapp as well as from
+       any other projects that need it.</p>
+       <p>If you can't move the classes to another project, you can deploy the classes and resources included in
+           your webapp as an "attached" artifact, with a classifier, by using the following configuration:</p>
+       <p>
+         <source><![CDATA[
+<project>
+  ...
+  <artifactId>mywebapp</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  ...
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-war-plugin</artifactId>
+        <version>X.Y</version>
+        <configuration>
+          <attachClasses>true</attachClasses>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  ...
+</project>
+]]></source>
+       </p>
+       <p>This will result in two artifacts being deployed: <code>mywebapp-1.0-SNAPSHOT.war</code>
+       and <code>mywebapp-1.0-SNAPSHOT-classes.jar</code>.</p>
+     </answer>
+   </faq>
+ </part>
+</faqs>
\ No newline at end of file
diff --git a/doxia-test-docs/src/main/resources/pom-4.0.0.xml b/doxia-test-docs/src/main/resources/pom-4.0.0.xml
new file mode 100644
index 0000000..be8af2e
--- /dev/null
+++ b/doxia-test-docs/src/main/resources/pom-4.0.0.xml
@@ -0,0 +1,158 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<!-- START SNIPPET: superpom -->
+<project>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven</groupId>
+  <artifactId>super-pom</artifactId>
+  <version>3.0-SNAPSHOT</version>
+  <name>Maven Default Project</name>
+  <repositories>
+    <repository>
+      <id>central</id>
+      <name>Maven Repository Switchboard</name>
+      <layout>default</layout>
+      <url>http://repo1.maven.org/maven2</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+  </repositories>
+
+  <pluginRepositories>
+    <pluginRepository>
+      <id>central</id>
+      <name>Maven Plugin Repository</name>
+      <url>http://repo1.maven.org/maven2</url>
+      <layout>default</layout>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <releases>
+        <updatePolicy>never</updatePolicy>
+      </releases>
+    </pluginRepository>
+  </pluginRepositories>
+
+  <build>
+    <directory>${project.basedir}/target</directory>
+    <outputDirectory>${project.build.directory}/classes</outputDirectory>
+    <finalName>${project.artifactId}-${project.version}</finalName>
+    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
+    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
+    <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
+    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
+    <resources>
+      <resource>
+        <directory>${project.basedir}/src/main/resources</directory>
+      </resource>
+    </resources>
+    <testResources>
+      <testResource>
+        <directory>${project.basedir}/src/test/resources</directory>
+      </testResource>
+    </testResources>
+    <pluginManagement>
+       <plugins>
+         <plugin>
+           <artifactId>maven-antrun-plugin</artifactId>
+           <version>1.1</version>
+         </plugin>       
+         <plugin>
+           <artifactId>maven-assembly-plugin</artifactId>
+           <version>2.2-beta-1</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-clean-plugin</artifactId>
+           <version>2.2</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-compiler-plugin</artifactId>
+           <version>2.0.2</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-dependency-plugin</artifactId>
+           <version>2.0</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-deploy-plugin</artifactId>
+           <version>2.3</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-ear-plugin</artifactId>
+           <version>2.3.1</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-ejb-plugin</artifactId>
+           <version>2.1</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-install-plugin</artifactId>
+           <version>2.2</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-jar-plugin</artifactId>
+           <version>2.2</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-javadoc-plugin</artifactId>
+           <version>2.4</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-plugin-plugin</artifactId>
+           <version>2.4.2</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-rar-plugin</artifactId>
+           <version>2.2</version>
+         </plugin>
+         <plugin>                
+           <artifactId>maven-release-plugin</artifactId>
+           <version>2.0-beta-7</version>
+         </plugin>
+         <plugin>                
+           <artifactId>maven-resources-plugin</artifactId>
+           <version>2.2</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-site-plugin</artifactId>
+           <version>2.0-beta-7</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-source-plugin</artifactId>
+           <version>2.0.4</version>
+         </plugin>         
+         <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <version>2.4.3</version>
+         </plugin>
+         <plugin>
+           <artifactId>maven-war-plugin</artifactId>
+           <version>2.1-alpha-1</version>
+         </plugin>
+       </plugins>
+     </pluginManagement>
+  </build>
+
+  <reporting>
+    <outputDirectory>${project.build.directory}/site</outputDirectory>
+  </reporting>
+</project>
+  <!-- END SNIPPET: superpom -->
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..5f9dee3
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,510 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven</groupId>
+    <artifactId>maven-parent</artifactId>
+    <version>16</version>
+    <relativePath>../../pom/maven/pom.xml</relativePath>
+  </parent>
+
+  <groupId>org.apache.maven.doxia</groupId>
+  <artifactId>doxia</artifactId>
+  <version>1.1.4</version>
+  <packaging>pom</packaging>
+
+  <name>Doxia</name>
+  <description>Doxia is a content generation framework that provides powerful techniques for generating static and dynamic content, supporting a variety of markup languages.</description>
+  <url>http://maven.apache.org/doxia/doxia</url>
+  <inceptionYear>2005</inceptionYear>
+
+  <mailingLists>
+    <mailingList>
+      <name>Doxia Developer List</name>
+      <post>doxia-dev at maven.apache.org</post>
+      <subscribe>doxia-dev-subscribe at maven.apache.org</subscribe>
+      <unsubscribe>doxia-dev-unsubscribe at maven.apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/maven-doxia-dev/</archive>
+      <otherArchives>
+        <otherArchive>http://www.mail-archive.com/doxia-dev@maven.apache.org</otherArchive>
+        <otherArchive>http://www.nabble.com/Doxia---dev-f11816.html</otherArchive>
+        <otherArchive>http://markmail.org/list/org.apache.maven.doxia-dev</otherArchive>
+      </otherArchives>
+    </mailingList>
+    <mailingList>
+      <name>Doxia User List</name>
+      <post>doxia-users at maven.apache.org</post>
+      <subscribe>doxia-users-subscribe at maven.apache.org</subscribe>
+      <unsubscribe>doxia-users-unsubscribe at maven.apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/maven-doxia-users/</archive>
+      <otherArchives>
+        <otherArchive>http://www.mail-archive.com/doxia-users@maven.apache.org</otherArchive>
+        <otherArchive>http://www.nabble.com/Doxia---Users-f14483.html</otherArchive>
+        <otherArchive>http://markmail.org/list/org.apache.maven.doxia-users</otherArchive>
+      </otherArchives>
+    </mailingList>
+    <mailingList>
+      <name>Doxia Commits List</name>
+      <subscribe>doxia-commits-subscribe at maven.apache.org</subscribe>
+      <unsubscribe>doxia-commits-unsubscribe at maven.apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/maven-doxia-commits/</archive>
+      <otherArchives>
+        <otherArchive>http://www.mail-archive.com/doxia-commits@maven.apache.org</otherArchive>
+        <otherArchive>http://markmail.org/list/org.apache.maven.doxia-commits</otherArchive>
+      </otherArchives>
+    </mailingList>
+    <mailingList>
+      <name>Maven Issues List</name>
+      <subscribe>issues-subscribe at maven.apache.org</subscribe>
+      <unsubscribe>issues-unsubscribe at maven.apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/maven-issues/</archive>
+      <otherArchives>
+        <otherArchive>http://www.mail-archive.com/issues@maven.apache.org</otherArchive>
+        <otherArchive>http://www.nabble.com/Maven---Issues-f15573.html</otherArchive>
+        <otherArchive>http://markmail.org/list/org.apache.maven.issues</otherArchive>
+      </otherArchives>
+    </mailingList>
+  </mailingLists>
+
+  <prerequisites>
+    <maven>2.0.6</maven>
+  </prerequisites>
+
+  <modules>
+    <module>doxia-logging-api</module>
+    <module>doxia-sink-api</module>
+    <module>doxia-test-docs</module>
+    <module>doxia-core</module>
+    <module>doxia-modules</module>
+    <module>doxia-book</module>
+    <module>doxia-maven-plugin</module>
+  </modules>
+
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/maven/doxia/doxia/tags/doxia-1.1.4</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/maven/doxia/doxia/tags/doxia-1.1.4</developerConnection>
+    <url>http://svn.apache.org/viewcvs.cgi/maven/doxia/doxia/tags/doxia-1.1.4</url>
+  </scm>
+  <issueManagement>
+    <system>jira</system>
+    <url>http://jira.codehaus.org/browse/DOXIA</url>
+  </issueManagement>
+  <distributionManagement>
+    <site>
+      <id>apache.website</id>
+      <url>scp://people.apache.org/www/maven.apache.org/doxia/doxia</url>
+    </site>
+  </distributionManagement>
+
+  <properties>
+    <projectVersion>${project.version}</projectVersion>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <!-- doxia -->
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-sink-api</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-logging-api</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-test-docs</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-core</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-core</artifactId>
+        <version>${projectVersion}</version>
+        <type>test-jar</type>
+      </dependency>
+
+      <!-- doxia modules -->
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-apt</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-confluence</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-docbook-simple</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-fml</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-fo</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-latex</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-itext</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-rtf</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-twiki</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-xdoc</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-module-xhtml</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+
+      <!-- Others -->
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-book</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.doxia</groupId>
+        <artifactId>doxia-maven-plugin</artifactId>
+        <version>${projectVersion}</version>
+      </dependency>
+
+      <!-- Plexus -->
+      <dependency>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-container-default</artifactId>
+        <version>1.0-alpha-30</version>
+      </dependency>
+      <dependency>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-utils</artifactId>
+        <version>1.5.12</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.2</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+      </resource>
+      <resource>
+        <directory>${build.directory}/generated-site/xsd</directory>
+        <includes>
+          <include>**/*.xsd</include>
+        </includes>
+      </resource>
+    </resources>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-antrun-plugin</artifactId>
+          <version>1.3</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-release-plugin</artifactId>
+          <configuration>
+            <tagBase>https://svn.apache.org/repos/asf/maven/doxia/doxia/tags</tagBase>
+          </configuration>
+        </plugin>
+        <!-- TODO need to upgrade to last version or Maven parent version -->
+        <plugin>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-maven-plugin</artifactId>
+          <!-- Version 1.3.8 makes the descriptor merges fail, probably due to PLX-409 -->
+          <version>1.3.5</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.modello</groupId>
+          <artifactId>modello-maven-plugin</artifactId>
+          <version>1.1</version>
+        </plugin>
+        <plugin>
+          <artifactId>maven-project-info-reports-plugin</artifactId>
+          <version>2.1.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>clirr-maven-plugin</artifactId>
+          <version>2.2.2</version>
+          <configuration>
+            <comparisonVersion>1.1</comparisonVersion>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-resources-plugin</artifactId>
+          <version>2.4.2</version>
+        </plugin>        
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>2.7</version>
+          <configuration>
+            <source>1.4</source>
+            <links>
+              <link>http://java.sun.com/j2se/1.4.2/docs/api/</link>
+              <link>http://plexus.codehaus.org/plexus-utils/apidocs/</link>
+              <link>http://junit.sourceforge.net/javadoc/</link>
+            </links>
+            <tagletArtifacts>
+              <tagletArtifact>
+                <groupId>org.codehaus.plexus</groupId>
+                <artifactId>plexus-javadoc</artifactId>
+                <version>1.0</version>
+              </tagletArtifact>
+            </tagletArtifacts>
+          </configuration>
+        </plugin>
+        <plugin>
+          <artifactId>maven-site-plugin</artifactId>
+          <configuration>
+            <stagingSiteURL>scp://people.apache.org/www/maven.apache.org/doxia/doxia-${project.version}</stagingSiteURL>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>descriptor</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>verify</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <artifactId>maven-project-info-reports-plugin</artifactId>
+        <version>2.1.2</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+  <profiles>
+    <profile>
+      <!-- AbstractXmlParser.CachedFileEntityResolver downloads DTD/XSD files in ${java.io.tmpdir} -->
+      <id>remove-temp</id>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>clean-download</id>
+                <phase>clean</phase>
+                <configuration>
+                  <tasks>
+                    <delete>
+                      <fileset dir="${java.io.tmpdir}" includes="**/*.dtd, **/*.ent, **/*.xsd" />
+                    </delete>
+                  </tasks>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>reporting</id>
+      <reporting>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <configuration>
+              <source>1.4</source>
+              <links>
+                <link>http://java.sun.com/j2se/1.4.2/docs/api/</link>
+                <link>http://plexus.codehaus.org/plexus-utils/apidocs/</link>
+                <link>http://junit.sourceforge.net/javadoc/</link>
+              </links>
+              <tagletArtifacts>
+                <tagletArtifact>
+                  <groupId>org.codehaus.plexus</groupId>
+                  <artifactId>plexus-javadoc</artifactId>
+                  <version>1.0</version>
+                </tagletArtifact>
+              </tagletArtifacts>
+            </configuration>
+          </plugin>
+          <plugin>
+            <artifactId>maven-checkstyle-plugin</artifactId>
+            <configuration>
+              <configLocation>http://svn.apache.org/repos/asf/maven/doxia/doxia/trunk/src/main/resources/config/doxia_checkstyle.xml</configLocation>
+            </configuration>
+          </plugin>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>clirr-maven-plugin</artifactId>
+          </plugin>
+        </plugins>
+      </reporting>
+    </profile>
+    <profile>
+      <id>reporting-aggregate</id>
+      <!-- To generate aggregate reports -->
+      <!-- To deploy both aggregate reports for top-level project and standalone reports for modules, call
+           mvn site -Preporting
+           mvn site:deploy
+           mvn site -Preporting-aggregate
+           mvn -N site:deploy
+      -->
+      <reporting>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-jxr-plugin</artifactId>
+            <configuration>
+              <aggregate>true</aggregate>
+            </configuration>
+          </plugin>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <configuration>
+              <aggregate>true</aggregate>
+              <source>1.4</source>
+              <links>
+                <link>http://java.sun.com/j2se/1.4.2/docs/api/</link>
+                <link>http://plexus.codehaus.org/plexus-utils/apidocs/</link>
+                <link>http://junit.sourceforge.net/javadoc/</link>
+              </links>
+              <tagletArtifacts>
+                <tagletArtifact>
+                  <groupId>org.codehaus.plexus</groupId>
+                  <artifactId>plexus-javadoc</artifactId>
+                  <version>1.0</version>
+                </tagletArtifact>
+              </tagletArtifacts>
+            </configuration>
+            <reportSets>
+              <reportSet>
+                <reports>
+                  <report>aggregate</report>
+                  <report>test-aggregate</report>
+                </reports>
+              </reportSet>
+            </reportSets>
+          </plugin>
+        </plugins>
+      </reporting>
+    </profile>
+    <profile>
+      <id>m2e</id>
+      <properties>
+        <m2BuildDirectory>target</m2BuildDirectory>
+      </properties>
+      <activation>
+        <property>
+          <name>m2e.version</name>
+        </property>
+      </activation>
+      <build>
+        <directory>${m2BuildDirectory}</directory>
+        <plugins>
+          <plugin>
+            <groupId>org.maven.ide.eclipse</groupId>
+            <artifactId>lifecycle-mapping</artifactId>
+            <version>0.10.0</version>
+            <configuration>
+              <mappingId>customizable</mappingId>
+              <configurators>
+                <configurator id="org.maven.ide.eclipse.jdt.javaConfigurator" />
+                <configurator id="org.maven.ide.eclipse.modello.modelloConfigurator" />
+                <configurator id="org.maven.ide.eclipse.plexus.annotations.plexusConfigurator" />
+              </configurators>
+              <mojoExecutions>
+                <mojoExecution>org.apache.maven.plugins:maven-resources-plugin::</mojoExecution>
+              </mojoExecutions>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>    
+  </profiles>
+</project>
diff --git a/src/main/resources/config/doxia_checkstyle.xml b/src/main/resources/config/doxia_checkstyle.xml
new file mode 100644
index 0000000..d6d22b4
--- /dev/null
+++ b/src/main/resources/config/doxia_checkstyle.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<!DOCTYPE module PUBLIC
+    "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
+
+<!--
+  Checkstyle configuration that checks the maven coding conventions from:
+-->
+
+<module name="Checker">
+
+    <!-- Checks that a package.html file exists for each package.     -->
+    <!-- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -->
+    <!-- module name="PackageHtml"/ -->
+
+    <!-- Checks whether files end with a new line.                        -->
+    <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
+    <!-- module name="NewlineAtEndOfFile"/ -->
+
+    <!-- Checks that property files contain the same keys.         -->
+    <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
+    <!-- module name="Translation"/ -->
+
+    <module name="TreeWalker">
+
+        <property name="cacheFile" value="${checkstyle.cache.file}"/>
+
+        <property name="tabWidth" value="4"/>
+
+        <module name="LeftCurly">
+          <property name="option" value="nl"/>
+        </module>
+
+        <module name="RightCurly">
+          <property name="option" value="alone"/>
+        </module>
+
+        <module name="LineLength">
+          <property name="max" value="120" />
+          <property name="ignorePattern" value="@version|@see|@todo|TODO"/>
+        </module>
+
+        <module name="MemberName" />
+
+        <!-- Checks for Javadoc comments.                     -->
+        <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+        <module name="JavadocMethod">
+          <property name="severity" value="warning"/>
+          <property name="scope" value="protected"/>
+        </module>
+        <module name="JavadocType"/>
+        <module name="JavadocVariable">
+          <property name="severity" value="info"/>
+          <property name="scope" value="protected"/>
+        </module>
+
+
+        <!-- Checks for Naming Conventions.                  -->
+        <!-- See http://checkstyle.sf.net/config_naming.html -->
+        <module name="ConstantName"/>
+        <module name="LocalFinalVariableName"/>
+        <module name="LocalVariableName"/>
+        <!-- <module name="MethodName"/> -->
+        <module name="MethodName">
+          <property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
+        </module>
+        <module name="PackageName"/>
+        <module name="ParameterName"/>
+        <module name="StaticVariableName"/>
+        <module name="TypeName"/>
+
+
+        <!-- Checks for Headers                              -->
+        <!-- See http://checkstyle.sf.net/config_header.html -->
+        <module name="RegexpHeader">
+          <property name="headerFile" value="${checkstyle.header.file}"/>
+        </module>
+
+        <!-- Checks for imports                              -->
+        <!-- See http://checkstyle.sf.net/config_import.html -->
+        <module name="AvoidStarImport"/>
+        <module name="IllegalImport"/>
+        <module name="RedundantImport"/>
+        <module name="UnusedImports"/>
+
+
+        <!-- Checks for Size Violations.                    -->
+        <!-- See http://checkstyle.sf.net/config_sizes.html -->
+        <module name="FileLength"/>
+        <module name="MethodLength"/>
+        <module name="ParameterNumber"/>
+
+
+        <!-- Checks for whitespace                               -->
+        <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+        <module name="EmptyForIteratorPad">
+          <property name="option" value="space"/>
+        </module>
+        <!-- module name="NoWhitespaceAfter"/ -->
+        <!-- module name="NoWhitespaceBefore"/ -->
+        <module name="OperatorWrap"/>
+        <module name="ParenPad">
+          <property name="option" value="space" />
+        </module>
+        <module name="TabCharacter"/>
+        <module name="WhitespaceAfter"/>
+        <module name="WhitespaceAround"/>
+        <!-- module name="MethodParamPad"/ -->
+
+
+        <!-- Modifier Checks                                    -->
+        <!-- See http://checkstyle.sf.net/config_modifiers.html -->
+        <module name="ModifierOrder"/>
+        <module name="RedundantModifier"/>
+
+
+        <!-- Checks for blocks. You know, those {}'s         -->
+        <!-- See http://checkstyle.sf.net/config_blocks.html -->
+        <module name="AvoidNestedBlocks"/>
+        <module name="EmptyBlock">
+          <property name="option" value="text"/>
+        </module>
+        <module name="NeedBraces"/>
+
+
+        <!-- Checks for common coding problems               -->
+        <!-- See http://checkstyle.sf.net/config_coding.html -->
+        <!-- module name="AvoidInlineConditionals"/ -->
+        <module name="DoubleCheckedLocking"/>
+        <module name="EmptyStatement"/>
+        <module name="EqualsHashCode"/>
+        <module name="HiddenField">
+          <property name="severity" value="warning"/>
+          <property name="ignoreSetter" value="true"/>
+          <property name="ignoreConstructorParameter" value="true"/>
+        </module>
+        <module name="IllegalInstantiation"/>
+        <module name="InnerAssignment"/>
+        <module name="MagicNumber">
+          <!-- some numbers are really not that magic -->
+          <property name="ignoreNumbers" value="-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32, 64, 100, 128, 256, 512, 1000, 1024"/>
+        </module>
+        <module name="MissingSwitchDefault"/>
+        <module name="RedundantThrows"/>
+        <module name="SimplifyBooleanExpression"/>
+        <module name="SimplifyBooleanReturn"/>
+
+        <!-- Checks for class design                         -->
+        <!-- See http://checkstyle.sf.net/config_design.html -->
+        <!-- module name="DesignForExtension"/ -->
+        <!-- module name="FinalClass"/ -->
+        <!-- module name="HideUtilityClassConstructor"/ -->
+        <!-- <module name="InterfaceIsType"/> -->
+        <module name="VisibilityModifier">
+          <property name="protectedAllowed" value="true"/>
+        </module>
+
+        <!-- Miscellaneous other checks.                   -->
+        <!-- See http://checkstyle.sf.net/config_misc.html -->
+        <!-- module name="ArrayTypeStyle"/ -->
+        <!-- module name="FinalParameters"/ -->
+        <!-- Line with Trailing Spaces -->
+        <module name="GenericIllegalRegexp">
+            <property name="format" value="\s+$"/>
+            <property name="message" value="Line has trailing spaces."/>
+        </module>
+        <!-- Let todo plugin handle this.
+        <module name="TodoComment"/>
+          -->
+        <module name="UpperEll"/>
+
+    </module>
+
+</module>
diff --git a/src/site/resources/images/doxia-deps.png b/src/site/resources/images/doxia-deps.png
new file mode 100644
index 0000000..0db311e
Binary files /dev/null and b/src/site/resources/images/doxia-deps.png differ
diff --git a/src/site/site.xml b/src/site/site.xml
new file mode 100644
index 0000000..3041176
--- /dev/null
+++ b/src/site/site.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ -->
+
+<project>
+  <bannerLeft>
+    <name>Doxia</name>
+    <src>images/apache-maven-project-2.png</src>
+  </bannerLeft>
+  <bannerRight>
+    <src>images/maven-logo-2.gif</src>
+  </bannerRight>
+
+  <body>
+    <head>
+      <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
+      </script>
+      <script type="text/javascript">
+        _uacct = "UA-140879-1";
+        urchinTracker();
+      </script>
+    </head>
+
+    <menu name="Downloads" inherit="bottom">
+      <item name="Download ${project.name}" href="http://maven.apache.org/doxia/downloads.html"/>
+    </menu>
+
+    <menu ref="modules"/>
+
+    <menu ref="reports"/>
+
+  </body>
+</project>
diff --git a/src/site/xdoc/doxia-deps.odg b/src/site/xdoc/doxia-deps.odg
new file mode 100644
index 0000000..8049367
Binary files /dev/null and b/src/site/xdoc/doxia-deps.odg differ
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
new file mode 100644
index 0000000..5fd017a
--- /dev/null
+++ b/src/site/xdoc/index.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<document>
+
+  <properties>
+    <title>Doxia</title>
+    <author email="hboutemy_AT_apache_DOT_org">Hervé Boutemy</author>
+  </properties>
+
+  <body>
+
+    <section name="Doxia">
+
+      <p>Doxia is a content generation framework that provides powerful techniques for generating static and dynamic content, supporting a variety of markup languages.</p>
+
+      <p>
+        <!-- workaround for DOXIA-227: usemap attribute is stripped (fixed in Doxia 1.1, was 1.0-beta-1) -->
+        <script type="text/javascript">
+          tag='<img src="images/doxia-deps.png" width="647" height="414" border="0" usemap="#Doxia_dependencies" />';
+          tag=tag.substring(0,tag.length-2)+' usemap=\'#Doxia_dependencies\''+tag.substring(tag.length-2);
+          document.write(tag);
+        </script>
+        <map name="Doxia_dependencies">
+          <area shape="rect" coords="98,382,220,413" href="doxia-test-docs/" />
+          <area shape="rect" coords="262,382,385,413" href="doxia-logging-api/" />
+          <area shape="rect" coords="262,333,385,364" href="doxia-sink-api/" />
+          <area shape="rect" coords="262,280,385,313" href="doxia-core/" />
+          <area shape="rect" coords="41,117,167,149" href="doxia-modules/doxia-module-apt/" />
+          <area shape="rect" coords="190,116,315,149" href="doxia-modules/doxia-module-xdoc/" />
+          <area shape="rect" coords="339,117,462,149" href="doxia-modules/doxia-module-xhtml/" />
+          <area shape="rect" coords="486,117,609,149" href="doxia-modules/doxia-module-fml/" />
+          <area shape="rect" coords="68,159,243,190" href="doxia-modules/doxia-module-confluence/" />
+          <area shape="rect" coords="276,159,399,190" href="doxia-modules/doxia-module-twiki/" />
+          <area shape="rect" coords="431,158,554,190" href="doxia-modules/doxia-module-latex/" />
+          <area shape="rect" coords="14,201,232,232" href="doxia-modules/doxia-module-docbook-simple/" />
+          <area shape="rect" coords="242,201,365,232" href="doxia-modules/doxia-module-rtf/" />
+          <area shape="rect" coords="378,201,501,232" href="doxia-modules/doxia-module-itext" />
+          <area shape="rect" coords="512,201,636,232" href="doxia-modules/doxia-module-fo/" />
+          <area shape="rect" coords="0,106,646,260" href="doxia-modules/" />
+          <area shape="rect" coords="251,53,396,85" href="doxia-book/" />
+          <area shape="rect" coords="251,0,395,32" href="doxia-maven-plugin/" />
+          <area shape="rect" coords="71,34,232,66" href="http://maven.apache.org/shared/maven-doxia-tools/" />
+          <area shape="rect" coords="420,34,580,66" href="http://maven.apache.org/ref/current/maven-plugin-api/" />
+          <area shape="rect" coords="407,280,472,312" href="http://www.lowagie.com/iText/" />
+          <area shape="rect" coords="552,280,599,312" href="http://xmlgraphics.apache.org/fop/" />
+          <area shape="rect" coords="420,382,542,412" href="http://plexus.codehaus.org/" />
+        </map>
+      </p>
+
+    </section>
+
+  </body>
+
+</document>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/doxia.git



More information about the pkg-java-commits mailing list