From 1f93448791e78c6465228a9338db518a2ced8cac Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 20 Mar 2013 17:00:41 +0530 Subject: [PATCH] ToC Editor: Allow creating ToC from links in the book --- src/calibre/ebooks/oeb/polish/toc.py | 33 ++++++++++++++++++++++++++++ src/calibre/gui2/toc/main.py | 21 ++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/toc.py b/src/calibre/ebooks/oeb/polish/toc.py index abac6bd7d1..3a72b837c8 100644 --- a/src/calibre/ebooks/oeb/polish/toc.py +++ b/src/calibre/ebooks/oeb/polish/toc.py @@ -39,6 +39,10 @@ class TOC(object): c.parent = self return c + def remove(self, child): + self.children.remove(child) + child.parent = None + def __iter__(self): for c in self.children: yield c @@ -191,6 +195,8 @@ def elem_to_toc_text(elem): text = elem.get('alt', '') text = re.sub(r'\s+', ' ', text.strip()) text = text[:1000].strip() + if not text: + text = _('(Untitled)') return text def from_xpaths(container, xpaths): @@ -229,6 +235,33 @@ def from_xpaths(container, xpaths): return tocroot +def from_links(container): + toc = TOC() + link_path = XPath('//h:a[@href]') + seen_titles, seen_dests = set(), set() + for spinepath in container.spine_items: + name = container.abspath_to_name(spinepath) + root = container.parsed(name) + for a in link_path(root): + href = a.get('href') + if not href or not href.strip(): + continue + dest = container.href_to_name(href, base=name) + frag = href.rpartition('#')[-1] or None + if (dest, frag) in seen_dests: + continue + seen_dests.add((dest, frag)) + text = elem_to_toc_text(a) + if text in seen_titles: + continue + seen_titles.add(text) + toc.add(text, dest, frag=frag) + verify_toc_destinations(container, toc) + for child in toc: + if not child.dest_exists: + toc.remove(child) + 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 d0e2a8e0f6..a9c160a0d4 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, 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) + get_toc, add_id, TOC, commit_toc, from_xpaths, from_links) from calibre.gui2 import Application, error_dialog, gprefs from calibre.gui2.progress_indicator import ProgressIndicator from calibre.gui2.toc.location import ItemEdit @@ -33,6 +33,7 @@ class ItemView(QFrame): # {{{ flatten_item = pyqtSignal() go_to_root = pyqtSignal() create_from_xpath = pyqtSignal(object) + create_from_links = pyqtSignal() def __init__(self, parent): QFrame.__init__(self, parent) @@ -79,7 +80,15 @@ class ItemView(QFrame): # {{{ ' heading tags. Uses the tags.'))) l.addWidget(b) - + self.lb = b = QPushButton(_('Generate ToC from &links')) + b.clicked.connect(self.create_from_links) + b.setToolTip(textwrap.fill(_( + 'Generate a Table of Contents from all the links in the book.' + ' Links that point to destinations that do not exist in the book are' + ' ignored. Also multiple links with the same destination or the same' + ' text are ignored.' + ))) + l.addWidget(b) l.addStretch() self.w1 = la = QLabel(_('WARNING: calibre only supports the ' @@ -270,6 +279,7 @@ class TOCView(QWidget): # {{{ self.item_view.delete_item.connect(self.delete_current_item) 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.flatten_item.connect(self.flatten_item) i.go_to_root.connect(self.go_to_root) l.addWidget(i, 0, 4, col, 1) @@ -490,6 +500,13 @@ class TOCView(QWidget): # {{{ _('No items were found that could be added to the Table of Contents.'), show=True) self.insert_toc_fragment(toc) + def create_from_links(self): + toc = from_links(self.ebook) + if len(toc) == 0: + return error_dialog(self, _('No items found'), + _('No links were found that could be added to the Table of Contents.'), show=True) + self.insert_toc_fragment(toc) + # }}} class TOCEditor(QDialog): # {{{