mirror of
https://github.com/gryf/ebook-converter.git
synced 2026-04-21 05:31:30 +02:00
Use the real constants module.
This is progressing refactor of the calibre code to make it more readable, and transform it to something more coherent. In this patch, there are changes regarding imports for some modules, instead of polluting namespace of each module with some other modules symbols, which often were imported from other modules. Yuck.
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
import re
|
||||
from collections import Counter, OrderedDict
|
||||
from functools import partial
|
||||
from operator import itemgetter
|
||||
import collections
|
||||
import functools
|
||||
import operator
|
||||
import pkg_resources
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
from lxml import etree
|
||||
from lxml.builder import ElementMaker
|
||||
|
||||
from ebook_converter import __version__
|
||||
from ebook_converter.ebooks.oeb.base import (
|
||||
XPath, uuid_id, xml2text, NCX, NCX_NS, XML, XHTML, XHTML_NS, serialize, EPUB_NS, XML_NS, OEB_DOCS)
|
||||
from ebook_converter import constants as const
|
||||
from ebook_converter.ebooks.oeb import base
|
||||
from ebook_converter.ebooks.oeb.polish.errors import MalformedMarkup
|
||||
from ebook_converter.ebooks.oeb.polish.utils import guess_type, extract
|
||||
from ebook_converter.ebooks.oeb.polish.opf import set_guide_item, get_book_language
|
||||
@@ -18,10 +18,6 @@ from ebook_converter.ebooks.oeb.polish.pretty import pretty_html_tree
|
||||
from ebook_converter.utils.localization import get_lang, canonicalize_lang, lang_as_iso639_1
|
||||
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
ns = etree.FunctionNamespace('calibre_xpath_extensions')
|
||||
ns.prefix = 'calibre'
|
||||
ns['lower-case'] = lambda c, x: x.lower() if hasattr(x, 'lower') else x
|
||||
@@ -81,7 +77,8 @@ class TOC(object):
|
||||
seen = set()
|
||||
remove = []
|
||||
for child in self:
|
||||
key = child.title if only_text else (child.title, child.dest, (child.frag or None))
|
||||
key = child.title if only_text else (child.title, child.dest,
|
||||
(child.frag or None))
|
||||
if key in seen:
|
||||
remove.append(child)
|
||||
else:
|
||||
@@ -104,7 +101,7 @@ class TOC(object):
|
||||
|
||||
def get_lines(self, lvl=0):
|
||||
frag = ('#'+self.frag) if self.frag else ''
|
||||
ans = [('\t'*lvl) + 'TOC: %s --> %s%s'%(self.title, self.dest, frag)]
|
||||
ans = [('\t'*lvl) + 'TOC: %s --> %s%s' % (self.title, self.dest, frag)]
|
||||
for child in self:
|
||||
ans.extend(child.get_lines(lvl+1))
|
||||
return ans
|
||||
@@ -113,10 +110,8 @@ class TOC(object):
|
||||
return '\n'.join(self.get_lines())
|
||||
|
||||
def to_dict(self, node_counter=None):
|
||||
ans = {
|
||||
'title':self.title, 'dest':self.dest, 'frag':self.frag,
|
||||
'children':[c.to_dict(node_counter) for c in self.children]
|
||||
}
|
||||
ans = {'title': self.title, 'dest': self.dest, 'frag': self.frag,
|
||||
'children': [c.to_dict(node_counter) for c in self.children]}
|
||||
if self.dest_exists is not None:
|
||||
ans['dest_exists'] = self.dest_exists
|
||||
if self.dest_error is not None:
|
||||
@@ -131,7 +126,7 @@ class TOC(object):
|
||||
|
||||
|
||||
def child_xpath(tag, name):
|
||||
return tag.xpath('./*[calibre:lower-case(local-name()) = "%s"]'%name)
|
||||
return tag.xpath('./*[calibre:lower-case(local-name()) = "%s"]' % name)
|
||||
|
||||
|
||||
def add_from_navpoint(container, navpoint, parent, ncx_name):
|
||||
@@ -142,7 +137,7 @@ def add_from_navpoint(container, navpoint, parent, ncx_name):
|
||||
text = ''
|
||||
for txt in child_xpath(nl, 'text'):
|
||||
text += etree.tostring(txt, method='text',
|
||||
encoding='unicode', with_tail=False)
|
||||
encoding='unicode', with_tail=False)
|
||||
content = child_xpath(navpoint, 'content')
|
||||
if content:
|
||||
content = content[0]
|
||||
@@ -154,7 +149,8 @@ def add_from_navpoint(container, navpoint, parent, ncx_name):
|
||||
|
||||
|
||||
def process_ncx_node(container, node, toc_parent, ncx_name):
|
||||
for navpoint in node.xpath('./*[calibre:lower-case(local-name()) = "navpoint"]'):
|
||||
for navpoint in node.xpath('./*[calibre:lower-case(local-name()) '
|
||||
'= "navpoint"]'):
|
||||
child = add_from_navpoint(container, navpoint, toc_parent, ncx_name)
|
||||
if child is not None:
|
||||
process_ncx_node(container, navpoint, child, ncx_name)
|
||||
@@ -171,29 +167,38 @@ def parse_ncx(container, ncx_name):
|
||||
if attr.endswith('lang'):
|
||||
toc_root.lang = str(val)
|
||||
break
|
||||
for uid in root.xpath('//*[calibre:lower-case(local-name()) = "meta" and @name="dtb:uid"]/@content'):
|
||||
for uid in root.xpath('//*[calibre:lower-case(local-name()) = "meta" and '
|
||||
'@name="dtb:uid"]/@content'):
|
||||
if uid:
|
||||
toc_root.uid = str(uid)
|
||||
break
|
||||
for pl in root.xpath('//*[calibre:lower-case(local-name()) = "pagelist"]'):
|
||||
for pt in pl.xpath('descendant::*[calibre:lower-case(local-name()) = "pagetarget"]'):
|
||||
for pt in pl.xpath('descendant::*[calibre:lower-case(local-name()) = '
|
||||
'"pagetarget"]'):
|
||||
pagenum = pt.get('value')
|
||||
if pagenum:
|
||||
href = pt.xpath('descendant::*[calibre:lower-case(local-name()) = "content"]/@src')
|
||||
href = pt.xpath('descendant::*[calibre:lower-case(local-name()'
|
||||
') = "content"]/@src')
|
||||
if href:
|
||||
dest = container.href_to_name(href[0], base=ncx_name)
|
||||
frag = urllib.parse.urlparse(href[0]).fragment or None
|
||||
toc_root.page_list.append({'dest': dest, 'pagenum': pagenum, 'frag': frag})
|
||||
toc_root.page_list.append({'dest': dest,
|
||||
'pagenum': pagenum,
|
||||
'frag': frag})
|
||||
return toc_root
|
||||
|
||||
|
||||
def add_from_li(container, li, parent, nav_name):
|
||||
dest = frag = text = None
|
||||
for x in li.iterchildren(XHTML('a'), XHTML('span')):
|
||||
text = etree.tostring(x, method='text', encoding='unicode', with_tail=False).strip() or ' '.join(x.xpath('descendant-or-self::*/@title')).strip()
|
||||
for x in li.iterchildren(base.tag('xhtml', 'a'),
|
||||
base.tag('xhtml', 'span')):
|
||||
text = (etree.tostring(x, method='text', encoding='unicode',
|
||||
with_tail=False).strip() or
|
||||
' '.join(x.xpath('descendant-or-self::*/@title')).strip())
|
||||
href = x.get('href')
|
||||
if href:
|
||||
dest = nav_name if href.startswith('#') else container.href_to_name(href, base=nav_name)
|
||||
dest = (nav_name if href.startswith('#') else
|
||||
container.href_to_name(href, base=nav_name))
|
||||
frag = urllib.parse.urlparse(href).fragment or None
|
||||
break
|
||||
return parent.add(text or None, dest or None, frag or None)
|
||||
@@ -207,9 +212,9 @@ def first_child(parent, tagname):
|
||||
|
||||
|
||||
def process_nav_node(container, node, toc_parent, nav_name):
|
||||
for li in node.iterchildren(XHTML('li')):
|
||||
for li in node.iterchildren(base.tag('xhtml', 'li')):
|
||||
child = add_from_li(container, li, toc_parent, nav_name)
|
||||
ol = first_child(li, XHTML('ol'))
|
||||
ol = first_child(li, base.tag('xhtml', 'ol'))
|
||||
if child is not None and ol is not None:
|
||||
process_nav_node(container, ol, child, nav_name)
|
||||
|
||||
@@ -218,14 +223,16 @@ def parse_nav(container, nav_name):
|
||||
root = container.parsed(nav_name)
|
||||
toc_root = TOC()
|
||||
toc_root.lang = toc_root.uid = None
|
||||
et = '{%s}type' % EPUB_NS
|
||||
for nav in root.iterdescendants(XHTML('nav')):
|
||||
if nav.get(et) == 'toc':
|
||||
ol = first_child(nav, XHTML('ol'))
|
||||
xhtml = functools.partial(base.tag, 'xhtml')
|
||||
for nav in root.iterdescendants(base.tag('xhtml', 'nav')):
|
||||
if nav.get(base.tag('epub', 'type')) == 'toc':
|
||||
ol = first_child(nav, base.tag('xhtml', 'ol'))
|
||||
if ol is not None:
|
||||
process_nav_node(container, ol, toc_root, nav_name)
|
||||
for h in nav.iterchildren(*map(XHTML, 'h1 h2 h3 h4 h5 h6'.split())):
|
||||
text = etree.tostring(h, method='text', encoding='unicode', with_tail=False) or h.get('title')
|
||||
for h in nav.iterchildren(*map(xhtml,
|
||||
'h1 h2 h3 h4 h5 h6'.split())):
|
||||
text = etree.tostring(h, method='text', encoding='unicode',
|
||||
with_tail=False) or h.get('title')
|
||||
if text:
|
||||
toc_root.toc_title = text
|
||||
break
|
||||
@@ -235,7 +242,7 @@ def parse_nav(container, nav_name):
|
||||
|
||||
def verify_toc_destinations(container, toc):
|
||||
anchor_map = {}
|
||||
anchor_xpath = XPath('//*/@id|//h:a/@name')
|
||||
anchor_xpath = base.XPath('//*/@id|//h:a/@name')
|
||||
for item in toc.iterdescendants():
|
||||
name = item.dest
|
||||
if not name:
|
||||
@@ -284,7 +291,8 @@ def get_x_toc(container, find_toc, parse_toc, verify_destinations=True):
|
||||
ans.lang = ans.uid = None
|
||||
return ans
|
||||
toc = find_toc(container)
|
||||
ans = empty_toc() if toc is None or not container.has_name(toc) else parse_toc(container, toc)
|
||||
ans = (empty_toc() if toc is None or not container.has_name(toc) else
|
||||
parse_toc(container, toc))
|
||||
ans.toc_file_name = toc if toc and container.has_name(toc) else None
|
||||
if verify_destinations:
|
||||
verify_toc_destinations(container, ans)
|
||||
@@ -294,11 +302,14 @@ def get_x_toc(container, find_toc, parse_toc, verify_destinations=True):
|
||||
def get_toc(container, verify_destinations=True):
|
||||
ver = container.opf_version_parsed
|
||||
if ver.major < 3:
|
||||
return get_x_toc(container, find_existing_ncx_toc, parse_ncx, verify_destinations=verify_destinations)
|
||||
return get_x_toc(container, find_existing_ncx_toc, parse_ncx,
|
||||
verify_destinations=verify_destinations)
|
||||
else:
|
||||
ans = get_x_toc(container, find_existing_nav_toc, parse_nav, verify_destinations=verify_destinations)
|
||||
ans = get_x_toc(container, find_existing_nav_toc, parse_nav,
|
||||
verify_destinations=verify_destinations)
|
||||
if len(ans) == 0:
|
||||
ans = get_x_toc(container, find_existing_ncx_toc, parse_ncx, verify_destinations=verify_destinations)
|
||||
ans = get_x_toc(container, find_existing_ncx_toc, parse_ncx,
|
||||
verify_destinations=verify_destinations)
|
||||
return ans
|
||||
|
||||
|
||||
@@ -308,25 +319,33 @@ def get_guide_landmarks(container):
|
||||
href, frag = href.partition('#')[::2]
|
||||
name = container.href_to_name(href, container.opf_name)
|
||||
if container.has_name(name):
|
||||
yield {'dest':name, 'frag':frag, 'title':title or '', 'type':rtype or ''}
|
||||
yield {'dest': name,
|
||||
'frag': frag,
|
||||
'title': title or '',
|
||||
'type': rtype or ''}
|
||||
|
||||
|
||||
def get_nav_landmarks(container):
|
||||
nav = find_existing_nav_toc(container)
|
||||
if nav and container.has_name(nav):
|
||||
root = container.parsed(nav)
|
||||
et = '{%s}type' % EPUB_NS
|
||||
for elem in root.iterdescendants(XHTML('nav')):
|
||||
et = base('epub', 'type')
|
||||
for elem in root.iterdescendants(base.tag('xhtml', 'nav')):
|
||||
if elem.get(et) == 'landmarks':
|
||||
for li in elem.iterdescendants(XHTML('li')):
|
||||
for a in li.iterdescendants(XHTML('a')):
|
||||
for li in elem.iterdescendants(base.tag('xhtml', 'li')):
|
||||
for a in li.iterdescendants(base.tag('xhtml', 'a')):
|
||||
href, rtype = a.get('href'), a.get(et)
|
||||
if href:
|
||||
title = etree.tostring(a, method='text', encoding='unicode', with_tail=False).strip()
|
||||
title = etree.tostring(a, method='text',
|
||||
encoding='unicode',
|
||||
with_tail=False).strip()
|
||||
href, frag = href.partition('#')[::2]
|
||||
name = container.href_to_name(href, nav)
|
||||
if container.has_name(name):
|
||||
yield {'dest':name, 'frag':frag, 'title':title or '', 'type':rtype or ''}
|
||||
yield {'dest': name,
|
||||
'frag': frag,
|
||||
'title': title or '',
|
||||
'type': rtype or ''}
|
||||
break
|
||||
|
||||
|
||||
@@ -344,7 +363,7 @@ def ensure_id(elem, all_ids):
|
||||
elem_id = elem.get('id')
|
||||
if elem_id:
|
||||
return False, elem_id
|
||||
if elem.tag == XHTML('a'):
|
||||
if elem.tag == base.tag('xhtml', 'a'):
|
||||
anchor = elem.get('name', None)
|
||||
if anchor:
|
||||
elem.set('id', anchor)
|
||||
@@ -361,7 +380,7 @@ def ensure_id(elem, all_ids):
|
||||
|
||||
|
||||
def elem_to_toc_text(elem):
|
||||
text = xml2text(elem).strip()
|
||||
text = base.xml2text(elem).strip()
|
||||
if not text:
|
||||
text = elem.get('title', '')
|
||||
if not text:
|
||||
@@ -375,7 +394,7 @@ def elem_to_toc_text(elem):
|
||||
|
||||
def item_at_top(elem):
|
||||
try:
|
||||
body = XPath('//h:body')(elem.getroottree().getroot())[0]
|
||||
body = base.XPath('//h:body')(elem.getroottree().getroot())[0]
|
||||
except (TypeError, IndexError, KeyError, AttributeError):
|
||||
return False
|
||||
tree = body.getroottree()
|
||||
@@ -387,7 +406,7 @@ def item_at_top(elem):
|
||||
try:
|
||||
if el.tag.endswith('}img') or (el.text and el.text.strip()):
|
||||
return False
|
||||
except:
|
||||
except Exception:
|
||||
return False
|
||||
if not path.startswith(epath):
|
||||
# Only check tail of non-parent elements
|
||||
@@ -404,24 +423,26 @@ def from_xpaths(container, xpaths):
|
||||
Table of Contents from the ``<h1>``, ``<h2>`` and ``<h3>`` tags.
|
||||
'''
|
||||
tocroot = TOC()
|
||||
xpaths = [XPath(xp) for xp in xpaths]
|
||||
xpaths = [base.XPath(xp) for xp in xpaths]
|
||||
|
||||
# Find those levels that have no elements in all spine items
|
||||
maps = OrderedDict()
|
||||
maps = collections.OrderedDict()
|
||||
empty_levels = {i+1 for i, xp in enumerate(xpaths)}
|
||||
for spinepath in container.spine_items:
|
||||
name = container.abspath_to_name(spinepath)
|
||||
root = container.parsed(name)
|
||||
level_item_map = maps[name] = {i+1:frozenset(xp(root)) for i, xp in enumerate(xpaths)}
|
||||
level_item_map = maps[name] = {i + 1: frozenset(xp(root))
|
||||
for i, xp in enumerate(xpaths)}
|
||||
for lvl, elems in level_item_map.items():
|
||||
if elems:
|
||||
empty_levels.discard(lvl)
|
||||
# Remove empty levels from all level_maps
|
||||
if empty_levels:
|
||||
for name, lmap in tuple(maps.items()):
|
||||
lmap = {lvl:items for lvl, items in lmap.items() if lvl not in empty_levels}
|
||||
lmap = sorted(lmap.items(), key=itemgetter(0))
|
||||
lmap = {i+1:items for i, (l, items) in enumerate(lmap)}
|
||||
lmap = {lvl: items for lvl, items in lmap.items()
|
||||
if lvl not in empty_levels}
|
||||
lmap = sorted(lmap.items(), key=operator.itemgetter(0))
|
||||
lmap = {i + 1: items for i, (l, items) in enumerate(lmap)}
|
||||
maps[name] = lmap
|
||||
|
||||
node_level_map = {tocroot: 0}
|
||||
@@ -434,13 +455,15 @@ def from_xpaths(container, xpaths):
|
||||
if child is None:
|
||||
return node
|
||||
lvl = node_level_map[child]
|
||||
return node if lvl > limit else child if lvl == limit else process_node(child)
|
||||
return (node if lvl > limit else
|
||||
child if lvl == limit else process_node(child))
|
||||
|
||||
return process_node(tocroot)
|
||||
|
||||
for name, level_item_map in maps.items():
|
||||
root = container.parsed(name)
|
||||
item_level_map = {e:i for i, elems in level_item_map.items() for e in elems}
|
||||
item_level_map = {e: i for i, elems in level_item_map.items()
|
||||
for e in elems}
|
||||
item_dirtied = False
|
||||
all_ids = set(root.xpath('//*/@id'))
|
||||
|
||||
@@ -470,7 +493,7 @@ def from_links(container):
|
||||
Generate a Table of Contents from links in the book.
|
||||
'''
|
||||
toc = TOC()
|
||||
link_path = XPath('//h:a[@href]')
|
||||
link_path = base.XPath('//h:a[@href]')
|
||||
seen_titles, seen_dests = set(), set()
|
||||
for name, is_linear in container.spine_names:
|
||||
root = container.parsed(name)
|
||||
@@ -506,7 +529,7 @@ def find_text(node):
|
||||
pat = re.compile(r'\s+')
|
||||
for child in node:
|
||||
if isinstance(child, etree._Element):
|
||||
text = xml2text(child).strip()
|
||||
text = base.xml2text(child).strip()
|
||||
text = pat.sub(' ', text)
|
||||
if len(text) < 1:
|
||||
continue
|
||||
@@ -526,7 +549,7 @@ def from_files(container):
|
||||
for i, spinepath in enumerate(container.spine_items):
|
||||
name = container.abspath_to_name(spinepath)
|
||||
root = container.parsed(name)
|
||||
body = XPath('//h:body')(root)
|
||||
body = base.XPath('//h:body')(root)
|
||||
if not body:
|
||||
continue
|
||||
text = find_text(body[0])
|
||||
@@ -576,42 +599,46 @@ def add_id(container, name, loc, totals=None):
|
||||
|
||||
def create_ncx(toc, to_href, btitle, lang, uid):
|
||||
lang = lang.replace('_', '-')
|
||||
ncx = etree.Element(NCX('ncx'),
|
||||
attrib={'version': '2005-1', XML('lang'): lang},
|
||||
nsmap={None: NCX_NS})
|
||||
head = etree.SubElement(ncx, NCX('head'))
|
||||
etree.SubElement(head, NCX('meta'),
|
||||
name='dtb:uid', content=str(uid))
|
||||
etree.SubElement(head, NCX('meta'),
|
||||
name='dtb:depth', content=str(toc.depth))
|
||||
ncx = etree.Element(base.tag('ncx', 'ncx'),
|
||||
attrib={'version': '2005-1',
|
||||
base.tag('xml', 'lang'): lang},
|
||||
nsmap={None: const.NCX_NS})
|
||||
head = etree.SubElement(ncx, base.tag('ncx', 'head'))
|
||||
etree.SubElement(head, base.tag('ncx', 'meta'),
|
||||
name='dtb:uid', content=str(uid))
|
||||
etree.SubElement(head, base.tag('ncx', 'meta'),
|
||||
name='dtb:depth', content=str(toc.depth))
|
||||
generator = ''.join(['calibre (', __version__, ')'])
|
||||
etree.SubElement(head, NCX('meta'),
|
||||
name='dtb:generator', content=generator)
|
||||
etree.SubElement(head, NCX('meta'), name='dtb:totalPageCount', content='0')
|
||||
etree.SubElement(head, NCX('meta'), name='dtb:maxPageNumber', content='0')
|
||||
title = etree.SubElement(ncx, NCX('docTitle'))
|
||||
text = etree.SubElement(title, NCX('text'))
|
||||
etree.SubElement(head, base.tag('ncx', 'meta'),
|
||||
name='dtb:generator', content=generator)
|
||||
etree.SubElement(head, base.tag('ncx', 'meta'), name='dtb:totalPageCount',
|
||||
content='0')
|
||||
etree.SubElement(head, base.tag('ncx', 'meta'), name='dtb:maxPageNumber',
|
||||
content='0')
|
||||
title = etree.SubElement(ncx, base.tag('ncx', 'docTitle'))
|
||||
text = etree.SubElement(title, base.tag('ncx', 'text'))
|
||||
text.text = btitle
|
||||
navmap = etree.SubElement(ncx, NCX('navMap'))
|
||||
navmap = etree.SubElement(ncx, base.tag('ncx', 'navMap'))
|
||||
spat = re.compile(r'\s+')
|
||||
|
||||
play_order = Counter()
|
||||
play_order = collections.Counter()
|
||||
|
||||
def process_node(xml_parent, toc_parent):
|
||||
for child in toc_parent:
|
||||
play_order['c'] += 1
|
||||
point = etree.SubElement(xml_parent, NCX('navPoint'), id='num_%d' % play_order['c'],
|
||||
playOrder=str(play_order['c']))
|
||||
label = etree.SubElement(point, NCX('navLabel'))
|
||||
point = etree.SubElement(xml_parent, base.tag('ncx', 'navPoint'),
|
||||
id='num_%d' % play_order['c'],
|
||||
playOrder=str(play_order['c']))
|
||||
label = etree.SubElement(point, base.tag('ncx', 'navLabel'))
|
||||
title = child.title
|
||||
if title:
|
||||
title = spat.sub(' ', title)
|
||||
etree.SubElement(label, NCX('text')).text = title
|
||||
etree.SubElement(label, base.tag('ncx', 'text')).text = title
|
||||
if child.dest:
|
||||
href = to_href(child.dest)
|
||||
if child.frag:
|
||||
href += '#'+child.frag
|
||||
etree.SubElement(point, NCX('content'), src=href)
|
||||
etree.SubElement(point, base.tag('ncx', 'content'), src=href)
|
||||
process_node(point, child)
|
||||
|
||||
process_node(navmap, toc)
|
||||
@@ -622,41 +649,43 @@ def commit_ncx_toc(container, toc, lang=None, uid=None):
|
||||
tocname = find_existing_ncx_toc(container)
|
||||
if tocname is None:
|
||||
item = container.generate_item('toc.ncx', id_prefix='toc')
|
||||
tocname = container.href_to_name(item.get('href'), base=container.opf_name)
|
||||
tocname = container.href_to_name(item.get('href'),
|
||||
base=container.opf_name)
|
||||
ncx_id = item.get('id')
|
||||
[s.set('toc', ncx_id) for s in container.opf_xpath('//opf:spine')]
|
||||
if not lang:
|
||||
lang = get_lang()
|
||||
for l in container.opf_xpath('//dc:language'):
|
||||
l = canonicalize_lang(xml2text(l).strip())
|
||||
if l:
|
||||
lang = l
|
||||
lang = lang_as_iso639_1(l) or l
|
||||
for _l in container.opf_xpath('//dc:language'):
|
||||
_l = canonicalize_lang(base.xml2text(_l).strip())
|
||||
if _l:
|
||||
lang = _l
|
||||
lang = lang_as_iso639_1(_l) or _l
|
||||
break
|
||||
lang = lang_as_iso639_1(lang) or lang
|
||||
if not uid:
|
||||
uid = uuid_id()
|
||||
uid = base.uuid_id()
|
||||
eid = container.opf.get('unique-identifier', None)
|
||||
if eid:
|
||||
m = container.opf_xpath('//*[@id="%s"]'%eid)
|
||||
m = container.opf_xpath('//*[@id="%s"]' % eid)
|
||||
if m:
|
||||
uid = xml2text(m[0])
|
||||
uid = base.xml2text(m[0])
|
||||
|
||||
title = 'Table of Contents'
|
||||
m = container.opf_xpath('//dc:title')
|
||||
if m:
|
||||
x = xml2text(m[0]).strip()
|
||||
x = base.xml2text(m[0]).strip()
|
||||
title = x or title
|
||||
|
||||
to_href = partial(container.name_to_href, base=tocname)
|
||||
to_href = functools.partial(container.name_to_href, base=tocname)
|
||||
root = create_ncx(toc, to_href, title, lang, uid)
|
||||
container.replace(tocname, root)
|
||||
container.pretty_print.add(tocname)
|
||||
|
||||
|
||||
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) == ntype]
|
||||
et = base('epub', 'type')
|
||||
navs = [n for n in root.iterdescendants(base.tag('xhtml', 'nav'))
|
||||
if n.get(et) == ntype]
|
||||
for x in navs[1:]:
|
||||
extract(x)
|
||||
if navs:
|
||||
@@ -667,13 +696,14 @@ def ensure_single_nav_of_type(root, ntype='toc'):
|
||||
nav.attrib.update(attrib)
|
||||
nav.tail = tail
|
||||
else:
|
||||
nav = root.makeelement(XHTML('nav'))
|
||||
first_child(root, XHTML('body')).append(nav)
|
||||
nav.set('{%s}type' % EPUB_NS, ntype)
|
||||
nav = root.makeelement(base.tag('xhtml', 'nav'))
|
||||
first_child(root, base.tag('xhtml', 'body')).append(nav)
|
||||
nav.set(et, ntype)
|
||||
return nav
|
||||
|
||||
|
||||
def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None):
|
||||
def commit_nav_toc(container, toc, lang=None, landmarks=None,
|
||||
previous_nav=None):
|
||||
from ebook_converter.ebooks.oeb.polish.pretty import pretty_xml_tree
|
||||
tocname = find_existing_nav_toc(container)
|
||||
if previous_nav is not None:
|
||||
@@ -684,7 +714,8 @@ def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None)
|
||||
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)
|
||||
tocname = container.href_to_name(item.get('href'),
|
||||
base=container.opf_name)
|
||||
if previous_nav is not None:
|
||||
root = previous_nav[1]
|
||||
else:
|
||||
@@ -698,24 +729,25 @@ def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None)
|
||||
if lang:
|
||||
lang = lang_as_iso639_1(lang) or lang
|
||||
root.set('lang', lang)
|
||||
root.set('{%s}lang' % XML_NS, lang)
|
||||
root.set(base.tag('xml', 'lang'), lang)
|
||||
nav = ensure_single_nav_of_type(root, 'toc')
|
||||
if toc.toc_title:
|
||||
nav.append(nav.makeelement(XHTML('h1')))
|
||||
nav.append(nav.makeelement(base.tag('xhtml', 'h1')))
|
||||
nav[-1].text = toc.toc_title
|
||||
|
||||
rnode = nav.makeelement(XHTML('ol'))
|
||||
rnode = nav.makeelement(base.tag('xhtml', 'ol'))
|
||||
nav.append(rnode)
|
||||
to_href = partial(container.name_to_href, base=tocname)
|
||||
to_href = functools.partial(container.name_to_href, base=tocname)
|
||||
spat = re.compile(r'\s+')
|
||||
|
||||
def process_node(xml_parent, toc_parent):
|
||||
for child in toc_parent:
|
||||
li = xml_parent.makeelement(XHTML('li'))
|
||||
li = xml_parent.makeelement(base.tag('xhtml', 'li'))
|
||||
xml_parent.append(li)
|
||||
title = child.title or ''
|
||||
title = spat.sub(' ', title).strip()
|
||||
a = li.makeelement(XHTML('a' if child.dest else 'span'))
|
||||
a = li.makeelement(base.tag('xhtml', 'a'
|
||||
if child.dest else 'span'))
|
||||
a.text = title
|
||||
li.append(a)
|
||||
if child.dest:
|
||||
@@ -724,14 +756,14 @@ def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None)
|
||||
href += '#'+child.frag
|
||||
a.set('href', href)
|
||||
if len(child):
|
||||
ol = li.makeelement(XHTML('ol'))
|
||||
ol = li.makeelement(base.tag('xhtml', 'ol'))
|
||||
li.append(ol)
|
||||
process_node(ol, child)
|
||||
process_node(rnode, toc)
|
||||
pretty_xml_tree(nav)
|
||||
|
||||
def collapse_li(parent):
|
||||
for li in parent.iterdescendants(XHTML('li')):
|
||||
for li in parent.iterdescendants(base.tag('xhtml', 'li')):
|
||||
if len(li) == 1:
|
||||
li.text = None
|
||||
li[0].tail = None
|
||||
@@ -739,9 +771,9 @@ def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None)
|
||||
nav.tail = '\n'
|
||||
|
||||
def create_li(ol, entry):
|
||||
li = ol.makeelement(XHTML('li'))
|
||||
li = ol.makeelement(base.tag('xhtml', 'li'))
|
||||
ol.append(li)
|
||||
a = li.makeelement(XHTML('a'))
|
||||
a = li.makeelement(base.tag('xhtml', 'a'))
|
||||
li.append(a)
|
||||
href = container.name_to_href(entry['dest'], tocname)
|
||||
if entry['frag']:
|
||||
@@ -752,12 +784,13 @@ def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None)
|
||||
if landmarks is not None:
|
||||
nav = ensure_single_nav_of_type(root, 'landmarks')
|
||||
nav.set('hidden', '')
|
||||
ol = nav.makeelement(XHTML('ol'))
|
||||
ol = nav.makeelement(base.tag('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:
|
||||
if (entry['type'] and container.has_name(entry['dest']) and
|
||||
container.mime_map[entry['dest']] in base.OEB_DOCS):
|
||||
a = create_li(ol, entry)
|
||||
a.set('{%s}type' % EPUB_NS, entry['type'])
|
||||
a.set(base.tag('epub', 'type'), entry['type'])
|
||||
a.text = entry['title'] or None
|
||||
pretty_xml_tree(nav)
|
||||
collapse_li(nav)
|
||||
@@ -765,10 +798,11 @@ def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None)
|
||||
if toc.page_list:
|
||||
nav = ensure_single_nav_of_type(root, 'page-list')
|
||||
nav.set('hidden', '')
|
||||
ol = nav.makeelement(XHTML('ol'))
|
||||
ol = nav.makeelement(base.tag('xhtml', 'ol'))
|
||||
nav.append(ol)
|
||||
for entry in toc.page_list:
|
||||
if container.has_name(entry['dest']) and container.mime_map[entry['dest']] in OEB_DOCS:
|
||||
if (container.has_name(entry['dest']) and
|
||||
container.mime_map[entry['dest']] in base.OEB_DOCS):
|
||||
a = create_li(ol, entry)
|
||||
a.text = str(entry['pagenum'])
|
||||
pretty_xml_tree(nav)
|
||||
@@ -785,11 +819,12 @@ def commit_toc(container, toc, lang=None, uid=None):
|
||||
def remove_names_from_toc(container, names):
|
||||
changed = []
|
||||
names = frozenset(names)
|
||||
for find_toc, parse_toc, commit_toc in (
|
||||
(find_existing_ncx_toc, parse_ncx, commit_ncx_toc),
|
||||
(find_existing_nav_toc, parse_nav, commit_nav_toc),
|
||||
):
|
||||
toc = get_x_toc(container, find_toc, parse_toc, verify_destinations=False)
|
||||
for find_toc, parse_toc, commit_toc in ((find_existing_ncx_toc,
|
||||
parse_ncx, commit_ncx_toc),
|
||||
(find_existing_nav_toc,
|
||||
parse_nav, commit_nav_toc)):
|
||||
toc = get_x_toc(container, find_toc, parse_toc,
|
||||
verify_destinations=False)
|
||||
if len(toc) > 0:
|
||||
remove = []
|
||||
for node in toc.iterdescendants():
|
||||
@@ -805,15 +840,16 @@ def remove_names_from_toc(container, names):
|
||||
|
||||
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"]'):
|
||||
if container.parsed(name).xpath('//*[local-name()="body" and @id='
|
||||
'"calibre_generated_inline_toc"]'):
|
||||
return name
|
||||
|
||||
|
||||
def toc_to_html(toc, container, toc_name, title, lang=None):
|
||||
|
||||
def process_node(html_parent, toc, level=1, indent=' ', style_level=2):
|
||||
li = html_parent.makeelement(XHTML('li'))
|
||||
li.tail = '\n'+ (indent*level)
|
||||
li = html_parent.makeelement(base.tag('xhtml', 'li'))
|
||||
li.tail = '\n' + (indent * level)
|
||||
html_parent.append(li)
|
||||
name, frag = toc.dest, toc.frag
|
||||
href = '#'
|
||||
@@ -821,32 +857,29 @@ def toc_to_html(toc, container, toc_name, title, lang=None):
|
||||
href = container.name_to_href(name, toc_name)
|
||||
if frag:
|
||||
href += '#' + frag
|
||||
a = li.makeelement(XHTML('a'), href=href)
|
||||
a = li.makeelement(base.tag('xhtml', 'a'), href=href)
|
||||
a.text = toc.title
|
||||
li.append(a)
|
||||
if len(toc) > 0:
|
||||
parent = li.makeelement(XHTML('ul'))
|
||||
parent = li.makeelement(base.tag('xhtml', 'ul'))
|
||||
parent.set('class', 'level%d' % (style_level))
|
||||
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, style_level=style_level + 1)
|
||||
process_node(parent, child, level+3,
|
||||
style_level=style_level + 1)
|
||||
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(P('templates/inline_toc_styles.css', data=True), type='text/css'),
|
||||
),
|
||||
E.body(
|
||||
E.h2(title),
|
||||
E.ul(),
|
||||
id="calibre_generated_inline_toc",
|
||||
)
|
||||
)
|
||||
E = ElementMaker(namespace=const.XHTML_NS, nsmap={None: const.XHTML_NS})
|
||||
# TODO(gryf): revisit lack of css.
|
||||
css_f = pkg_resources.resource_filename('ebook_converter',
|
||||
'data/inline_toc_styles.css')
|
||||
html = E.html(E.head(E.title(title),
|
||||
E.style(css_f, type='text/css')),
|
||||
E.body(E.h2(title), E.ul(),
|
||||
id="calibre_generated_inline_toc"))
|
||||
|
||||
ul = html[1][1]
|
||||
ul.set('class', 'level1')
|
||||
@@ -859,11 +892,12 @@ def toc_to_html(toc, container, toc_name, title, lang=None):
|
||||
|
||||
|
||||
def create_inline_toc(container, title=None):
|
||||
'''
|
||||
Create an inline (HTML) Table of Contents from an existing NCX Table of Contents.
|
||||
"""
|
||||
Create an inline (HTML) Table of Contents from an existing NCX Table of
|
||||
Contents.
|
||||
|
||||
:param title: The title for this table of contents.
|
||||
'''
|
||||
"""
|
||||
lang = get_book_language(container)
|
||||
default_title = 'Table of Contents'
|
||||
title = title or default_title
|
||||
@@ -874,7 +908,7 @@ def create_inline_toc(container, title=None):
|
||||
|
||||
name = toc_name
|
||||
html = toc_to_html(toc, container, name, title, lang)
|
||||
raw = serialize(html, 'text/html')
|
||||
raw = base.serialize(html, 'text/html')
|
||||
if name is None:
|
||||
name, c = 'toc.xhtml', 0
|
||||
while container.has_name(name):
|
||||
@@ -884,5 +918,6 @@ def create_inline_toc(container, title=None):
|
||||
else:
|
||||
with container.open(name, 'wb') as f:
|
||||
f.write(raw)
|
||||
set_guide_item(container, 'toc', title, name, frag='calibre_generated_inline_toc')
|
||||
set_guide_item(container, 'toc', title, name,
|
||||
frag='calibre_generated_inline_toc')
|
||||
return name
|
||||
|
||||
Reference in New Issue
Block a user