Parse NCX directory for chapter titles. Show chapter titles for bookmarks.
authorAkos Polster <polster@marzipan.pipacs.com>
Sat, 24 Jul 2010 15:13:23 +0000 (17:13 +0200)
committerAkos Polster <polster@marzipan.pipacs.com>
Sat, 24 Jul 2010 15:13:23 +0000 (17:13 +0200)
TODO.txt
book.cpp
book.h
bookmarksdialog.cpp
bookview.cpp
dorian.pro
ncxhandler.h [new file with mode: 0644]
opserrorhandler.h [deleted file]
opshandler.h
pkg/changelog
xmlerrorhandler.h [new file with mode: 0644]

index ae2c05a..ee989e8 100644 (file)
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,6 +1,6 @@
-To dos migrated to Maemo Garage:
-https://garage.maemo.org/tracker/?group_id=1757 . Old to dos below are kept as
-historical artifacts.
+To do list migrated to Maemo Garage:
+https://garage.maemo.org/tracker/?group_id=1757 . Old to do list below is kept as
+historical artifact.
 
 To Do
 -----
index bd96c45..489e087 100644 (file)
--- a/book.cpp
+++ b/book.cpp
@@ -9,10 +9,11 @@
 
 #include "book.h"
 #include "opshandler.h"
-#include "opserrorhandler.h"
+#include "xmlerrorhandler.h"
 #include "extractzip.h"
 #include "library.h"
 #include "containerhandler.h"
+#include "ncxhandler.h"
 
 Book::Book()
 {
@@ -77,7 +78,7 @@ void Book::fail(const QString &details, const QString &error)
         "</title></head><body><h1>" + Qt::escape(error) + "</h1><p>" +
         Qt::escape(details) + "</p></body></html>";
     content["error"].href = errorPage;
-    content["error"].type = "text/html";
+    content["error"].name = "Error";
 }
 
 bool Book::extract()
@@ -128,21 +129,37 @@ bool Book::parse()
     qDebug() << "Book::parse";
 
     bool ret = false;
-    QFile bookFile(opsPath());
+    QString opsFileName = opsPath();
+    qDebug() << " Parsing OPS file" << opsFileName;
+    QFile opsFile(opsFileName);
     QXmlSimpleReader reader;
-    QXmlInputSource *source = new QXmlInputSource(&bookFile);
+    QXmlInputSource *source = new QXmlInputSource(&opsFile);
     OpsHandler *opsHandler = new OpsHandler(*this);
-    OpsErrorHandler *opsErrorHandler = new OpsErrorHandler();
+    XmlErrorHandler *errorHandler = new XmlErrorHandler();
     reader.setContentHandler(opsHandler);
-    reader.setErrorHandler(opsErrorHandler);
-
+    reader.setErrorHandler(errorHandler);
     ret = reader.parse(source);
-    if (!ret) {
-        qCritical() << "*** Book::parse: XML parsing failed";
-    }
-
+    delete errorHandler;
     delete opsHandler;
     delete source;
+
+    // If there is an "ncx" item in content, parse it: That's the real table of
+    // contents
+    if (content.contains("ncx")) {
+        QString ncxFileName = content["ncx"].href;
+        qDebug() << " Parsing NCX file" << ncxFileName;
+        QFile ncxFile(ncxFileName);
+        source = new QXmlInputSource(&ncxFile);
+        NcxHandler *ncxHandler = new NcxHandler(*this);
+        errorHandler = new XmlErrorHandler();
+        reader.setContentHandler(ncxHandler);
+        reader.setErrorHandler(errorHandler);
+        ret = reader.parse(source);
+        delete ncxHandler;
+        delete errorHandler;
+        delete source;
+    }
+
     return ret;
 }
 
@@ -286,15 +303,16 @@ QString Book::opsPath()
     QXmlSimpleReader reader;
     QXmlInputSource *source = new QXmlInputSource(&container);
     ContainerHandler *containerHandler = new ContainerHandler();
-    OpsErrorHandler *opsErrorHandler = new OpsErrorHandler();
+    XmlErrorHandler *errorHandler = new XmlErrorHandler();
     reader.setContentHandler(containerHandler);
-    reader.setErrorHandler(opsErrorHandler);
+    reader.setErrorHandler(errorHandler);
     if (reader.parse(source)) {
         ret = tmpDir() + "/" + containerHandler->rootFile;
         mRootPath = QFileInfo(ret).absoluteDir().absolutePath();
         qDebug() << " OSP path" << ret;
         qDebug() << " Root dir" << mRootPath;
     }
+    delete errorHandler;
     delete containerHandler;
     delete source;
     return ret;
diff --git a/book.h b/book.h
index 974b7c7..6449112 100644 (file)
--- a/book.h
+++ b/book.h
@@ -16,7 +16,7 @@ public:
     struct ContentItem
     {
         QString href;
-        QString type;
+        QString name;
     };
 
     /** Bookmark: a volume index and a relative position in volume. */
