Commits

Brian Mearns committed b546b77 Merge

Merged the "epub parts and books" into the default branch.

  • Participants
  • Parent commits 8571c41, 92aa68d

Comments (0)

Files changed (9)

src/tome/doc/EPUB_TEMPL.otl

 		-The copyright holder.
 
 
-These are only available while processing chapters:
-	CHAP-NUM
-		- The current chapter number.
-	CHAP-TITLE-LIST
-		- The list of all titles for the chapter.
-	CHAP-TITLE
-		-The first item in CHAP-TITLE-LIST
-	CHAP-TITLES-HTML
-		- A string of HTML content that gives all of the titles of the chapter.
-	CHAP-TITLE-SEP-HTML
+These are only available while processing sections (parts, books, or chapters):
+	-NUM
+		- The overall number of this section within the entire document, starting with 1.
+	-IDX
+		- The number of the section within it's part, starting at 1.
+	-LEN
+		- The number of children in this section. For parts this is the number of books,
+		for books this is the number of chapters, and for chapters this is the number of scenes.
+	-IS-FIRST
+		-"1" if this is the first section of it's type in the entire document, "0" otherwise
+	-IS-LAST
+		-"1" if this is the last section of it's type in the entire document, "0" otherwise
+
+	-TITLE-LIST
+		- The list of all titles for the section.
+	-TITLE
+		-The first item in -TITLE-LIST
+	-TITLES-HTML
+		- A string of HTML content that gives all of the titles of the section.
+	-TITLE-SEP-HTML
 		-A string of HTML content giving a separator to precede the title, or empty if there is no title.
-	CHAP-SHORTMARK
-		-A string giving the short mark of the chapter. If there is no short mark, it's the first title of
+	-SHORTMARK
+		-A string giving the short mark of the section. If there is no short mark, it's the first title of
 		the chapter. If there's not one of those either, it's empty.
-	CHAP-HAS-SHORTMARK
+	-HAS-SHORTMARK
 		-0 if there is not short mark for the chapter, non-zero if there is. In this case, shortmark is as
-		returned by CHAP-SHORTMARK, not necessarily the actual short mark.
-	CHAP-SHORTMARK-SEP-HTML
+		returned by -SHORTMARK, not necessarily the actual short mark.
+	-SHORTMARK-SEP-HTML
 		-A string of HTML content giving a separator to precede the short mark, or empty if there is no shortmark.
 
-	CHAP-SCENE-COUNT
-		-The number of scenes in the chapter.
 
-	CHAP-CONTENT-HTML
-		-A string giving the complete content of the chapter as HTML.
+	Chapter specfic:
+		CHAP-SCENE-COUNT
+			-@deprecated, use CHAP-LEN instead. The number of scenes in the chapter.
 
-	CHAP-IS-FIRST
-		-"1" if this is the first chapter, "0" otherwise
-	CHAP-IS-LAST
-		-"1" if this is the last chapter, "0" otherwise
-	CHAP-REL-PREV-HTML
-		-A string of HTML content giving a LINK element with a "prev" REL attribute pointing to the previous
-		chapter, or empty if there is no previous chapter.
-	CHAP-REL-NEXT-HTML
-		-A string of HTML content giving a LINK element with a "next" REL attribute pointing to the next
-		chapter, or empty if there is no next chapter.
+		CHAP-CONTENT-HTML
+			-A string giving the complete content of the chapter as HTML.
 
-	CHAP-HAS-ENDNOTES
-		-0 if there are no endnotes for this chapter, non-zero otherwise.
-	CHAP-ENDNOTES-LIST
-		-A list of all the endnotes for this chapter. If there are no notes for this chapter, it's an empty
-		list, otherwise, it contains each endnote as a string of HTML content.
-	CHAP-ENDNOTES-HTML
-		-A string of HTML content giving all of the endsnotes for this chapter. The string is empty if there are no
-		notes for this chapter.
-	CHAP-ENDNOTES-TITLE-HTML
-		-A string of HTML content giving the end-notes title for this chapter. This is empty if there are no notes for
-		this chapter.
+		CHAP-HAS-ENDNOTES
+			-0 if there are no endnotes for this chapter, non-zero otherwise.
+		CHAP-ENDNOTES-LIST
+			-A list of all the endnotes for this chapter. If there are no notes for this chapter, it's an empty
+			list, otherwise, it contains each endnote as a string of HTML content.
+		CHAP-ENDNOTES-HTML
+			-A string of HTML content giving all of the endsnotes for this chapter. The string is empty if there are no
+			notes for this chapter.
+		CHAP-ENDNOTES-TITLE-HTML
+			-A string of HTML content giving the end-notes title for this chapter. This is empty if there are no notes for
+			this chapter.
+
+	File-specific:
+		PREV-FILE
+			-A string giving a the path to the previous file, or empty if there is no previous
+			(known) file.
+		HAS-PREV
+			-"1" if there is a known previous file, else "0".
+		REL-PREV-HTML
+			-A string of HTML content giving a LINK element with a "prev" REL attribute pointing to the previous
+			file, or empty if there is no previous (known) file.
+		NEXT-FILE
+			-A string giving a the path to the next file, or empty if there is no next (known) file.
+		HAS-NEXT
+			-"1" if there is a known next file, else "0".
+		REL-NEXT-HTML
+			-A string of HTML content giving a LINK element with a "next" REL attribute pointing to the next
+			file, or empty if there is no next (known) file.
 
 
 These are only available at the end:
+	PART-COUNT
+	BOOK-COUNT
 	CHAP-COUNT
 		- The total number of chapters in the Tome.
 	CHAP-FILE-LIST
-		- A list of strings giving the relative path for each of the generated chapter files,
-		in numerical chpater order.
+		- A list of strings giving the relative path for each of the generated chapter files
+		in the order in which they appear in the book.
+	BOOK-FILE-LIST
+	PART-FILE-LIST
+	GEN-FILE-LIST
+		-A list of strings giving the relative path for all the generated files (part title pages,
+		book title pages, and chapter files), in the order in which they appear in the book.
+	LAST-CHAP-FILE-HTML
+		@Deprecated: Use LAST-GEN-FILE-HTML instead.
+	LAST-GEN-FILE-HTML
+		-A string giving the relative path to the last generated file, escaped for HTML.
 
 	HAS-ENDNOTES
 		-0 if there are no endnotes for the entire tome, non-zero otherwise.

src/tome/res/epub-template/bookTitle.xhtml.tmpl

+{#  "
+
+    "
+}{syntax xml
+
+}<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>Book {roman-up {$ BOOK-NUM}}{$ BOOK-TITLE-SEP-HTML}{$ BOOK-SHORTMARK}</title>
+        <link href="stylesheet.css" type="text/css" rel="stylesheet" />
+        <link rel="stylesheet" type="application/vnd.adobe-page-template+xml" href="page-template.xpgt"/>
+
+        {$ LICENSE-LINK-HTML }
+        <link rel="contents" href="toc.xhtml" />
+        <link rel="start" href="frontcover.xhtml" />
+
+    </head>
+    <body class='bodymatter book titlepage'>
+
+        <a id="chapter{$ BOOK-NUM}" />
+        <div class='title'>
+            <h3 class='titleGeneric'>Book {roman-up {$ BOOK-NUM}}</h3>
+            <h1 class='title'>{$ BOOK-TITLE}</h1>
+            <hr class='titleSep' />
+            {$ BOOK-SUBTITLES-HTML}
+        </div>
+
+    </body>
+</html>
+

src/tome/res/epub-template/chapter.xhtml.tmpl

         <link rel="stylesheet" type="text/css" href="stylesheet.css" />
         <link rel="stylesheet" type="application/vnd.adobe-page-template+xml" href="page-template.xpgt"/>
 
