mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Edit book: new tool to automatically generate an inline (HTML) Table of Contents based on the current NCX Table of Contents. Accessed via Tools->Table of Contents->Insert inline Table of Contents.
This commit is contained in:
parent
b821dbd9b8
commit
62ce1a4e92
@ -235,7 +235,7 @@ Edit the Table of Contents
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There is a dedicated tool to ease editing of the Table of Contents. Launch it
|
||||
with :guilabel:`Tools->Edit Table of Contents`.
|
||||
with :guilabel:`Tools->Table of Contents->Edit Table of Contents`.
|
||||
|
||||
.. image:: images/tocedit.png
|
||||
:alt: The Edit Table of Contents tool
|
||||
@ -366,6 +366,20 @@ broken HTML/CSS. Therefore, if you dont want any auto-fixing to be performed,
|
||||
first use the Check Book tool to correct all problems and only then run
|
||||
beautify. Accessed via :guilabel:`Tools->Beautify all files`.
|
||||
|
||||
|
||||
Insert inline Table of Contents
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Normally in ebooks, the Table of Contents is separate from the main text and is
|
||||
typically accessed via a special Table of Contents button/menu in the ebook
|
||||
reading device. You can also have |app| automatically generate an *inline*
|
||||
Table of Contents that becomes part of the text of the book. It is
|
||||
generated based on the currently defined Table of Contents.
|
||||
|
||||
If you use this tool multiple times, each invocation will cause the previously
|
||||
created inline Table of Contents to be replaced. The tool can be accessed via
|
||||
:guilabel:`Tools->Table of Contents->Insert inline Table of Contents`.
|
||||
|
||||
.. _checkpoints:
|
||||
|
||||
Checkpoints
|
||||
|
@ -171,7 +171,7 @@ class Container(object): # {{{
|
||||
ans = 'application/xhtml+xml'
|
||||
return ans
|
||||
|
||||
def add_file(self, name, data, media_type=None):
|
||||
def add_file(self, name, data, media_type=None, spine_index=None):
|
||||
''' Add a file to this container. Entries for the file are
|
||||
automatically created in the OPF manifest and spine
|
||||
(if the file is a text document) '''
|
||||
@ -209,7 +209,7 @@ class Container(object): # {{{
|
||||
if mt in OEB_DOCS:
|
||||
spine = self.opf_xpath('//opf:spine')[0]
|
||||
si = manifest.makeelement(OPF('itemref'), idref=item_id)
|
||||
self.insert_into_xml(spine, si)
|
||||
self.insert_into_xml(spine, si, index=spine_index)
|
||||
|
||||
def rename(self, current_name, new_name):
|
||||
''' Renames a file from current_name to new_name. It automatically
|
||||
|
@ -14,10 +14,12 @@ from functools import partial
|
||||
from operator import itemgetter
|
||||
|
||||
from lxml import etree
|
||||
from lxml.builder import ElementMaker
|
||||
|
||||
from calibre import __version__
|
||||
from calibre.ebooks.oeb.base import XPath, uuid_id, xml2text, NCX, NCX_NS, XML, XHTML
|
||||
from calibre.ebooks.oeb.base import XPath, uuid_id, xml2text, NCX, NCX_NS, XML, XHTML, XHTML_NS, serialize
|
||||
from calibre.ebooks.oeb.polish.utils import guess_type
|
||||
from calibre.ebooks.oeb.polish.pretty import pretty_html_tree
|
||||
from calibre.utils.localization import get_lang, canonicalize_lang, lang_as_iso639_1
|
||||
|
||||
ns = etree.FunctionNamespace('calibre_xpath_extensions')
|
||||
@ -457,3 +459,71 @@ def remove_names_from_toc(container, names):
|
||||
commit_toc(container, toc)
|
||||
return True
|
||||
return False
|
||||
|
||||
def find_inline_toc(container):
|
||||
for name, linear in container.spine_names:
|
||||
if container.parsed(name).xpath('//*[local-name()="body" and @id="calibre_generated_inline_toc"]'):
|
||||
return name
|
||||
|
||||
def create_inline_toc(container, title=None):
|
||||
title = title or _('Table of Contents')
|
||||
toc = get_toc(container)
|
||||
if len(toc) == 0:
|
||||
return None
|
||||
toc_name = find_inline_toc(container)
|
||||
|
||||
def process_node(html_parent, toc, level=1, indent=' '):
|
||||
li = html_parent.makeelement(XHTML('li'))
|
||||
li.tail = '\n'+ (indent*level)
|
||||
html_parent.append(li)
|
||||
name, frag = toc.dest, toc.frag
|
||||
href = '#'
|
||||
if name:
|
||||
href = container.name_to_href(name, toc_name)
|
||||
if frag:
|
||||
href += '#' + frag
|
||||
a = li.makeelement(XHTML('a'), href=href)
|
||||
a.text = toc.title
|
||||
li.append(a)
|
||||
if len(toc) > 0:
|
||||
parent = li.makeelement(XHTML('ul'))
|
||||
li.append(parent)
|
||||
a.tail = '\n\n' + (indent*(level+2))
|
||||
parent.text = '\n'+(indent*(level+3))
|
||||
parent.tail = '\n\n' + (indent*(level+1))
|
||||
for child in toc:
|
||||
process_node(parent, child, level+3)
|
||||
parent[-1].tail = '\n' + (indent*(level+2))
|
||||
|
||||
E = ElementMaker(namespace=XHTML_NS, nsmap={None:XHTML_NS})
|
||||
html = E.html(
|
||||
E.head(
|
||||
E.title(title),
|
||||
E.style('''
|
||||
li { list-style-type: none; padding-left: 2em; margin-left: 0}
|
||||
a { text-decoration: none }
|
||||
a:hover { color: red }''', type='text/css'),
|
||||
),
|
||||
E.body(
|
||||
E.h2(title),
|
||||
E.ul(),
|
||||
id="calibre_generated_inline_toc",
|
||||
)
|
||||
)
|
||||
|
||||
name = toc_name
|
||||
for child in toc:
|
||||
process_node(html[1][1], child)
|
||||
pretty_html_tree(container, html)
|
||||
raw = serialize(html, 'text/html')
|
||||
if name is None:
|
||||
name, c = 'toc.xhtml', 0
|
||||
while container.has_name(name):
|
||||
c += 1
|
||||
name = 'toc%d.xhtml' % c
|
||||
container.add_file(name, raw, spine_index=0)
|
||||
else:
|
||||
with container.open(name, 'wb') as f:
|
||||
f.write(raw)
|
||||
return name
|
||||
|
||||
|
@ -23,7 +23,7 @@ from calibre.ebooks.oeb.polish.cover import mark_as_cover, mark_as_titlepage
|
||||
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
|
||||
from calibre.ebooks.oeb.polish.replace import rename_files, replace_file, get_recommended_folders, rationalize_folders
|
||||
from calibre.ebooks.oeb.polish.split import split, merge, AbortError, multisplit
|
||||
from calibre.ebooks.oeb.polish.toc import remove_names_from_toc, find_existing_toc
|
||||
from calibre.ebooks.oeb.polish.toc import remove_names_from_toc, find_existing_toc, create_inline_toc
|
||||
from calibre.ebooks.oeb.polish.utils import link_stylesheets, setup_cssutils_serialization as scs
|
||||
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
@ -365,6 +365,19 @@ class Boss(QObject):
|
||||
self.update_editors_from_container()
|
||||
self.gui.toc_view.update_if_visible()
|
||||
|
||||
def insert_inline_toc(self):
|
||||
self.commit_all_editors_to_container()
|
||||
self.add_savepoint(_('Before: Insert inline Table of Contents'))
|
||||
name = create_inline_toc(current_container())
|
||||
if name is None:
|
||||
self.rewind_savepoint()
|
||||
return error_dialog(self.gui, _('No Table of Contents'), _(
|
||||
'Cannot create an inline Table of Contents as this book has no existing'
|
||||
' Table of Contents. You must first create a Table of Contents using the'
|
||||
' Edit Table of Contents tool.'), show=True)
|
||||
self.apply_container_update_to_gui()
|
||||
self.edit_file(name, 'html')
|
||||
|
||||
def polish(self, action, name):
|
||||
with BusyCursor():
|
||||
self.add_savepoint(_('Before: %s') % name)
|
||||
|
@ -322,6 +322,8 @@ class Main(MainWindow):
|
||||
# Tool actions
|
||||
group = _('Tools')
|
||||
self.action_toc = reg('toc.png', _('&Edit Table of Contents'), self.boss.edit_toc, 'edit-toc', (), _('Edit Table of Contents'))
|
||||
self.action_inline_toc = reg('chapters.png', _('&Insert inline Table of Contents'),
|
||||
self.boss.insert_inline_toc, 'insert-inline-toc', (), _('Insert inline Table of Contents'))
|
||||
self.action_fix_html_current = reg('html-fix.png', _('&Fix HTML'), partial(self.boss.fix_html, True), 'fix-html-current', (),
|
||||
_('Fix HTML in the current file'))
|
||||
self.action_fix_html_all = reg('html-fix.png', _('&Fix HTML - all files'), partial(self.boss.fix_html, False), 'fix-html-all', (),
|
||||
@ -450,7 +452,9 @@ class Main(MainWindow):
|
||||
e.addAction(self.action_preferences)
|
||||
|
||||
e = b.addMenu(_('&Tools'))
|
||||
e.addAction(self.action_toc)
|
||||
tm = e.addMenu(_('Table of Contents'))
|
||||
tm.addAction(self.action_toc)
|
||||
tm.addAction(self.action_inline_toc)
|
||||
e.addAction(self.action_embed_fonts)
|
||||
e.addAction(self.action_subset_fonts)
|
||||
e.addAction(self.action_smarten_punctuation)
|
||||
|
Loading…
x
Reference in New Issue
Block a user