diff --git a/src/calibre/ebooks/oeb/polish/toc.py b/src/calibre/ebooks/oeb/polish/toc.py index 44c9c52db1..2420a3ed86 100644 --- a/src/calibre/ebooks/oeb/polish/toc.py +++ b/src/calibre/ebooks/oeb/polish/toc.py @@ -19,7 +19,7 @@ from lxml.builder import ElementMaker from calibre import __version__ from calibre.ebooks.oeb.base import ( - XPath, uuid_id, xml2text, NCX, NCX_NS, XML, XHTML, XHTML_NS, serialize, EPUB_NS) + XPath, uuid_id, xml2text, NCX, NCX_NS, XML, XHTML, XHTML_NS, serialize, EPUB_NS, XML_NS, OEB_DOCS) from calibre.ebooks.oeb.polish.errors import MalformedMarkup from calibre.ebooks.oeb.polish.utils import guess_type, extract from calibre.ebooks.oeb.polish.opf import set_guide_item, get_book_language @@ -625,19 +625,9 @@ def commit_ncx_toc(container, toc, lang=None, uid=None): container.pretty_print.add(tocname) -def commit_nav_toc(container, toc, lang=None): - from calibre.ebooks.oeb.polish.pretty import pretty_xml_tree - tocname = find_existing_nav_toc(container) - if tocname is None: - item = container.generate_item('nav.xhtml', id_prefix='nav') - item.set('properties', 'nav') - tocname = container.href_to_name(item.get('href'), base=container.opf_name) - try: - root = container.parsed(tocname) - except KeyError: - root = container.parse_xhtml(P('templates/new_nav.html', data=True).decode('utf-8')) +def ensure_single_nav_of_type(root, ntype='toc'): et = '{%s}type' % EPUB_NS - navs = [n for n in root.iterdescendants(XHTML('nav')) if n.get(et) == 'toc'] + navs = [n for n in root.iterdescendants(XHTML('nav')) if n.get(et) == ntype] for x in navs[1:]: extract(x) if navs: @@ -650,7 +640,26 @@ def commit_nav_toc(container, toc, lang=None): else: nav = root.makeelement(XHTML('nav')) first_child(root, XHTML('body')).append(nav) - nav.set('{%s}type' % EPUB_NS, 'toc') + nav.set('{%s}type' % EPUB_NS, ntype) + return nav + + +def commit_nav_toc(container, toc, lang=None, landmarks=None): + from calibre.ebooks.oeb.polish.pretty import pretty_xml_tree + tocname = find_existing_nav_toc(container) + if tocname is None: + item = container.generate_item('nav.xhtml', id_prefix='nav') + item.set('properties', 'nav') + tocname = container.href_to_name(item.get('href'), base=container.opf_name) + root = container.parse_xhtml(P('templates/new_nav.html', data=True).decode('utf-8')) + container.replace(tocname, root) + else: + root = container.parsed(tocname) + if lang: + lang = lang_as_iso639_1(lang) or lang + root.set('lang', lang) + root.set('{%s}lang' % XML_NS, lang) + nav = ensure_single_nav_of_type(root, 'toc') if toc.toc_title: nav.append(nav.makeelement(XHTML('h1'))) nav[-1].text = toc.toc_title @@ -679,11 +688,35 @@ def commit_nav_toc(container, toc, lang=None): li.append(ol) process_node(ol, child) process_node(rnode, toc) - pretty_xml_tree(rnode) - for li in rnode.iterdescendants(XHTML('li')): - if len(li) == 1: - li.text = None - li[0].tail = None + pretty_xml_tree(nav) + + def collapse_li(parent): + for li in parent.iterdescendants(XHTML('li')): + if len(li) == 1: + li.text = None + li[0].tail = None + collapse_li(nav) + nav.tail = '\n' + + if landmarks is not None: + nav = ensure_single_nav_of_type(root, 'landmarks') + nav.set('hidden', '') + ol = nav.makeelement(XHTML('ol')) + nav.append(ol) + for entry in landmarks: + if entry['type'] and container.has_name(entry['dest']) and container.mime_map[entry['dest']] in OEB_DOCS: + li = ol.makeelement(XHTML('li')) + ol.append(li) + a = li.makeelement(XHTML('a')) + li.append(a) + a.set('{%s}type' % EPUB_NS, entry['type']) + href = container.name_to_href(entry['dest'], tocname) + if entry['frag']: + href += '#' + entry['frag'] + a.set('href', href) + a.text = entry['title'] or None + pretty_xml_tree(nav) + collapse_li(nav) container.replace(tocname, root) diff --git a/src/calibre/ebooks/oeb/polish/upgrade.py b/src/calibre/ebooks/oeb/polish/upgrade.py index 590f7c79a6..a65e8b45c4 100644 --- a/src/calibre/ebooks/oeb/polish/upgrade.py +++ b/src/calibre/ebooks/oeb/polish/upgrade.py @@ -4,9 +4,15 @@ from __future__ import absolute_import, division, print_function, unicode_literals +import sys + from calibre.ebooks.metadata.opf_2_to_3 import upgrade_metadata from calibre.ebooks.oeb.base import OEB_DOCS, xpath from calibre.ebooks.oeb.polish.container import OEB_FONTS +from calibre.ebooks.oeb.polish.opf import get_book_language +from calibre.ebooks.oeb.polish.toc import ( + commit_nav_toc, find_existing_ncx_toc, get_landmarks, get_toc +) def add_properties(item, *props): @@ -44,9 +50,76 @@ def collect_properties(container): add_properties(item, *tuple(properties)) +guide_epubtype_map = { + 'acknowledgements' : 'acknowledgments', + 'other.afterword' : 'afterword', + 'other.appendix' : 'appendix', + 'other.backmatter' : 'backmatter', + 'bibliography' : 'bibliography', + 'text' : 'bodymatter', + 'other.chapter' : 'chapter', + 'colophon' : 'colophon', + 'other.conclusion' : 'conclusion', + 'other.contributors' : 'contributors', + 'copyright-page' : 'copyright-page', + 'cover' : 'cover', + 'dedication' : 'dedication', + 'other.division' : 'division', + 'epigraph' : 'epigraph', + 'other.epilogue' : 'epilogue', + 'other.errata' : 'errata', + 'other.footnotes' : 'footnotes', + 'foreword' : 'foreword', + 'other.frontmatter' : 'frontmatter', + 'glossary' : 'glossary', + 'other.halftitlepage': 'halftitlepage', + 'other.imprint' : 'imprint', + 'other.imprimatur' : 'imprimatur', + 'index' : 'index', + 'other.introduction' : 'introduction', + 'other.landmarks' : 'landmarks', + 'other.loa' : 'loa', + 'loi' : 'loi', + 'lot' : 'lot', + 'other.lov' : 'lov', + 'notes' : '', + 'other.notice' : 'notice', + 'other.other-credits': 'other-credits', + 'other.part' : 'part', + 'other.preamble' : 'preamble', + 'preface' : 'preface', + 'other.prologue' : 'prologue', + 'other.rearnotes' : 'rearnotes', + 'other.subchapter' : 'subchapter', + 'title-page' : 'titlepage', + 'toc' : 'toc', + 'other.volume' : 'volume', + 'other.warning' : 'warning' +} + + +def create_nav(container, toc, landmarks): + lang = get_book_language(container) + if lang == 'und': + lang = None + if landmarks: + for entry in landmarks: + entry['type'] = guide_epubtype_map.get(entry['type'].lower()) + commit_nav_toc(container, toc, lang=lang, landmarks=landmarks) + + def epub_2_to_3(container, report): upgrade_metadata(container.opf) collect_properties(container) + toc = get_toc(container) + toc_name = find_existing_ncx_toc(container) + if toc_name: + container.remove_item(toc_name) + container.opf_xpath('./opf:spine')[0].attrib.pop('toc', None) + landmarks = get_landmarks(container) + for guide in container.opf_xpath('./opf:guide'): + guide.getparent().remove(guide) + create_nav(container, toc, landmarks) container.opf.set('version', '3.0') fix_font_mime_types(container) container.dirty(container.opf_name)