+        {$ LICENSE-LINK-HTML }
         <link rel="contents" href="toc.xhtml" />
         <link rel="start" href="frontcover.xhtml" />
-        <link rel="license" href="https://creativecommons.org/licenses/by-nc-nd/3.0/deed.en_US" />
 
         {if
             {$ CHAP-IS-FIRST}

src/tome/res/epub-template/endnotes.xhtml.tmpl

     "
 }{v{:
 
-}}{syntax xml
+}}{syn html
 
 }<?xml version="1.0" encoding="UTF-8"?>
 <html xmlns="http://www.w3.org/1999/xhtml">
         <link href="stylesheet.css" type="text/css" rel="stylesheet" />
         <link rel="stylesheet" type="application/vnd.adobe-page-template+xml" href="page-template.xpgt"/>
 
+        {$ LICENSE-LINK-HTML }
         <link rel="contents" href="toc.xhtml" />
         <link rel="start" href="frontcover.xhtml" />
-        <link rel="license" href="https://creativecommons.org/licenses/by-nc-nd/3.0/deed.en_US" />
 
         <link rel="prev" href="{$ LAST-CHAP-FILE-HTML}" />
     </head>

src/tome/res/epub-template/frontcover.xhtml.tmpl

         <link href="stylesheet.css" type="text/css" rel="stylesheet" />
         <link rel="stylesheet" type="application/vnd.adobe-page-template+xml" href="page-template.xpgt"/>
 
+        {$ LICENSE-LINK-HTML }
         <link rel="contents" href="toc.xhtml" />
         <link rel="start" href="frontcover.xhtml" />
-        <link rel="license" href="https://creativecommons.org/licenses/by-nc-nd/3.0/deed.en_US" />
 
         <link rel="next" href="titlepage.xhtml" />
 

src/tome/res/epub-template/partTitle.xhtml.tmpl

+{#  "
+
+    "
+}{syntax xml
+
+}<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>Part {roman-up {$ PART-NUM}}{$ PART-TITLE-SEP-HTML}{$ PART-SHORTMARK}</title>
+        <link href="stylesheet.css" type="text/css" rel="stylesheet" />
+        <link rel="stylesheet" type="application/vnd.adobe-page-template+xml" href="page-template.xpgt"/>
+
+        {$ LICENSE-LINK-HTML }
+        <link rel="contents" href="toc.xhtml" />
+        <link rel="start" href="frontcover.xhtml" />
+
+    </head>
+    <body class='bodymatter part titlepage'>
+
+        <a id="chapter{$ PART-NUM}" />
+        <div class='title'>
+            <h3 class='titleGeneric'>Part {roman-up {$ PART-NUM}}</h3>
+            <h1 class='title'>{$ PART-TITLE}</h1>
+            <hr class='titleSep' />
+            {$ PART-SUBTITLES-HTML}
+        </div>
+
+    </body>
+</html>
+

src/tome/res/epub-template/title_page.xhtml.tmpl

         <link href="stylesheet.css" type="text/css" rel="stylesheet" />
         <link rel="stylesheet" type="application/vnd.adobe-page-template+xml" href="page-template.xpgt"/>
 
+        {$ LICENSE-LINK-HTML }
         <link rel="contents" href="toc.xhtml" />
         <link rel="start" href="frontcover.xhtml" />
-        <link rel="license" href="https://creativecommons.org/licenses/by-nc-nd/3.0/deed.en_US" />
 
         <link rel="prev" href="frontcover.xhtml" />
         <link rel="next" href="toc.xhtml" />

src/tome/res/epub-template/toc.xhtml.tmpl

         <link href="stylesheet.css" type="text/css" rel="stylesheet" />
         <link rel="stylesheet" type="application/vnd.adobe-page-template+xml" href="page-template.xpgt"/>
 
+        {$ LICENSE-LINK-HTML }
         <link rel="contents" href="toc.xhtml" />
         <link rel="start" href="frontcover.xhtml" />
-        <link rel="license" href="https://creativecommons.org/licenses/by-nc-nd/3.0/deed.en_US" />
 
         <link rel='prev' href='titlepage.xhtml' />
-        <link rel="next" href="chapter0001.xhtml" />
         
     </head>
     <body class="frontmatter toc" id="toc">

src/tome/writeEpub.py

     """
     return text.replace("&", "&amp;").replace("'", "&#8217;").replace("<", "&lt;").replace(">", "&gt;")
 
+@templ.texec.function
+class xHtmlEscape(templ.texec.TFunction):
+    """
+    {html-esc TEXT}
+
+    Returns a string which is the given text, escaped for use in an HTML document.
+    """
+    __mnemonics__ = ("html-esc",)
+    def execute(self, name, args, ostream, stack):
+        self.checkArgCount(name, args, exact=[1,])
+        xstr = self.checkArgType(name, 0, args, str)
+        return templ.ttypes.String(htmlEscape(xstr))
+
 
 class EpubWriter(object):
     """
     def __init__(
         self, tome, ofile, templateDirectory=None,
         tdFilter=None, tmplFilter=None, chapterTemplatePath=None,
-        bookTitleTemplatePath=None, partTitleTemplatePath=None,
-        uid=None, lang=None
+        uid=None, lang=None,
+        bookTitleTemplatePath=None, partTitleTemplatePath=None
     ):
         """
         :param tome: The Tome object for which to generate an EPUB document.
             If not given or `None`, the default filename :file:`chapter.xhtml.tmpl` will be appended to
             the path of the template directory and used. 
 
-        :param str bookTitleTemplatePath:
-            Simmilar to `chapterTemplatePath`, but for the title page at the start of explicit books sections
-
-        :param str partTitleTemplatePath:
-            Simmilar to `chapterTemplatePath`, but for the title page at the start of explicit part sections.
-
         :param str uid:
             A unique identifier for the book, which will be used for the 
             `dc:identifier <http://idpf.org/epub/30/spec/epub30-publications.html#elemdef-opf-dcidentifier>`_
             from the "lang" :meth:`meta item <Tome.Tome.getMeta>` in the |Tome|. If no such meta item is
             found, an Exception is raised.
 
+        :param str bookTitleTemplatePath:
+            Simmilar to `chapterTemplatePath`, but for the title page at the start of explicit books sections
+
+        :param str partTitleTemplatePath:
+            Simmilar to `chapterTemplatePath`, but for the title page at the start of explicit part sections.
         """
         self.__tome = tome
         self.__ofile = ofile
         self.__chHtmlShortMarks = []
 
         #Current positions and things...
-        self.__prtIdx = 0
-        self.__bkIdx = 0
-        self.__chIdx = 0
+        self.__prtNum = 0
+        self.__bkNum = 0
+        self.__chNum = 0
 
         self.__tocHtmlStr = ""
         self.__ncxXmlStr = ""
 
             ### End notes
             elif tag == "n":
-                chEndNotes = self.__enList[self.__chIdx-1]
+                chEndNotes = self.__enList[self.__chNum-1]
                 enIdx = len(chEndNotes)
                 chEndNotes.append(None)
                 enNum = str(enIdx+1)
-                chNum = str(self.__chIdx)
+                chNum = str(self.__chNum)
 
                 #Add a reference for the endnote to the content.