@@ -97,6 +97,9 @@ public:
     QString subject;                        //< Subject.
     QString source;                         //< Source.
     QString rights;                         //< Rights.
+    QString tocPath;                        //< Path to toc ncx.
+    QString coverPath;                      //< Path to cover html.
+    QString coverImagePath;                 //< Path to cover image.
 
 protected:
     /** Indicate failure by creating a single "error" content item. */
index 9594bbd..14b3b3e 100644 (file)
@@ -15,7 +15,9 @@ BookmarksDialog::BookmarksDialog(Book *book_, QWidget *parent):
     list = new QListWidget(this);
     list->setSelectionMode(QAbstractItemView::SingleSelection);
     foreach (Book::Bookmark bookmark, book_->bookmarks()) {
-        list->addItem("Volume " + QString::number(bookmark.chapter + 1) + ", at " +
+        QString contentId = book_->toc[bookmark.chapter];
+        QString contentTitle = book_->content[contentId].name;
+        list->addItem(contentTitle + ", at " +
                       QString::number((int)(bookmark.pos * 100)) + "%");
     }
 
index d960e26..cea29f2 100644 (file)
@@ -167,13 +167,11 @@ void BookView::onSettingsChanged(const QString &key)
     }
     else if (key == "font") {
         QString face = Settings::instance()->value("font").toString();
-        qDebug() << "" << face;
         settings()->setFontFamily(QWebSettings::StandardFont, face);
     }
     else if (key == "scheme") {
         QWebFrame *frame = page()->mainFrame();
         QString scheme = Settings::instance()->value("scheme").toString();
-        qDebug() << "" << scheme;
         if ((scheme != "day") && (scheme != "night") && (scheme != "sand") &&
             (scheme != "default")) {
             scheme = "default";
@@ -183,8 +181,6 @@ void BookView::onSettingsChanged(const QString &key)
         QString scriptText = script.readAll();
         script.close();
         QVariant ret = frame->evaluateJavaScript(scriptText);
-        qDebug() << "" << script.fileName() << ":" << scriptText;
-        qDebug() << "" << ret;
     }
 }
 
index 217f457..0caa0af 100644 (file)
@@ -37,9 +37,10 @@ HEADERS += \
     settingswindow.h \
     settings.h \
     bookmarksdialog.h \
-    opserrorhandler.h \
+    xmlerrorhandler.h \
     containerhandler.h \
-    sortedlibrary.h
+    sortedlibrary.h \
+    ncxhandler.h
 
 RESOURCES += \
     dorian.qrc
