Implement upgrade for NCX

This commit is contained in:
Kovid Goyal 2018-04-15 10:26:33 +05:30
parent 6eb67b9b80
commit 0e9991c331
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 125 additions and 19 deletions

View File

@ -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')):
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)

View File

@ -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)