-                ostream.write("<a class='noteref' rel='footnote' id='ch{chNum}N{enNum}Ref' href='chapter{chUid}.xhtml#ch{chNum}N{enNum}'>{enNum}</a>".format(
+                ostream.write("<a class='noteref' rel='footnote' id='ch{chNum}N{enNum}Ref' href='{path}#ch{chNum}N{enNum}'>{enNum}</a>".format(
+                    path = self.chapterFileName(self.__chNum),
                     chNum = chNum,
-                    enNum = enNum,
-                    chUid = self.__chUid(self.__chIdx)
+                    enNum = enNum
                 ))
 
                 #Now generate the content for the footnote.
         Actually generates and outputs all of the EPUB content and the final EPUB document.
         """
 
-        zf = zipfile.ZipFile(self.__ofile, "w")
-        zf.writestr("mimetype", "application/epub+zip")
+        self.__zipfile = zipfile.ZipFile(self.__ofile, "w")
+        self.__zipfile.writestr("mimetype", "application/epub+zip")
 
         #Find all of our source files. Save a list of templ files we need to process, and add
         # verbatim files directly to the zipfile.
         for f in srcFiles:
             if os.path.join(self.__templateDir, f) == self.__chapterTemplatePath:
                 continue
+            if os.path.join(self.__templateDir, f) == self.__bookTitleTemplatePath:
+                continue
+            if os.path.join(self.__templateDir, f) == self.__partTitleTemplatePath:
+                continue
             elif self.__tmplFilter(f):
                 templFiles.append(f)
             else:
                 src = os.path.join(self.__templateDir, f)
                 arcname = f
-                zf.write(src, arcname)
+                self.__zipfile.write(src, arcname)
 
 
         ##### Start generating content
         topScope["MODIFIED"] = templ.ttypes.String(time.strftime("%Y-%m-%dT%H:%M:%SZ", now))
         topScope["COPYRIGHT-YEAR"] = templ.ttypes.String(time.strftime("%Y", now))
         topScope["COPYRIGHT-HOLDER"] = templ.ttypes.String(firstAuthor)
+
         topScope["LICENSE-HTML"] = templ.ttypes.String("")
         if self.__tome.hasMeta("license-html"):
             topScope["LICENSE-HTML"] = templ.ttypes.String(
                 licenseHtml = pretext + licenseHtml
                 topScope["LICENSE-HTML"] = templ.ttypes.String(licenseHtml)
 
+        topScope["LICENSE-LINK-HTML"] = templ.ttypes.String("")
+        if self.__tome.hasMeta("license-url"):
+            topScope["LICENSE-LINK-HTML"] = templ.ttypes.String(
+                "".join(("""
+        <link rel="license" href="{url}" />""".format(
+                        url = htmlEscape(url)
+                    )) for url in self.__tome.getMeta("license-url")
+            )
+        )
 
+        #Generate a complete list of files to be generated.
+        prtFiles = []
+        bkFiles = []
+        chFiles = []
+        genFiles = []
+        prtNum = bkNum = chNum = 0
+        for prtIdx in xrange(len(self.__tome)):
+            prtNum += 1
+            part = self.__tome[prtIdx]
+            if part.title() is not None:
+                prtFile = self.partFileName(prtNum)
+                prtFiles.append(prtFile)
+                genFiles.append(prtFile)
+            for bkIdx in xrange(len(part)):
+                bkNum += 1
+                book = self.__tome[bkIdx]
+                if book.title() is not None:
+                    bkFile = self.bookFileName(bkNum)
+                    bkFiles.append(bkFile)
+                    genFiles.append(bkFile)
+                for chIdx in xrange(len(book)):
+                    chNum += 1
+                    chFile = self.chapterFileName(chNum)
+                    chFiles.append(chFile)
+                    genFiles.append(chFile)
+        self.__fileList = genFiles
+        self.__fileIdx = 0
 
-        #TODO Include Parts and Books.
 
-        chFileList = []
-        hasEndnotes = "0"
-        lastPart = False
-        lastBook = False
-        lastChapter = False
-        for prtNumber in xrange(len(self.__tome)):
-            part = self.__tome[prtNumber]
-            self.__prtIdx += 1
-            lastPart = (prtNumber+1 == len(self.__tome))
-
-            for bkNumber in xrange(len(part)):
-                book = part[bkNumber]
-                self.__bkIdx += 1
-                lastBook = (bkNumber+1 == len(part))
-
-                for chNumber in xrange(len(book)):
-                    chapter = book[chNumber]
-                    self.__chIdx += 1
-                    lastChapter = (chNumber+1 == len(book))
-
-                    #Create a new list of end notes for this chapter.
-                    self.__enList.append([])
-
-                    #Generate the chapter content.
-                    startingDepth = templStack.depth()
-                    chOstream = cStringIO.StringIO()
-                    chRelPath = self.writeChapterContent(templStack, chOstream, prtNumber, bkNumber, chapter, lastChapter and lastBook and lastPart)
-                    chFileList.append(chRelPath)
-                    chFileContent = chOstream.getvalue()
-                    chOstream.close()
-
-                    #Add the generated chapter file to the zipfile.
-                    zf.writestr(chRelPath, chFileContent)
-                    chFileContent = ""
-                    if templStack.depth() != startingDepth:
-                        raise Exception("Error processing chapter %d: stack depth is incorrect." % self.__chIdx)
-
-                    if len(self.__enList[-1]) > 0:
-                        hasEndnotes = "1"
+        for prtIdx in xrange(len(self.__tome)):
+            self.__prtNum += 1
+            self.generatePart(templStack, self.__tome[prtIdx], prtIdx, len(self.__tome))
 
 
         #Now that we've processed everything, we can add the rest of the symbols to the scope.
-        topScope["CHAP-FILE-LIST"] = templ.ttypes.List(chFileList)
-        topScope["LAST-CHAP-FILE-HTML"] = templ.ttypes.String(htmlEscape(chFileList[-1]))
-        topScope["CHAP-COUNT"] = templ.ttypes.String(self.__chIdx)
 
-        topScope["HAS-ENDNOTES"] = templ.ttypes.String(hasEndnotes)
+        topScope["PART-COUNT"] = templ.ttypes.String(self.__prtNum)
+        topScope["BOOK-COUNT"] = templ.ttypes.String(self.__bkNum)
+        topScope["CHAP-COUNT"] = templ.ttypes.String(self.__chNum)
+
+        topScope["PART-FILE-LIST"] = templ.ttypes.List(prtFiles)
+        topScope["BOOK-FILE-LIST"] = templ.ttypes.List(bkFiles)
+        topScope["CHAP-FILE-LIST"] = templ.ttypes.List(chFiles)
+        topScope["GEN-FILE-LIST"] = templ.ttypes.List(genFiles)
+
+        topScope["LAST-GEN-FILE-HTML"] = topScope["LAST-CHAP-FILE-HTML"] = templ.ttypes.String(htmlEscape(self.chapterFileName(self.__chNum)))
+
+        hasEndnotes = False
+        for notes in self.__enList:
+            if len(notes) > 0:
+                hasEndnotes = True
+                break
+        topScope["HAS-ENDNOTES"] = templ.ttypes.String("0")
+        if hasEndnotes:
+            topScope["HAS-ENDNOTES"] = templ.ttypes.String("1")
+
         topScope["ENDNOTES-LIST"] = templ.ttypes.List(self.__enList)
         topScope["ENDNOTES-HTML"] = templ.ttypes.String(self.generate_ENDNOTES_HTML())
 
-        topScope["MANIFEST-XML"] = templ.ttypes.String(self.generate_MANIFEST_XML(chFileList))
+        topScope["MANIFEST-XML"] = templ.ttypes.String(self.generate_MANIFEST_XML())
         topScope["SPINE-XML"] = templ.ttypes.String(self.generate_SPINE_XML())
         topScope["NAV-HTML"] = templ.ttypes.String(self.generate_NAV_HTML())
         topScope["NCX-XML"] = templ.ttypes.String(self.generate_NCX_XML())
         topScope["TOC-HTML"] = templ.ttypes.String(self.generate_TOC_HTML())
 
+
+        #Now we can process all the other template files in the template dir.
         for path in templFiles:
             src = os.path.join(self.__templateDir, path)
             destname = os.path.splitext(path)[0]
             content = obuffer.getvalue()
             ostream.close()
             istream.close()
-            zf.writestr(destname, content)
+            self.__writeGenFile(destname, content)
 
-        zf.close()
+        self.__zipfile.close()
 
+            
+    def preloadTitledScope(self, scope, key, titled, idx, total, number, isFirst, isLast):
+        """
+        Prepopualtes a scope for a titled object. This is done before
+        we know if it's explicit or not.
 
-    def writeChapterContent(self, templStack, chOstream, prtNumber, bkNumber, chapter, isLastChapter):
+        :param scope: The scope to populate
+        :type scope: :py:class:`templ.stack.Scope`
+
+        :param str key: The key for the scope symbols: "PART", "BOOK", or "CHAP"
+
+        :param int idx: The index of the object within it's parent, starting at 0.
+
+        :param int total: The total number of elements in the parent object.
+
+        :param int number: The overall number of this element counting (this type of
+        element) continuously through the entire document, starting at 1.
+
+        :param bool isFirst: Indicates whether or not this is the first section
+            of its kind in the entire document.
+        :param bool isLast: Indicats whether or not this is the last section of
+            its kind in the entire document.
+        """
+        scope[key + "-NUM"] = templ.ttypes.String(number)
+        scope[key + "-IDX"] = templ.ttypes.String(idx)
+        scope[key + "-LEN"] = templ.ttypes.String(len(titled))
+
+        scope[key + "-IS-FIRST"] = templ.ttypes.String("0")
+        scope[key + "-IS-LAST"] = templ.ttypes.String("0")
+        if isFirst:
+            scope[key + "-IS-FIRST"] = templ.ttypes.String("1")
+        if isLast:
+            scope[key + "-IS-LAST"] = templ.ttypes.String("1")
+
+        #Defaults
+        scope[key + "-TITLE"] = templ.ttypes.String("")
+        scope[key + "-EXP"] = templ.ttypes.String("0")
+
+
+    def loadTitledScope(self, scope, key, titled, idx, total, number):
+        """
+        Populates explicitely titled sections.
+
+        :param scope: The scope to populate
+        :type scope: :py:class:`templ.stack.Scope`
+
+        :param str key: The key for the scope symbols: "PART", "BOOK", or "CHAP"
+
+        :param titled: The titled section.
+        :type titled: :py:class:`~Tome.TitledDivision`
+
+        :param int idx: The index of the object within it's parent, starting at 0.
+
+        :param int total: The total number of elements in the parent object.
+
+        :param int number: The overall number of this element counting (this type of
+        element) continuously through the entire document, starting at 1.
+        """
+        scope[key + "-TITLE"] = templ.ttypes.String(titled.title())
+        scope[key + "-EXP"] = templ.ttypes.String("1")
+
+        scope[key + "-TITLE-LIST"] = templ.ttypes.List(titled.allTitles())
+        scope[key + "-TITLE"] = templ.ttypes.String(self.generate_any_TITLE(titled))
+        scope[key + "-TITLE-SEP-HTML"] = templ.ttypes.String(self.generate_any_TITLE_SEP_HTML(titled))
+        scope[key + "-TITLES-HTML"] = templ.ttypes.String(self.generate_any_TITLES_HTML(titled))
+        scope[key + "-SUBTITLES-HTML"] = templ.ttypes.String(self.generate_any_SUBTITLES_HTML(titled))
+        scope[key + "-SHORTMARK"] = templ.ttypes.String(self.generate_any_SHORTMARK(titled))
+        scope[key + "-HAS-SHORTMARK"] = templ.ttypes.String(self.generate_any_HAS_SHORTMARK(titled))
+        scope[key + "-SHORTMARK-SEP-HTML"] = templ.ttypes.String(self.generate_any_SHORTMARK_SEP_HTML(titled))
+
+        scope["REL-PREV-HTML"] = templ.ttypes.String(self.generate_REL_PREV_HTML())
+        scope["HAS-PREV"] = templ.ttypes.String("0")
+        scope["PREV-FILE"] = templ.ttypes.String("")
+        if self.__fileIdx > 0:
+            scope["HAS-PREV"] = templ.ttypes.String("1")
+            scope["PREV-FILE"] = templ.ttypes.String(self.__fileList[self.__fileIdx-1])
+
+        scope["REL-NEXT-HTML"] = templ.ttypes.String(self.generate_REL_NEXT_HTML())
+        scope["HAS-NEXT"] = templ.ttypes.String("0")
+        scope["NEXT-FILE"] = templ.ttypes.String("")
+        if self.__fileIdx+1 < len(self.__fileList):
+            scope["HAS-NEXT"] = templ.ttypes.String("1")
+            scope["NEXT-FILE"] = templ.ttypes.String(self.__fileList[self.__fileIdx+1])
+
+
+    def generatePart(self, templStack, part, prtIdx, parentLength):
+        """
+        :param templStack: The templ stack to use.
+        :type templStack: :py:class:`templ.stack.Stack`
+
+        :param part: The part object to generate.
+        :type part: `~Tome.Part`
+
+        :param int prtIdx: The index into the tome of the part, starting at 0.
+
+        :param int parentLength: The number of parts in the tome.
+
+        `__prtNum` is the number of the part through the whole tome, starting at 1.
+        """
+        prtScope = templStack.push()
+
+        isFirst = prtIdx == 0
+        isLast = (prtIdx+1) == parentLength
+        self.preloadTitledScope(prtScope, "PART", part, prtIdx, parentLength, self.__prtNum, isFirst, isLast)
+
+        prtTitle = part.title()
+        if prtTitle is not None:
+            #Explicit, so create the title page.
+            self.loadTitledScope(prtScope, "PART", part, prtIdx, parentLength, self.__prtNum)
+
+            #Open the template file.
+            try:
+                #TODO: Buffer this once, instead of reading it every time.
+                istream = open(self.__partTitleTemplatePath, "r")
+            except Exception, e:
+                raise Exception("Error opening part template file for reading:", e)
+
+            prtOstream = cStringIO.StringIO()
+            ostream = templ.tstreams.TemplateStreamOutputStream(prtOstream)
+            templ.templ.processWithStack(istream, ostream, templStack, self.__partTitleTemplatePath, debug=False)
+            istream.close()
+            prtFileContent = prtOstream.getvalue()
+            prtOstream.close()
+
+            #Output the part file.
+            self.writePartFile(self.__prtNum, prtFileContent)
+
+        #Process all the books in this part.
+        for bkIdx in xrange(len(part)):
+            self.__bkNum += 1
+            self.generateBook(templStack, part[bkIdx], bkIdx, len(part), isFirst, isLast)
+
+        #Pop the part scope.
+        templStack.pop()
+
+
+    def writePartFile(self, number, contents):
+        self.__writeGenFile(self.partFileName(number), contents)
+
+    def writeBookFile(self, number, contents):
+        self.__writeGenFile(self.bookFileName(number), contents)
+
+    def writeChapterFile(self, number, contents):
+        self.__writeGenFile(self.chapterFileName(number), contents)
+
+    def __writeGenFile(self, relPath, contents):
+        #Add the generated book file to the zipfile.
+        self.__zipfile.writestr(relPath, contents)
+
+
+    def generateBook(self, templStack, book, bkIdx, parentLength, parentIsFirst, parentIsLast):
+        """
+        :param templStack: The templ stack to use.
+        :type templStack: :py:class:`templ.stack.Stack`
+
+        :param book: The book object to generate.
+        :type book: `~Tome.Book`
+
+        :param int bkIdx: The index into the part of the book, starting at 0.
+
+        :param int parentLength: The number of books in the part.
+
+        `__bkNum` is the number of the book through the whole tome, starting at 1.
+        """
+        bkScope = templStack.push()
+
+        isFirst = parentIsFirst and (bkIdx == 0)
+        isLast = parentIsLast and (bkIdx+1 == parentLength)
+        self.preloadTitledScope(bkScope, "BOOK", book, bkIdx, parentLength, self.__bkNum, isFirst, isLast)
+
+        bkTitle = book.title()
+        if bkTitle is not None:
+            #Explicit, so create the title page.
+            self.loadTitledScope(bkScope, "BOOK", book, bkIdx, parentLength, self.__bkNum)
+
+            #Open the template file.
+            try:
+                #TODO: Buffer this once, instead of reading it every time.
+                istream = open(self.__bookTitleTemplatePath, "r")
+            except Exception, e:
+                raise Exception("Error opening book template file for reading:", e)
+
+            bkOstream = cStringIO.StringIO()
+            ostream = templ.tstreams.TemplateStreamOutputStream(bkOstream)
+            templ.templ.processWithStack(istream, ostream, templStack, self.__bookTitleTemplatePath, debug=False)
+            istream.close()
+            bkFileContent = bkOstream.getvalue()
+            bkOstream.close()
+
+            #Output the book file.
+            self.writeBookFile(self.__bkNum, bkFileContent)
+
+        #Process all the chapters in this book.
+        for chIdx in xrange(len(book)):
+            self.__chNum += 1
+            self.generateChapter(templStack, book[chIdx], chIdx, len(book), isFirst, isLast)
+
+        #Pop the book scope.
+        templStack.pop()
+
+
+
+    def generateChapter(self, templStack, chapter, chIdx, parentLength, parentIsFirst, parentIsLast):
+        """
+        :param templStack: The templ stack to use.
+        :type templStack: :py:class:`templ.stack.Stack`
+
+        :param chapter: The chapter object to generate.
+        :type chapter: `~Tome.Chapter`
+
+        :param int chIdx: The index into the book of the chapter, starting at 0.
+
+        :param int parentLength: The number of chapters in the book.
+
+        `__chNum` is the number of the chapter through the whole tome, starting at 1.
+        """
+        chScope = templStack.push()
+
+        isFirst = parentIsFirst and (chIdx == 0)
+        isLast = parentIsLast and ((chIdx+1) == parentLength)
+        self.preloadTitledScope(chScope, "CHAP", chapter, chIdx, parentLength, self.__chNum, isFirst, isLast)
+        self.loadTitledScope(chScope, "CHAP", chapter, chIdx, parentLength, self.__chNum)
+
+        #Legacy symbols:
+        chScope["CHAP-REL-PREV-HTML"] = templ.ttypes.String(self.generate_REL_PREV_HTML())
+        chScope["CHAP-REL-NEXT-HTML"] = templ.ttypes.String(self.generate_REL_NEXT_HTML())
+
+        #Open the template file.
+        try:
+            #TODO: Buffer this once, instead of reading it every time.
+            istream = open(self.__chapterTemplatePath, "r")
+        except Exception, e:
+            raise Exception("Error opening chapter template file for reading:", e)
+
+        self.generateChapterContent(chScope, chapter)
+
+        chOstream = cStringIO.StringIO()
+        ostream = templ.tstreams.TemplateStreamOutputStream(chOstream)
+        templ.templ.processWithStack(istream, ostream, templStack, self.__chapterTemplatePath, debug=False)
+        istream.close()
+        chFileContent = chOstream.getvalue()
+        chOstream.close()
+
+        #Output the book file.
+        self.writeChapterFile(self.__chNum, chFileContent)
+
+
+        #Pop the chap scope.
+        templStack.pop()
+
+
+    def generateChapterContent(self, chScope, chapter):
         """
         Writes all the content files.
         """
 
-        #A new scope for this chapter, which we'll pop before we go.
-        chapScope = templStack.push()
-
-        ### Populate the scope with some initial stuff.
-        chNum = str(self.__chIdx)
-        chUid = self.__chUid(self.__chIdx)
-        chAllTitles = chapter.allTitles()
-        chapScope["CHAP-NUM"] = templ.ttypes.String(chNum)
-        chapScope["CHAP-REL-PREV-HTML"] = templ.ttypes.String("")
-        chapScope["CHAP-REL-NEXT-HTML"] = templ.ttypes.String("")
-        chapScope["CHAP-IS-FIRST"] = templ.ttypes.String("1")
-        chapScope["CHAP-IS-LAST"] = templ.ttypes.String("1")
-        if self.__chIdx > 1:
-            chapScope["CHAP-IS-FIRST"] = templ.ttypes.String("0")
-            chapScope["CHAP-REL-PREV-HTML"] = templ.ttypes.String(self.generate_CHAP_REL_PREV_HTML())
-        if not isLastChapter:
-            chapScope["CHAP-IS-LAST"] = templ.ttypes.String("0")
-            chapScope["CHAP-REL-NEXT-HTML"] = templ.ttypes.String(self.generate_CHAP_REL_NEXT_HTML())
-        chapScope["CHAP-TITLE-LIST"] = templ.ttypes.List(chAllTitles)
-        chapScope["CHAP-TITLE"] = templ.ttypes.String(self.generate_CHAP_TITLE(chapter))
-        chapScope["CHAP-TITLES-HTML"] = templ.ttypes.String(self.generate_CHAP_TITLES_HTML(chapter))
-        chapScope["CHAP-TITLE-SEP-HTML"] = templ.ttypes.String(self.generate_CHAP_TITLE_SEP_HTML(chapter))
-        chapScope["CHAP-SHORTMARK"] = templ.ttypes.String(self.generate_CHAP_SHORTMARK(chapter))
-        chapScope["CHAP-HAS-SHORTMARK"] = templ.ttypes.String(self.generate_CHAP_HAS_SHORTMARK(chapter))
-        chapScope["CHAP-SHORTMARK-SEP-HTML"] = templ.ttypes.String(self.generate_CHAP_SHORTMARK_SEP_HTML(chapter))
-        chapScope["CHAP-SCENE-COUNT"] = templ.ttypes.String(len(chapter))
-
+        #Legacy symbol:
+        chScope["CHAP-SCENE-COUNT"] = templ.ttypes.String(len(chapter))
 
 
         #### Generate chapter content.
 
-        #Open a buffer to write out content to. After we generate the content, we'll stick it into the templ
-        # scope and process our chapter template file with it.
+        #Add a new set of endnotes for this chapter.
+        self.__enList.append([])
+
+        #Open a buffer to write out content to. After we generate the content,
+        # we'll stick it into the templ scope.
         chContentBuffer = cStringIO.StringIO()
 
         ### Iterate over scenes.
         scCount = len(chapter)
         scLastScene = scCount - 1
-        for i in xrange(scCount):
-            scene = chapter[i]
-            scNum = str(i+1)
+        for scIdx in xrange(scCount):
+            scene = chapter[scIdx]
 
             ### Generate the content for the scene.
 
             #Write an anchor for the scene.
-            chContentBuffer.write("        <a id='ch{chNum}Scene{scNum}' />\n".format(
-                chNum = chNum,
-                scNum = scNum
+            chContentBuffer.write("        <a id='ch{chNum}Scene{scIdx}' />\n".format(
+                chNum = self.__chNum,
+                scIdx = scIdx+1
             ))
 
             #Iterate over each paragraph in the scene.
                 classes = []
                 if j == 0:
                     classes.append("firstPar")
-                if i == 0:
+                if scIdx == 0:
                     classes.append("firstScene")
 
                 classHtml = ""
 
                 #Iterate over each toplevel segment in the paragraph.
                 for k in xrange(len(paragraph)):
-                    self.writeSegment(chContentBuffer, paragraph[k], dropCap = (i==0 and j==0 and k==0))
+                    self.writeSegment(chContentBuffer, paragraph[k], dropCap = (scIdx==0 and j==0 and k==0))
 
                 chContentBuffer.write("\n</%s>\n\n" % tag)
 
             #Scene separator
             lastScene = ""
             firstScene = ""
-            if i == scLastScene:
+            if scIdx == scLastScene:
                 lastScene = "lastScene"
-            if i == 0:
+            if scIdx == 0:
                 firstScene = "firstScene"
             chContentBuffer.write("        <hr class='sceneSep %s %s' />\n\n" % (firstScene, lastScene))
 
-        chapScope["CHAP-CONTENT"] = templ.ttypes.String(chContentBuffer.getvalue())
+        chScope["CHAP-CONTENT"] = templ.ttypes.String(chContentBuffer.getvalue())
         chContentBuffer.close()
 
 
         ### Process end notes for the chapter.
         chEndNotes = self.__enList[-1]
-        chapScope["CHAP-ENDNOTES-LIST"] = templ.ttypes.List(chEndNotes)
+        chScope["CHAP-ENDNOTES-LIST"] = templ.ttypes.List(chEndNotes)
 
         #Now if there really are end notes, overwrite with those.
         if len(chEndNotes) > 0:
-            chapScope["CHAP-HAS-ENDNOTES"] = templ.ttypes.String("1")
-            chapScope["CHAP-ENDNOTES-TITLE-HTML"] = templ.ttypes.String(self.generate_CHAP_ENDNOTES_TITLE_HTML(chapter, self.__chIdx))
-            chapScope["CHAP-ENDNOTES-HTML"] = templ.ttypes.String(self.generate_CHAP_ENDNOTES_HTML(chapter, chEndNotes, self.__chIdx))
+            chScope["CHAP-HAS-ENDNOTES"] = templ.ttypes.String("1")
+            chScope["CHAP-ENDNOTES-TITLE-HTML"] = templ.ttypes.String(self.generate_CHAP_ENDNOTES_TITLE_HTML(chapter, self.__chNum))
+            chScope["CHAP-ENDNOTES-HTML"] = templ.ttypes.String(self.generate_CHAP_ENDNOTES_HTML(chapter, chEndNotes, self.__chNum))
         else:
-            chapScope["CHAP-HAS-ENDNOTES"] = templ.ttypes.String("0")
-            chapScope["CHAP-ENDNOTES-TITLE-HTML"] = templ.ttypes.String("")
-            chapScope["CHAP-ENDNOTES-HTML"] = templ.ttypes.String("")
+            chScope["CHAP-HAS-ENDNOTES"] = templ.ttypes.String("0")
+            chScope["CHAP-ENDNOTES-TITLE-HTML"] = templ.ttypes.String("")
+            chScope["CHAP-ENDNOTES-HTML"] = templ.ttypes.String("")
 
-        #Open the chapter template file.
-        try:
-            istream = open(self.__chapterTemplatePath, "r")
-        except Exception, e:
-            raise Exception("Error opening chapter template file for reading:", e)
 
-        #Process to the given output stream.
-        ostream = templ.tstreams.TemplateStreamOutputStream(chOstream)
-        templ.templ.processWithStack(istream, ostream, templStack, self.__chapterTemplatePath, debug=False)
-        istream.close()
+    def chapterFileName(self, chNum):
+        return "chapter%s.xhtml" % self.__chUid(chNum)
 
-        templStack.pop()
+    def bookFileName(self, bkNum):
+        return "book%s.xhtml" % self.__chUid(bkNum)
 
-        return "chapter%s.xhtml" % chUid
+    def partFileName(self, prtNum):
+        return "part%s.xhtml" % self.__chUid(prtNum)
 
 
     def generate_AUTHORS_HTML(self):
 
 
     def generate_CHAP_TITLE(self, chapter):
-        title = chapter.title()
+        """
+        .. deprecated:: 1.1.0.0
+        Use <generate_any_TITLE> instead.
+        """
+        return generate_any_TITLE(chapter)
+
+    def generate_any_TITLE(self, titled):
+        title = titled.title()
         if title is None:
             return ""
         else:
             return title
 
     def generate_TITLE(self):
-        title = self.__tome.title()
-        if title is None:
-            return ""
-        else:
-            return title
+        return self.generate_any_TITLE(self.__tome)
 
     def generate_AUTHOR(self):
         authors = self.__tome.authors()
 
     def generate_CHAP_SHORTMARK(self, chapter):
         """
-        Returns a short title for the given chapter. If the chapter has a short mark, then it is the shortmark.
+        .. deprecated:: 1.1.0.0
+        Use <generate_any_SHORTMARK> instead.
+        """
+        return generate_any_SHORTMARK(chapter)
+
+    def generate_any_SHORTMARK(self, titled):
+        """
+        Returns a short title for the given titled section. If the section has a short mark, then it is the shortmark.
         If it doesn't, then it is the first title. If it doesn't have a title either, then it's an empty string.
         """
-        smark = chapter.shortMark()
+        smark = titled.shortMark()
         if smark is None:
-            return self.generate_CHAP_TITLE(chapter)
+            return self.generate_any_TITLE(titled)
         else:
             return smark
 
     def hasChapShortMark(self, chapter):
         """
-        Determines whether or not the given chapter has a short mark, as specified by <generate_CHAP_SHORTMARK>, which
+        .. deprecated:: 1.1.0.0
+        Use <hasShortMark> instead.
+        """
+        return hasShortMark(chapter)
+
+    def hasShortMark(self, titled):
+        """
+        Determines whether or not the given section has a short mark, as specified by <generate_any_SHORTMARK>, which
         really just means it has anything it can use for a shortmark.
         """
-        smark = chapter.shortMark()
+        smark = titled.shortMark()
         if smark is None:
-            if len(chapter.allTitles()) == 0:
+            if len(titled.allTitles()) == 0:
                 return False
             else:
                 return True
             return False
 
     def generate_CHAP_HAS_SHORTMARK(self, chapter):
-        if self.hasChapShortMark(chapter):
+        """
+        .. deprecated:: 1.1.0.0
+        Use <generate_any_HAS_SHORTMARK> instead.
+        """
+        return generate_any_HAS_SHORTMARK(chapter)
+
+    def generate_any_HAS_SHORTMARK(self, titled):
+        if self.hasShortMark(titled):
             return "1"
         else:
             return "0"
 
     def generate_CHAP_SHORTMARK_SEP_HTML(self, chapter):
         """
-        Returns an HTML string which can be used to separate the chapter shortmark from other text, for instance the chapter number.
-        If there is no short mark (as determined by :meth:`hasChapShortMark`), returns an empty string, so you can safely include this
+        .. deprecated:: 1.1.0.0
+        Use <generate_any_SHORTMARK_SEP_HTML> instead.
+        """
+        return generate_any_SHORTMARK_SEP_HTML(chapter)
+
+    def generate_any_SHORTMARK_SEP_HTML(self, titled):
+        """
+        Returns an HTML string which can be used to separate the section's shortmark from other text, for instance the section number.
+        If there is no short mark (as determined by :meth:`hasShortMark`), returns an empty string, so you can safely include this
         regardless of whether there is or isn't a short mark.
         """
-        if self.hasChapShortMark(chapter):
+        if self.hasShortMark(titled):
             return " &#8211; "
         else:
             return ""
 
     def generate_CHAP_TITLE_SEP_HTML(self, chapter):
-        if len(chapter.allTitles()) == 0:
+        """
+        .. deprecated:: 1.1.0.0
+        Use <generate_any_TITLE_SEP_HTML> instead.
+        """
+        return generate_any_TITLE_SEP_HTML(chapter)
+
+    def generate_any_TITLE_SEP_HTML(self, titled):
+        if len(titled.allTitles()) == 0:
             return ""
         else:
             return " &#8211; "
 
     def generate_CHAP_TITLES_HTML(self, chapter):
+        """
+        .. deprecated:: 1.1.0.0
+        Use <generate_any_TITLES_HTML> instead.
+        """
+        return generate_any_TITLES_HTML(chapter)
+
+    def generate_any_TITLES_HTML(self, titled):
         #Build the HTML for the chatper titles.
         titleLevel = 2
         titleIdx = 0
-        chHtmlAllTitles = ""
-        allTitles = chapter.allTitles()
+        htmlAllTitles = ""
+        allTitles = titled.allTitles()
         titleCount = len(allTitles)
         for title in allTitles:
             titleIdx += 1
             if titleIdx > 1:
                 subtitleClasses = "subtitle subtitle%d" % (titleIdx)
 
-            chHtmlAllTitles += "<h%d class='title %s %s'>%s</h%d>\n" % (titleLevel, subtitleClasses, lastTitle, htmlEscape(title), titleLevel)
+            htmlAllTitles += "<h%d class='title %s %s'>%s</h%d>\n" % (titleLevel, subtitleClasses, lastTitle, htmlEscape(title), titleLevel)
             if titleLevel < 6:
                 titleLevel += 1
-        return chHtmlAllTitles
+        return htmlAllTitles
 
     def generate_ENDNOTES_HTML(self):
         endNotesHtml = """
                     if len(chEndNotes) > 0:
                         chNum = str(chIdx)
                         chUid = self.__chUid(chIdx)
-                        shortMark = self.generate_CHAP_SHORTMARK_SEP_HTML(chapter) + self.generate_CHAP_SHORTMARK(chapter)
+                        shortMark = self.generate_any_SHORTMARK_SEP_HTML(chapter) + self.generate_any_SHORTMARK(chapter)
 
                         #Open the chapter section.
                         endNotesHtml += """
             <h2 class='title'>Chapter Notes</h2>""".format(chNum = str(chIdx))
 
             
+    def generate_SUBTITLES_HTML(self):
+        """
+        .. deprecated:: 1.1.0.0
+        Use `generate_any_SUBTITLES_HTML`, passing in `__tome`.
+        """
+        return self.generate_any_SUBTITLES_HTML(self.__tome)
 
-    def generate_SUBTITLES_HTML(self):
+
+    def generate_any_SUBTITLES_HTML(self, titled):
         ### Build some HTML for the sub titles.
         titleLevel = 2
         subtitleHtml = ""
-        allSubTitles = self.__tome.allTitles()[1:]
+        allSubTitles = titled.allTitles()[1:]
         stitleCount = len(allSubTitles)
         for titleIdx in xrange(stitleCount):
             lastTitle = ""
 
         
 
-    def generate_MANIFEST_XML(self, chapterFiles):
+    def generate_MANIFEST_XML(self, chapterFiles=None):
+        """
+        :param chapterFiles: **deprecated** No longer used.
+        """
         manifestXml = ""
-        chIdx = 0
-        for path in chapterFiles:
-            chIdx += 1
-            manifestXml += """
-        <item id="chapter{chUid}" href="{path}" media-type="application/xhtml+xml" />""".format(
-                chUid = self.__chUid(chIdx),
-                path = path
-            )
+        chNum = 0
+        bkNum = 0
+        prtNum = 0
+        for part in self.__tome:
+            prtNum += 1
+
+            #Title page files for explicit parts.
+            if part.title() is not None:
+                manifestXml += """
+            <item id="part{idx}" href="{path}" media-type="application/xhtml+xml" />""".format(
+                    idx = str(prtNum),
+                    path = self.partFileName(prtNum)
+                )
+
+            for book in part:
+                bkNum += 1
+
+                #Title page files for explicit books.
+                if book.title() is not None:
+                    manifestXml += """
+                <item id="book{idx}" href="{path}" media-type="application/xhtml+xml" />""".format(
+                        idx = str(bkNum),
+                        path = self.bookFileName(bkNum)
+                    )
+
+                for chapter in book:
+                    chNum += 1
+                    manifestXml += """
+                <item id="chapter{idx}" href="{path}" media-type="application/xhtml+xml" />""".format(
+                        idx = str(chNum),
+                        path = self.chapterFileName(chNum)
+                    )
 
         return manifestXml
 
     def generate_SPINE_XML(self):
         spineXml = ""
-        for i in xrange(self.__chIdx):
-            spineXml += """
-        <itemref idref="chapter{chUid}" />""".format(
-                chUid = self.__chUid(i+1)
-            )
+        chNum = 0
+        bkNum = 0
+        prtNum = 0
+        #TODO: FIXME: Only include part and book if explicitely titled.
+        for part in self.__tome:
+            prtNum += 1
+
+            #Title page files for explicit parts.
+            if part.title() is not None:
+                spineXml += """
+            <itemref idref="part{idx}" />""".format(
+                    idx = str(prtNum)
+                )
+
+            for book in part:
+                bkNum += 1
+
+                #Title page files for explicit books.
+                if book.title() is not None:
+                    spineXml += """
+                <itemref idref="book{idx}" />""".format(
+                        idx = str(bkNum)
+                    )
+
+                for chapter in book:
+                    chNum += 1
+                    spineXml += """
+                <itemref idref="chapter{idx}" />""".format(
+                        idx = str(chNum)
+                    )
+
         return spineXml
 
 
     def generate_TOC_HTML(self):
         tocHtml = ""
-        chIdx = 0
+        prtNum = 0
+        bkNum = 0
+        chNum = 0
         for part in self.__tome:
+            prtNum += 1
+            if part.title() is not None:
+                title = self.generate_any_TITLE_SEP_HTML(part) + htmlEscape(self.generate_any_TITLE(part))
+                tocHtml += """
+                <li class='bodymatter part' id='tocPart{idx}'>
+                    <a href="{path}">
+                        Part {idx}{title}
+                    </a>
+                </li>""".format(
+                    idx = str(prtNum),
+                    path = self.partFileName(prtNum),
+                    title = title
+                )
+                
             for book in part:
+                bkNum += 1
+                if book.title() is not None:
+                    title = self.generate_any_TITLE_SEP_HTML(book) + htmlEscape(self.generate_any_TITLE(book))
+                    tocHtml += """
+                    <li class='bodymatter book' id='tocBook{idx}'>
+                        <a href="{path}">
+                            Book {idx}{title}
+                        </a>
+                    </li>""".format(
+                        idx = str(bkNum),
+                        path = self.bookFileName(bkNum),
+                        title = title
+                    )
+
                 for chapter in book:
-                    chIdx += 1
-                    chUid = self.__chUid(chIdx)
-                    chNum = str(chIdx)
-
-                    title = self.generate_CHAP_TITLE_SEP_HTML(chapter) + htmlEscape(self.generate_CHAP_TITLE(chapter))
+                    chNum += 1
+                    title = self.generate_any_TITLE_SEP_HTML(chapter) + htmlEscape(self.generate_any_TITLE(chapter))
 
                     tocHtml += """
-                <li class='bodymatter chapter' id='tocChapter{chNum}'>
-                    <a href="chapter{chUid}.xhtml">
-                        Chapter {chNum}{title}
-                    </a>
-                </li>""".format(
-                        chNum = chNum,
-                        chUid = chUid,
+                        <li class='bodymatter chapter' id='tocChapter{idx}'>
+                            <a href="{path}">
+                                Chapter {idx}{title}
+                            </a>
+                        </li>""".format(
+                        idx = str(chNum),
+                        path = self.chapterFileName(chNum),
                         title = title
                     )
 
 
     def generate_NCX_XML(self):
         ncxXml = ""
-        chIdx = 0
+        chNum = 0
+        bkNum = 0
+        prtNum = 0
         for part in self.__tome:
+            prtNum += 1
+            prtOpened = False
+
+            #Title page files for explicit parts.
+            if part.title() is not None:
+                prtOpened = True
+
+                title = self.generate_any_SHORTMARK_SEP_HTML(part) + self.generate_any_SHORTMARK(part)
+
+                ### Add an entry for the part.
+                ncxXml += """
+    <navPoint id="part{num}">
+        <navLabel>
+            <text>Part {num}{title}</text>
+        </navLabel>
+        <content src="{path}"/>""".format(
+                    num = str(prtNum),
+                    title = title,
+                    path = self.partFileName(prtNum)
+                )
+
+
             for book in part:
+                bkNum += 1
+                bkOpened = False
+
+
+                #Title page files for explicit books.
+                if book.title() is not None:
+                    bkOpened = True
+
+                    title = self.generate_any_SHORTMARK_SEP_HTML(book) + self.generate_any_SHORTMARK(book)
+
+                    ### Add an entry for the book.
+                    ncxXml += """
+            <navPoint id="book{num}">
+                <navLabel>
+                    <text>Book {num}{title}</text>
+                </navLabel>
+                <content src="{path}"/>""".format(
+                        num = str(bkNum),
+                        title = title,
+                        path = self.bookFileName(bkNum)
+                    )
+
                 for chapter in book:
-                    chIdx += 1
-                    chUid = self.__chUid(chIdx)
-                    chNum = str(chIdx)
+                    chNum += 1
 
-                    title = self.generate_CHAP_SHORTMARK_SEP_HTML(chapter) + self.generate_CHAP_SHORTMARK(chapter)
+                    title = self.generate_any_SHORTMARK_SEP_HTML(chapter) + self.generate_any_SHORTMARK(chapter)
 
                     ### Add an entry for the chapter.
                     ncxXml += """
-        <navPoint id="chapter{chNum}">
-            <navLabel>
-                <text>Chapter {chNum}{title}</text>
-            </navLabel>
-            <content src="chapter{chUid}.xhtml"/>""".format(
-                        chNum = chNum,
-                        chUid = chUid,
-                        title=title
+                <navPoint id="chapter{num}">
+                    <navLabel>
+                        <text>Chapter {num}{title}</text>
+                    </navLabel>
+                    <content src="{path}"/>""".format(
+                        num = str(chNum),
+                        title = title,
+                        path = self.chapterFileName(chNum)
                     )
 
+
                     ### Add an entry for each scene in the chapter.
                     sceneCount = len(chapter)
-                    for scIdx in xrange(sceneCount):
-                        scNum = str(scIdx + 1)
-
+                    for scNum in xrange(sceneCount):
                         ncxXml += """
-            <navPoint id="ch{chNum}Scene{scNum}">
-                <navLabel>
-                    <text>Scene {scNum}</text>
-                </navLabel>
-                <content src="chapter{chUid}.xhtml#ch{chNum}Scene{scNum}" />
-            </navPoint>""".format(
-                            chUid = chUid,
-                            chNum = chNum,
-                            scNum = scNum
+                    <navPoint id="ch{chNum}Scene{scNum}">
+                        <navLabel>
+                            <text>Scene {scNum}</text>
+                        </navLabel>
+                        <content src="{path}#ch{chNum}Scene{scNum}" />
+                    </navPoint>""".format(
+                            chNum = str(chNum),
+                            scNum = str(scNum+1),
+                            path = self.chapterFileName(chNum)
                         )
 
                     ### Add an entry for the chapter end-notes, if there are any.
-                    if len(self.__enList[chIdx-1]) > 0:
+                    if len(self.__enList[chNum-1]) > 0:
                         ncxXml += """
-            <navPoint id="ch{chNum}Notes">
-                <navLabel>
-                    <text>SCh. {chNum} Notes</text>
-                </navLabel>
-                <content src="chapter{chUid}.xhtml#ch{chNum}Notes" />
-            </navPoint>""".format(
-                            chUid = chUid,
-                            chNum = chNum,
-                            scNum = scNum
+                    <navPoint id="ch{num}Notes">
+                        <navLabel>
+                            <text>Ch. {num} Notes</text>
+                        </navLabel>
+                        <content src="{path}#ch{num}Notes" />
+                    </navPoint>""".format(
+                            num = str(chNum),
+                            path = self.chapterFileName(chNum)
                         )
 
                     #Close the chapter item.
                     ncxXml += """
-        </navPoint>
-"""
+                </navPoint>"""
+
+                if bkOpened:
+                    ncxXml += """
+            </navPoint>"""
+
+            if prtOpened:
+                ncxXml += """
+        </navPoint>"""
         
-        return ncxXml
+        return ncxXml + "\n"
 
 
     def generate_CHAP_REL_PREV_HTML(self):
         """
-        Uses `__chIdx`, assumes that there is a previous chapter.
+        .. deprecated:: 1.1.0.0
+        Use `generate_REL_PREV_HTML` instead.
         """
-        chUid = self.__chUid(self.__chIdx-1)
-        return """<link rel="prev" href="chapter{chUid}.xhtml" />""".format(chUid = chUid)
+        return self.generate_REL_PREV_HTML()
+
+    def generate_REL_PREV_HTML(self):
+        """
+        Returns an HTML ``link`` element with ``rel="prev"`` attribute to point to the previous
+        file, or an empty string if there is no previous file. This uses `__fileList`, and assumes that
+        `__fileIdx` is the index of the current file.
+        """
+        if self.__fileIdx > 0:
+            return """<link rel="prev" href="{path}" />""".format(path = htmlEscape(self.__fileList[self.__fileIdx-1]))
+        else:
+            return ""
 
     def generate_CHAP_REL_NEXT_HTML(self):
         """
-        Uses `__chIdx`, assumes that there is a next chapter.
+        .. deprecated:: 1.1.0.0
+        Use `generate_REL_NEXT_HTML` instead.
         """
-        chUid = self.__chUid(self.__chIdx+1)
-        return """<link rel="next" href="chapter{chUid}.xhtml" />""".format(chUid = chUid)
+        return self.generate_REL_NEXT_HTML()
+
+    def generate_REL_NEXT_HTML(self):
+        """
+        Returns an HTML ``link`` element with ``rel="next"`` attribute to point to the next
+        file, or an empty string if there is no next file. This uses `__fileList`, and assumes that
+        `__fileIdx` is the index of the current file.
+        """
+        if self.__fileIdx+1 < len(self.__fileList):
+            return """<link rel="next" href="{path}" />""".format(path = htmlEscape(self.__fileList[self.__fileIdx+1]))
+        else:
+            return ""
+
 
 
     def generate_NAV_HTML(self):
                     chUid = self.__chUid(chIdx)
                     chNum = str(chIdx)
 
-                    title = self.generate_CHAP_SHORTMARK_SEP_HTML(chapter) + self.generate_CHAP_SHORTMARK(chapter)
+                    title = self.generate_any_SHORTMARK_SEP_HTML(chapter) + self.generate_any_SHORTMARK(chapter)
 
                     ### Add an entry for the chapter.
                     navHtml += """