You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by hb...@apache.org on 2020/01/23 08:14:11 UTC

[maven-doxia-book-renderer] 01/34: Move book and plugin since they don't belong in the base of Doxia, and rename them in the process.

This is an automated email from the ASF dual-hosted git repository.

hboutemy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-doxia-book-renderer.git

commit 534db7d2aee83a09c56867d323700a036681afc6
Author: Dennis Lundberg <de...@apache.org>
AuthorDate: Sat Mar 31 16:42:15 2012 +0000

    Move book and plugin since they don't belong in the base of Doxia, and rename them in the process.
    
    doxia/doxia-book --> doxia-tools/doxia-book-renderer
    doxia/doxia-maven-plugin -> doxia/doxia-book-maven-plugin
    
    git-svn-id: https://svn.apache.org/repos/asf/maven/doxia/doxia-tools/trunk@1307847 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            | 165 +++++++
 .../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  | 147 ++++++
 .../maven/doxia/book/services/io/BookIo.java       |  57 +++
 .../doxia/book/services/io/DefaultBookIo.java      | 144 ++++++
 .../renderer/AbstractITextBookRenderer.java        | 338 +++++++++++++
 .../doxia/book/services/renderer/BookRenderer.java |  44 ++
 .../services/renderer/DocbookBookRenderer.java     | 223 +++++++++
 .../book/services/renderer/LatexBookRenderer.java  | 246 ++++++++++
 .../book/services/renderer/PdfBookRenderer.java    |  52 ++
 .../book/services/renderer/RtfBookRenderer.java    |  52 ++
 .../book/services/renderer/XHtmlBookRenderer.java  | 208 ++++++++
 .../book/services/renderer/XdocBookRenderer.java   | 445 +++++++++++++++++
 .../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  | 104 ++++
 .../book/services/validation/ValidationResult.java |  78 +++
 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 +
 src/site/apt/index.apt                             |  20 +
 src/site/apt/usage.apt                             |  31 ++
 src/site/apt/using-book-xsd.apt                    |  41 ++
 src/site/site.xml                                  |  48 ++
 .../apache/maven/doxia/book/BookRendererTest.java  |  75 +++
 .../book/services/indexer/BookIndexerTest.java     | 118 +++++
 .../renderer/docbook/DocBookBookSinkTest.java      | 490 +++++++++++++++++++
 src/test/resources/book-1.xml                      |  51 ++
 src/test/resources/book-1/section-1.apt            |  61 +++
 src/test/resources/book-1/section-2.apt            |  16 +
 src/test/resources/book-1/section-3.apt            |  17 +
 src/test/resources/book-1/section-4.apt            |  16 +
 .../expected/doc-book/plexus-user-guide.xml        | 124 +++++
 46 files changed, 5918 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..cc90293
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,165 @@
+<?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.3-SNAPSHOT</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>
+  <url>http://maven.apache.org/doxia/doxia/doxia-book/</url>
+
+  <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>
+      <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>
+                <locale>fr</locale>
+              </locales>
+            </configuration>
+          </plugin>
+        </plugins>
+      </reporting>
+    </profile>
+  </profiles>
+</project>
\ No newline at end of file
diff --git a/src/main/java/org/apache/maven/doxia/book/BookDoxia.java b/src/main/java/org/apache/maven/doxia/book/BookDoxia.java
new file mode 100644
index 0000000..3d4ef96
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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<File> 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<File> files, File outputDirectory, Locale locale,
+                     String inputEncoding, String outputEncoding )
+        throws BookDoxiaException;
+}
diff --git a/src/main/java/org/apache/maven/doxia/book/BookDoxiaException.java b/src/main/java/org/apache/maven/doxia/book/BookDoxiaException.java
new file mode 100644
index 0000000..db02e25
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/DefaultBookDoxia.java b/src/main/java/org/apache/maven/doxia/book/DefaultBookDoxia.java
new file mode 100644
index 0000000..02fb421
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ * @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<String, BookRenderer> bookRenderers;
+
+    // ----------------------------------------------------------------------
+    // BookDoxia Implementation
+    // ----------------------------------------------------------------------
+
+    /** {@inheritDoc} */
+    public BookModel loadBook( File bookDescriptor )
+        throws BookDoxiaException
+    {
+        return bookIo.readBook( bookDescriptor );
+    }
+
+    /** {@inheritDoc} */
+    public void renderBook( BookModel book, String bookRendererId, List<File> 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<File> 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 = 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<String> getAvailableBookRenderers()
+    {
+        return Collections.unmodifiableSet( bookRenderers.keySet() );
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/doxia/book/InvalidBookDescriptorException.java b/src/main/java/org/apache/maven/doxia/book/InvalidBookDescriptorException.java
new file mode 100644
index 0000000..efc9c80
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/context/BookContext.java b/src/main/java/org/apache/maven/doxia/book/context/BookContext.java
new file mode 100644
index 0000000..3e78b19
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+public class BookContext
+{
+    /** The BookModel of this context. */
+    private BookModel book;
+
+    /** The files. */
+    private Map<String, BookContext.BookFile> 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<String, BookContext.BookFile> getFiles()
+    {
+        if ( files == null )
+        {
+            files = new HashMap<String, BookContext.BookFile>();
+        }
+
+        return files;
+    }
+
+    /**
+     * Set the files of this BookContext.
+     *
+     * @param files the Map of files.
+     */
+    public void setFiles( Map<String, BookContext.BookFile> 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/src/main/java/org/apache/maven/doxia/book/context/BookIndex.java b/src/main/java/org/apache/maven/doxia/book/context/BookIndex.java
new file mode 100644
index 0000000..b1d9ca0
--- /dev/null
+++ b/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@objectware.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+public class BookIndex
+    extends IndexEntry
+{
+    /**
+     * Constructs a new BookIndex.
+     */
+    public BookIndex()
+    {
+        super( "book" );
+    }
+}
diff --git a/src/main/java/org/apache/maven/doxia/book/services/indexer/BookIndexer.java b/src/main/java/org/apache/maven/doxia/book/services/indexer/BookIndexer.java
new file mode 100644
index 0000000..61129fa
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/indexer/DefaultBookIndexer.java b/src/main/java/org/apache/maven/doxia/book/services/indexer/DefaultBookIndexer.java
new file mode 100644
index 0000000..420859b
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/indexer/DefaultBookIndexer.java
@@ -0,0 +1,147 @@
+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 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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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 ( Chapter chapter : book.getChapters() )
+        {
+            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 ( Section section : chapter.getSections() )
+        {
+            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/src/main/java/org/apache/maven/doxia/book/services/io/BookIo.java b/src/main/java/org/apache/maven/doxia/book/services/io/BookIo.java
new file mode 100644
index 0000000..517f7fd
--- /dev/null
+++ b/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@objectware.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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<File> files );
+}
diff --git a/src/main/java/org/apache/maven/doxia/book/services/io/DefaultBookIo.java b/src/main/java/org/apache/maven/doxia/book/services/io/DefaultBookIo.java
new file mode 100644
index 0000000..9509d1d
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/io/DefaultBookIo.java
@@ -0,0 +1,144 @@
+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.Map;
+import java.util.TreeMap;
+import java.util.List;
+
+/**
+ * <p>DefaultBookIo class.</p>
+ *
+ * @plexus.component
+ * @author <a href="mailto:trygve.laugstol@objectware.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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<File> files )
+    {
+        // ----------------------------------------------------------------------
+        // Find all the files, map the file names to ids
+        // ----------------------------------------------------------------------
+
+        Collection<SiteModule> siteModules = siteModuleManager.getSiteModules();
+
+        for ( SiteModule siteModule : siteModules )
+        {
+            String extension = siteModule.getExtension();
+
+            String sourceDirectory = File.separator + siteModule.getSourceDirectory() + File.separator;
+
+            String parserId = siteModule.getParserId();
+
+            for ( File file : files )
+            {
+                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<String, BookContext.BookFile> map = new TreeMap<String, BookContext.BookFile>( context.getFiles() );
+
+            for ( Map.Entry<String, BookContext.BookFile> entry : map.entrySet() )
+            {
+                BookContext.BookFile file = entry.getValue();
+
+                getLogger().debug( " " + entry.getKey() + "=" + file.getFile() + ", parser: " + file.getParserId() );
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/maven/doxia/book/services/renderer/AbstractITextBookRenderer.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/AbstractITextBookRenderer.java
new file mode 100644
index 0000000..78e18ff
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/renderer/AbstractITextBookRenderer.java
@@ -0,0 +1,338 @@
+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 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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ */
+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 ( Chapter chapter : book.getChapters() )
+            {
+                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 ( Section section : chapter.getSections() )
+        {
+            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/src/main/java/org/apache/maven/doxia/book/services/renderer/BookRenderer.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/BookRenderer.java
new file mode 100644
index 0000000..759831f
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/renderer/DocbookBookRenderer.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/DocbookBookRenderer.java
new file mode 100644
index 0000000..0d86ebd
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/renderer/DocbookBookRenderer.java
@@ -0,0 +1,223 @@
+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 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$
+ */
+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 ( Chapter chapter : book.getChapters() )
+            {
+                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 ( Section section : chapter.getSections() )
+        {
+            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/src/main/java/org/apache/maven/doxia/book/services/renderer/LatexBookRenderer.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/LatexBookRenderer.java
new file mode 100644
index 0000000..0c5b383
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/renderer/LatexBookRenderer.java
@@ -0,0 +1,246 @@
+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.Map;
+import java.util.HashMap;
+
+/**
+ * <p>LatexBookRenderer class.</p>
+ *
+ * @plexus.component role-hint="latex"
+ * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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<String, SectionInfo> sectionInfos = new HashMap<String, SectionInfo>();
+
+        for ( Chapter chapter : book.getChapters() )
+        {
+            for ( Section section : chapter.getSections() )
+            {
+                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 ( Chapter chapter : book.getChapters() )
+        {
+            writer.println( "\\chapter{" + chapter.getTitle() + "}" );
+
+            for ( Section section : chapter.getSections() )
+            {
+                SectionInfo info = 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/src/main/java/org/apache/maven/doxia/book/services/renderer/PdfBookRenderer.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/PdfBookRenderer.java
new file mode 100644
index 0000000..b6a1ae5
--- /dev/null
+++ b/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@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/renderer/RtfBookRenderer.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/RtfBookRenderer.java
new file mode 100644
index 0000000..0e60e13
--- /dev/null
+++ b/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@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/renderer/XHtmlBookRenderer.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/XHtmlBookRenderer.java
new file mode 100644
index 0000000..ad6f585
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/renderer/XHtmlBookRenderer.java
@@ -0,0 +1,208 @@
+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;
+
+/**
+ * <p>XHtmlBookRenderer class.</p>
+ *
+ * @plexus.component role-hint="xhtml"
+ * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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 ( Chapter chapter : book.getChapters() )
+            {
+                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 ( Section section : chapter.getSections() )
+        {
+            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 = 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/src/main/java/org/apache/maven/doxia/book/services/renderer/XdocBookRenderer.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/XdocBookRenderer.java
new file mode 100644
index 0000000..f5c6541
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/renderer/XdocBookRenderer.java
@@ -0,0 +1,445 @@
+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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ * @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<IndexEntry> ii = context.getIndex().getChildEntries().iterator();
+
+        for ( Chapter chapter : book.getChapters() )
+        {
+            renderChapter( chapter, context, 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 ( IndexEntry entry : context.getIndex().getChildEntries() )
+            {
+                writeChapterIndexForBookIndex( sink, entry );
+            }
+            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 ( IndexEntry sectionIndex : chapterEntry.getChildEntries() )
+        {
+            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 ( IndexEntry subsectionIndex : sectionIndex.getChildEntries() )
+        {
+            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<IndexEntry> ii = chapterIndex.getChildEntries().iterator();
+
+        for ( Section section : chapter.getSections() )
+        {
+            renderSection( context, section, 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 ( IndexEntry sectionIndex : chapterIndex.getChildEntries() )
+            {
+                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/src/main/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSink.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSink.java
new file mode 100644
index 0000000..56a632b
--- /dev/null
+++ b/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$
+ * @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/src/main/java/org/apache/maven/doxia/book/services/renderer/latex/LatexBookSink.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/latex/LatexBookSink.java
new file mode 100644
index 0000000..984093e
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/AbstractXdocBookSink.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/AbstractXdocBookSink.java
new file mode 100644
index 0000000..11944dc
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/ChapterXdocBookSink.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/ChapterXdocBookSink.java
new file mode 100644
index 0000000..c3d90c1
--- /dev/null
+++ b/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@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/IndexXdocBookSink.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/IndexXdocBookSink.java
new file mode 100644
index 0000000..7832957
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/SectionXdocBookSink.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/xdoc/SectionXdocBookSink.java
new file mode 100644
index 0000000..f16a179
--- /dev/null
+++ b/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@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/renderer/xhtml/XhtmlBookSink.java b/src/main/java/org/apache/maven/doxia/book/services/renderer/xhtml/XhtmlBookSink.java
new file mode 100644
index 0000000..a6b823e
--- /dev/null
+++ b/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$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/validation/BookValidator.java b/src/main/java/org/apache/maven/doxia/book/services/validation/BookValidator.java
new file mode 100644
index 0000000..878300d
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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/src/main/java/org/apache/maven/doxia/book/services/validation/DefaultBookValidator.java b/src/main/java/org/apache/maven/doxia/book/services/validation/DefaultBookValidator.java
new file mode 100644
index 0000000..19a06ec
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/validation/DefaultBookValidator.java
@@ -0,0 +1,104 @@
+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;
+
+/**
+ * Default implementation of BookValidator.
+ *
+ * @plexus.component
+ *
+ * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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 ( Chapter chapter : book.getChapters() )
+            {
+                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/src/main/java/org/apache/maven/doxia/book/services/validation/ValidationResult.java b/src/main/java/org/apache/maven/doxia/book/services/validation/ValidationResult.java
new file mode 100644
index 0000000..47e218c
--- /dev/null
+++ b/src/main/java/org/apache/maven/doxia/book/services/validation/ValidationResult.java
@@ -0,0 +1,78 @@
+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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+public class ValidationResult
+{
+    /** List of errors. */
+    private List<String> errors;
+
+    /** List of warnings. */
+    private List<String> 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<String> getErrors()
+    {
+        if ( errors == null )
+        {
+            errors = new ArrayList<String>();
+        }
+
+        return errors;
+    }
+
+    /**
+     * Return the list of warnings.
+     *
+     * @return List. A new ArrayList is constructed if the current List is null.
+     */
+    public List<String> getWarnings()
+    {
+        if ( warnings == null )
+        {
+            warnings = new ArrayList<String>();
+        }
+
+        return warnings;
+    }
+}
diff --git a/src/main/modello/book.mdo b/src/main/modello/book.mdo
new file mode 100644
index 0000000..13068f6
--- /dev/null
+++ b/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/src/main/resources/book-renderer.properties b/src/main/resources/book-renderer.properties
new file mode 100644
index 0000000..9369897
--- /dev/null
+++ b/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/src/main/resources/book-renderer_en.properties b/src/main/resources/book-renderer_en.properties
new file mode 100644
index 0000000..0c479bc
--- /dev/null
+++ b/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/src/main/resources/book-renderer_fr.properties b/src/main/resources/book-renderer_fr.properties
new file mode 100644
index 0000000..385696e
--- /dev/null
+++ b/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/src/site/apt/index.apt b/src/site/apt/index.apt
new file mode 100644
index 0000000..968e955
--- /dev/null
+++ b/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/src/site/apt/usage.apt b/src/site/apt/usage.apt
new file mode 100644
index 0000000..e90d983
--- /dev/null
+++ b/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/src/site/apt/using-book-xsd.apt b/src/site/apt/using-book-xsd.apt
new file mode 100644
index 0000000..1c3fdc9
--- /dev/null
+++ b/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/src/site/site.xml b/src/site/site.xml
new file mode 100644
index 0000000..cc6f0cd
--- /dev/null
+++ b/src/site/site.xml
@@ -0,0 +1,48 @@
+<?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.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/DECORATION/1.1.0 http://maven.apache.org/xsd/decoration-1.1.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="JavaDocs" href="apidocs/index.html"/>
+      <item name="Source Xref" href="xref/index.html"/>
+      <!--item name="FAQ" href="faq.html"/-->
+    </menu>
+
+    <menu name="Book Schema">
+      <item name="Reference" href="book.html"/>
+      <item name="Using Book Schema" href="using-book-xsd.html"/>
+    </menu>
+
+    <menu ref="reports"/>
+
+  </body>
+
+</project>
diff --git a/src/test/java/org/apache/maven/doxia/book/BookRendererTest.java b/src/test/java/org/apache/maven/doxia/book/BookRendererTest.java
new file mode 100644
index 0000000..b445a97
--- /dev/null
+++ b/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@inamo.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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/src/test/java/org/apache/maven/doxia/book/services/indexer/BookIndexerTest.java b/src/test/java/org/apache/maven/doxia/book/services/indexer/BookIndexerTest.java
new file mode 100644
index 0000000..1a293ad
--- /dev/null
+++ b/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@objectware.no">Trygve Laugst&oslash;l</a>
+ * @version $Id$
+ */
+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/src/test/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSinkTest.java b/src/test/java/org/apache/maven/doxia/book/services/renderer/docbook/DocBookBookSinkTest.java
new file mode 100644
index 0000000..6a4de45
--- /dev/null
+++ b/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 "~,_=,_-,_+,_*,_[,_],_&lt;,_&gt;,_{,_},_\\";
+    }
+
+    /** {@inheritDoc} */
+    protected String getRawTextBlock( String text )
+    {
+        // TODO
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    protected String getCommentBlock( String text )
+    {
+        return "<!-- Simple comment with - - - - -->";
+    }
+}
diff --git a/src/test/resources/book-1.xml b/src/test/resources/book-1.xml
new file mode 100644
index 0000000..7364904
--- /dev/null
+++ b/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/src/test/resources/book-1/section-1.apt b/src/test/resources/book-1/section-1.apt
new file mode 100644
index 0000000..35d204f
--- /dev/null
+++ b/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/src/test/resources/book-1/section-2.apt b/src/test/resources/book-1/section-2.apt
new file mode 100644
index 0000000..64c1f5f
--- /dev/null
+++ b/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/src/test/resources/book-1/section-3.apt b/src/test/resources/book-1/section-3.apt
new file mode 100644
index 0000000..f5d07ec
--- /dev/null
+++ b/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/src/test/resources/book-1/section-4.apt b/src/test/resources/book-1/section-4.apt
new file mode 100644
index 0000000..a148762
--- /dev/null
+++ b/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/src/test/resources/expected/doc-book/plexus-user-guide.xml b/src/test/resources/expected/doc-book/plexus-user-guide.xml
new file mode 100644
index 0000000..37ed826
--- /dev/null
+++ b/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 &quot;de Finibus Bonorum et Malorum&quot;,
+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 &quot;de Finibus Bonorum et Malorum&quot;,
+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>