[转] – Design of an NCX handler class in C#

29 Jan 2010

Design of an NCX handler class in C#

Figure 1. shows a revised design for the Book Content screen of the online wysiwyg epub editor I’m developing.


Figure 1. Book content and editing screen

The main changes to the design are as follows:

  • The TreeView display of the NCX (Table of Contents) is replaced by a Repeater control. This allows more flexibility in the management of the NCX. This post explores the functionality required by a C# class that maintains the NCX document of an epub publication; access to that functionality is achieved through the Repeater control in a way that isn’t so easy with a TreeView. Each document now has a checkbox beside it, allowing the document to be selected for action.
  • A dropdown list of actions that can be applied to one or more selected content documents. This is the key feature that gives access to NCX management functionality and includes the following actions:
    • Remove one or more content documents from the publication.
    • Move a content document up or down in the reading order.
    • Insert a new content document before or after a given document.
    • Change the text that appears in the table of contents for a given document.
  • The TinyMCE editor configuration has been enriched so the author has more control over the initial styling of the text (but remember, features like selected font, font-size, and text/background colouring should be left to the reader who makes personal presentation choices when using a reading device). The new editor configuration also allows the more technical author to view the XHTML for the current content document.
  • When the user wants to add a content document, they now have the ability to specify the following items:
    • The name of the file where the new content should be written.
    • The document heading that should appear at the start of the content.
    • The text that should appear in the table of contents.

In Figure 2. the Actions dropdown list is shown expanded. This list provides the structure for much of the remainder of this post, with the addition of the following methods and properties:

  • A constructor that, given an NCX document, will instantiate the class.
  • A method for saving changes to the NCX back to disk. In the current design, changes are only written to the .epub file when the entire publication is saved.
  • Properties that allow the following items in the NCX to be set programmatically:
    • The title of the publication in the docTitle/text element.
    • The author of the publication in the docAuthor/text element.
    • The unique identifier of the publication in the meta element which has a name attribute of dtb:uid
    • The document publisher in the meta element which has a name attribute of epub-creator.


Figure 2. Actions on content documents

This editor application contains a class called ‘ncx’ which we will now examine.

Constructor
An instance of the ncx class is created by its constructor. An NCX is an XML document, so much of the functionality of NCX management is concerned with XML operations – finding individual nodes, getting and setting attribute values, managing NameSpaces etc.

The ncx constructor is given the filepath to the ncx document. This was extracted from the .epub file when the current book was selected. The <spine> element of the <package> has a toc attribute. This holds the identifier of the <manifest> item which gives the href of the NCX file. The path to this file is passed to the constructor.

The ncx document is loaded and parsed, then key nodes are extracted as follows:

  • <docTitle><text>Book Title</text></docTitle> – allowing the book’s title to be easily read and written using the Title property.
  • <docAuthor><text>Author name</text></docAuthor> – allowing the book’s author to be easily read and written using the Author property.
  • <head><meta name=’dtb:uid’/> – allowing the unique identifier of the publication to be easily read and written using the Identifier property.
  • <head><meta name=’epub-creator’/> – allowing the publisher to be easily read and written using the Publisher property.

Save
The converse of the constructor, where the NCX is read and parsed, the Save method writes the updated NCX back to disk. After it has been saved by this method, the new NCX will be saved to the .epub file the next time the publication is saved.

Remove
What does it mean to remove a content document from an epub publication? Complete removal means performing the following actions:

  • Delete the <item> from the <manifest>.
  • Delete the <itemref> from the <spine>.
  • Delete the <navPoint> from the <navMap> in the NCX.
  • Delete the content document from the file system.
  • Rebuild the container (.epub file).

The ncx class is concerned only with managing the NCX document, so the Remove method deletes the <navPoint> from the <navMap>. To keep the <navMap> tidy, the method also renumbers the playOrder attribute of all <navPoint> elements that came after the removed document.

Insert After and Insert Before
Authors and publishers commonly want to reorganise and rearrange content. This might involve changing the order in which content documents are presented and that is the topic of the next section. It might also involve the insertion of new content into an existing publication. As discussed above with the Remove method, adding a content document requires work elsewhere in the <package> – the <manifest> and the <spine> must also be updated.

The ncx class methods InsertAfter and InsertBefore make the required changes to the NCX document. The required behaviour is as follows:

  • In both cases a new <navPoint> is added to the <navMap>. The <navLabel><text> element of the <navPoint> is set to the given text. The src attribute of the <content> element is set to the given filename.
  • The value given to the playOrder attribute of the <navPoint> will depend on whether the action is to insert the new document before or after a selected document:
    • When the new document is inserted before a selected document, the new document takes the playOrder of the selected document. The selected document and all following <navPoint> elements have their playOrder incremented to move them down the reading order.
    • When the new document is inserted after a selected document, the new document takes the playOrder of the next document. That document and all following documents have their playOrder incremented, again moving them down the reading order.

Move Up and Move Down
To move a content document up or down in the reading order is relatively straightforward. The only requirement is to swap the playOrder values of two adjacent <navPoint> elements.

When a given document is to be moved up the reading order i.e. to be presented to the reader earlier, the playOrder of the given document is swapped with that of the previous document. When a document is to be moved down the reading order, it will swap playOrder values with the content document that follows it in the current reading order.

Of course, the <navMap> nodes must be sorted by playOrder before they are bound to the user interface controls. Only then will the table of contents look as expected.

The <spine> element of the <package> document also holds the linear reading order of the publication. Any changes made to the NCX should also be reflected there, but those actions are outside the scope of the ncx class.

Rename
The Rename method allows the content provider to change the text that describes a content document as seen in the table of contents. The action of this method is to store the given string in the <navLabel><text> element of the given <navPoint>.

Properties
The following properties provide convenient access to the elements and attributes of the NCX document that aren’t part of the document navigation (navMap).

Title
Gets or sets the publication title, held in the <docTitle><text> element.

Author
Gets or sets the publication author, held in the &t;docTitle><text> element.

Identifer
Gets or set the unique identifier of the publication which is held in the content attribute of the <head><meta> element with its name attribute set to dtb:uid.

Publisher
Gets or sets the publisher name which is held in the content attribute of the <head><meta> element with its name attribute set to epub-creator.