diff --git a/ncxhandler.h b/ncxhandler.h
new file mode 100644 (file)
index 0000000..0f808cb
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef NCXHANDLER_H
+#define NCXHANDLER_H
+
+#include <QXmlContentHandler>
+
+#include "book.h"
+
+/** XML content handler for NCX format. */
+class NcxHandler: public QXmlContentHandler
+{
+public:
+    bool endDocument() {return true;}
+    bool endPrefixMapping(const QString &) {return true;}
+    QString errorString() const {return "";}
+    bool ignorableWhitespace(const QString &) {return true;}
+    bool processingInstruction(const QString &, const QString &) {return true;}
+    void setDocumentLocator(QXmlLocator *) {}
+    bool skippedEntity(const QString &) {return true;}
+    bool startDocument() {return true;}
+    bool startPrefixMapping(const QString &, const QString &) {return true;}
+
+    NcxHandler(Book &b): book(b)
+    {
+        book.toc.clear();
+    }
+
+    bool characters(const QString &ch)
+    {
+        currentText += ch;
+        return true;
+    }
+
+    bool endElement(const QString &namespaceUri, const QString &name,
+                    const QString &qName)
+    {
+        (void)namespaceUri;
+        (void)qName;
+        if (name == "text") {
+            contentTitle = currentText;
+        } else if (name == "navPoint") {
+            Book::ContentItem item;
+            item.href = book.rootPath() + "/" + contentUrl;
+            item.name = contentTitle;
+            book.content[contentId] = item;
+            book.toc.append(contentId);
+        }
+        return true;
+    }
+
+    bool startElement(const QString &namespaceUri, const QString &name,
+                      const QString &qName, const QXmlAttributes &attrs)
+    {
+        (void)namespaceUri;
+        (void)qName;
+        currentText = "";
+        if (name == "navPoint") {
+            contentId = attrs.value("id");
+        } else if (name == "content") {
+            contentUrl = attrs.value("src");
+        }
+        return true;
+    }
+
+private:
+    Book &book;
+    QString currentText;
+    QString contentId;
+    QString contentUrl;
+    QString contentTitle;
+};
+
+#endif // NCXHANDLER_H
diff --git a/opserrorhandler.h b/opserrorhandler.h
deleted file mode 100644 (file)
index f1a75f6..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef OPSERRORHANDLER_H
-#define OPSERRORHANDLER_H
-
-#include <QXmlErrorHandler>
-#include <QDebug>
-
-#include "book.h"
-
-/**
-  XML error handler for OPS format.
-  */
-class OpsErrorHandler: public QXmlErrorHandler
-{
-    bool error(const QXmlParseException &e) {
-        qDebug() << "OpsErrorHandler::error" << e.message() << "at line"
-                << e.lineNumber();
-        return true;
-    }
-    virtual QString errorString() const {return QString();}
-    virtual bool fatalError(const QXmlParseException &e) {
-        qDebug() << "OpsErrorHandler::fatalError" << e.message() << "at line"
-                <<  e.lineNumber();
-        return true;
-    }
-    virtual bool warning(const QXmlParseException &e) {
-        qDebug() << "OpsErrorHandler::warning" << e.message() << "at line"
-                << e.lineNumber();
-        return true;
-    }
-};
-
-#endif // OPSERRORHANDLER_H
index 6e1ee01..9b42c23 100644 (file)
@@ -9,7 +9,7 @@
 class OpsHandler: public QXmlContentHandler
 {
 public:
-    OpsHandler(Book &book): mBook(book) {}
+    OpsHandler(Book &b): book(b), partCount(0) {}
     bool endDocument() {return true;}
     bool endPrefixMapping(const QString &) {return true;}
     QString errorString() const {return "";}
@@ -24,7 +24,7 @@ public:
 
     bool characters(const QString &ch)
     {
-        mCurrentText += ch;
+        currentText += ch;
         return true;
     }
 
@@ -33,24 +33,24 @@ public:
     {
         (void)namespaceUri;
         (void)qName;
-        if (mCurrentText != "") {
+        if (currentText != "") {
             if (name == "title") {
-                mBook.title = mCurrentText;
+                book.title = currentText;
             }
             else if (name == "creator") {
-                mBook.creators.append(mCurrentText);
+                book.creators.append(currentText);
             }
             else if (name == "publisher") {
-                mBook.publisher = mCurrentText;
+                book.publisher = currentText;
             }
             else if (name == "subject") {
-                mBook.subject = mCurrentText;
+                book.subject = currentText;
             }
             else if (name == "source") {
-                mBook.source = mCurrentText;
+                book.source = currentText;
             }
             else if (name == "rights") {
-                mBook.rights = mCurrentText;
+                book.rights = currentText;
             }
         }
         return true;
@@ -60,30 +60,27 @@ public:
                       const QString &qName, const QXmlAttributes &attrs)
     {
         (void)namespaceUri;
-        (void)name;
         (void)qName;
-        (void)attrs;
-        mCurrentText = "";
+        currentText = "";
 
         if (name == "item") {
             Book::ContentItem item;
-            item.href = mBook.rootPath() + "/" + attrs.value("href");
-            item.type = attrs.value("media-type");
+            item.href = book.rootPath() + "/" + attrs.value("href");
+            item.name = QString("Part %1").arg(partCount + 1);
             QString key = attrs.value("id");
-            mBook.content[key] = item;
-            qDebug() << "OpsHandler::startElement: item" << key << "type"
-                    << item.type << "href" << item.href;
+            book.content[key] = item;
+            partCount++;
         }
         else if (name == "itemref") {
-            mBook.toc.append(attrs.value("idref"));
-            qDebug() << "OpsHandler::startElement: itemref" << attrs.value("idref");
+            book.toc.append(attrs.value("idref"));
         }
         return true;
     }
 
 private:
-    Book &mBook;
-    QString mCurrentText;
+    Book &book;
+    QString currentText;
+    int partCount;
 };
 
 #endif // OPSHANDLER_H
index d8e8981..4f46599 100644 (file)
@@ -4,7 +4,9 @@ dorian (0.0.10-1) unstable; urgency=low
   * Show message if selected book is already in the library
   * Make library window full screen on Maemo
   * Show busy indicator while loading book
-  * Create Maemo Garage web pages
+  * Create Maemo Garage web page
+  * Read chapter titles from NCX directory
+  * Display chapter titles for bookmarks
 
  -- Akos Polster <akos@pipacs.com>  Fri, 16 Jul 2010 20:00:00 +0200
 
diff --git a/xmlerrorhandler.h b/xmlerrorhandler.h
new file mode 100644 (file)
index 0000000..30082e7
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef XMLERRORHANDLER_H
+#define XMLERRORHANDLER_H
+
+#include <QXmlErrorHandler>
+#include <QDebug>
+
+/** Generic XML error handler. */
+class XmlErrorHandler: public QXmlErrorHandler
+{
+    bool error(const QXmlParseException &e) {
+        qCritical() << "*** XmlErrorHandler::error" << e.message() << "at line"
+                << e.lineNumber();
+        return true;
+    }
+    QString errorString() const {return QString();}
+    bool fatalError(const QXmlParseException &e) {
+        qCritical() << "*** XmlErrorHandler::fatalError" << e.message()
+                << "at line" <<  e.lineNumber();
+        return true;
+    }
+    bool warning(const QXmlParseException &e) {
+        qWarning() << "XmlErrorHandler::warning" << e.message() << "at line"
+                << e.lineNumber();
+        return true;
+    }
+};
+
+#endif // XMLERRORHANDLER_H