From fe1f2c79259aad4286a7c9194e8ab341c66b495c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 3 Apr 2013 09:58:58 +0530 Subject: [PATCH] ToC Editor: Allow generating the ToC directly from individual files inside the ebook. Useful for EPUBs that have individual chapters in single files. Fixes #1163520 (Request for new method to generate entries in ToC editor) --- src/calibre/ebooks/oeb/polish/toc.py | 29 ++++++++++++++++++++++++++++ src/calibre/gui2/toc/main.py | 21 +++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/oeb/polish/toc.py b/src/calibre/ebooks/oeb/polish/toc.py index 3a72b837c8..c84dd1b094 100644 --- a/src/calibre/ebooks/oeb/polish/toc.py +++ b/src/calibre/ebooks/oeb/polish/toc.py @@ -262,6 +262,35 @@ def from_links(container): toc.remove(child) return toc +def find_text(node): + LIMIT = 200 + pat = re.compile(r'\s+') + for child in node: + if isinstance(child, etree._Element): + text = xml2text(child).strip() + text = pat.sub(' ', text) + if len(text) < 1: + continue + if len(text) > LIMIT: + # Look for less text in a child of this node, recursively + ntext = find_text(child) + return ntext or (text[:LIMIT] + '...') + else: + return text + +def from_files(container): + toc = TOC() + for spinepath in container.spine_items: + name = container.abspath_to_name(spinepath) + root = container.parsed(name) + body = XPath('//h:body')(root) + if not body: + continue + text = find_text(body[0]) + if text: + toc.add(text, name) + return toc + def add_id(container, name, loc): root = container.parsed(name) body = root.xpath('//*[local-name()="body"]')[0] diff --git a/src/calibre/gui2/toc/main.py b/src/calibre/gui2/toc/main.py index 74886bbf63..7cb4f9b462 100644 --- a/src/calibre/gui2/toc/main.py +++ b/src/calibre/gui2/toc/main.py @@ -18,7 +18,7 @@ from PyQt4.Qt import (QPushButton, QFrame, QVariant, QMenu, QInputDialog, from calibre.ebooks.oeb.polish.container import get_container, AZW3Container from calibre.ebooks.oeb.polish.toc import ( - get_toc, add_id, TOC, commit_toc, from_xpaths, from_links) + get_toc, add_id, TOC, commit_toc, from_xpaths, from_links, from_files) from calibre.gui2 import Application, error_dialog, gprefs from calibre.gui2.progress_indicator import ProgressIndicator from calibre.gui2.toc.location import ItemEdit @@ -126,6 +126,7 @@ class ItemView(QFrame): # {{{ go_to_root = pyqtSignal() create_from_xpath = pyqtSignal(object) create_from_links = pyqtSignal() + create_from_files = pyqtSignal() flatten_toc = pyqtSignal() def __init__(self, parent): @@ -183,6 +184,15 @@ class ItemView(QFrame): # {{{ ))) l.addWidget(b) + self.cfb = b = QPushButton(_('Generate ToC from &files')) + b.clicked.connect(self.create_from_files) + b.setToolTip(textwrap.fill(_( + 'Generate a Table of Contents from individual files in the book.' + ' Each entry in the ToC will point to the start of the file, the' + ' text of the entry will be the "first line" of text from the file.' + ))) + l.addWidget(b) + self.xpb = b = QPushButton(_('Generate ToC from &XPath')) b.clicked.connect(self.create_from_user_xpath) b.setToolTip(textwrap.fill(_( @@ -577,6 +587,7 @@ class TOCView(QWidget): # {{{ i.add_new_item.connect(self.add_new_item) i.create_from_xpath.connect(self.create_from_xpath) i.create_from_links.connect(self.create_from_links) + i.create_from_files.connect(self.create_from_files) i.flatten_item.connect(self.flatten_item) i.flatten_toc.connect(self.flatten_toc) i.go_to_root.connect(self.go_to_root) @@ -778,6 +789,14 @@ class TOCView(QWidget): # {{{ _('No links were found that could be added to the Table of Contents.'), show=True) self.insert_toc_fragment(toc) + def create_from_files(self): + toc = from_files(self.ebook) + if len(toc) == 0: + return error_dialog(self, _('No items found'), + _('No files were found that could be added to the Table of Contents.'), show=True) + self.insert_toc_fragment(toc) + + # }}} class TOCEditor(QDialog): # {{{