11
11
from urlparse import urlparse
12
from collections import deque, Counter, OrderedDict
12
from collections import Counter, OrderedDict
13
13
from functools import partial
14
14
from operator import itemgetter
16
16
from lxml import etree
17
from lxml.builder import ElementMaker
18
19
from calibre import __version__
19
from calibre.ebooks.oeb.base import XPath, uuid_id, xml2text, NCX, NCX_NS, XML, XHTML
20
from calibre.ebooks.oeb.base import XPath, uuid_id, xml2text, NCX, NCX_NS, XML, XHTML, XHTML_NS, serialize
21
from calibre.ebooks.oeb.polish.errors import MalformedMarkup
20
22
from calibre.ebooks.oeb.polish.utils import guess_type
23
from calibre.ebooks.oeb.polish.pretty import pretty_html_tree
21
24
from calibre.utils.localization import get_lang, canonicalize_lang, lang_as_iso639_1
23
26
ns = etree.FunctionNamespace('calibre_xpath_extensions')
347
350
toc.add(text, name)
350
def node_from_loc(root, loc):
351
body = root.xpath('//*[local-name()="body"]')[0]
353
def node_from_loc(root, locs, totals=None):
354
node = root.xpath('//*[local-name()="body"]')[0]
355
for i, loc in enumerate(locs):
355
356
children = tuple(node.iterchildren(etree.Element))
356
node = children[locs[0]]
357
if totals is not None and totals[i] != len(children):
358
raise MalformedMarkup()
360
def add_id(container, name, loc):
362
def add_id(container, name, loc, totals=None):
361
363
root = container.parsed(name)
362
node = node_from_loc(root, loc)
365
node = node_from_loc(root, loc, totals=totals)
366
except MalformedMarkup:
367
# The webkit HTML parser and the container parser have yielded
368
# different node counts, this can happen if the file is valid XML
369
# but contains constructs like nested <p> tags. So force parse it
370
# with the HTML 5 parser and try again.
371
raw = container.raw_data(name)
372
root = container.parse_xhtml(raw, fname=name, force_html5_parse=True)
374
node = node_from_loc(root, loc, totals=totals)
375
except MalformedMarkup:
376
raise MalformedMarkup(_('The file %s has malformed markup. Try running the Fix HTML tool'
377
' before editing.') % name)
378
container.replace(name, root)
363
380
node.set('id', node.get('id', uuid_id()))
364
381
container.commit_item(name, keep_parsed=True)
365
382
return node.get('id')
457
474
commit_toc(container, toc)
478
def find_inline_toc(container):
479
for name, linear in container.spine_names:
480
if container.parsed(name).xpath('//*[local-name()="body" and @id="calibre_generated_inline_toc"]'):
483
def create_inline_toc(container, title=None):
484
title = title or _('Table of Contents')
485
toc = get_toc(container)
488
toc_name = find_inline_toc(container)
490
def process_node(html_parent, toc, level=1, indent=' '):
491
li = html_parent.makeelement(XHTML('li'))
492
li.tail = '\n'+ (indent*level)
493
html_parent.append(li)
494
name, frag = toc.dest, toc.frag
497
href = container.name_to_href(name, toc_name)
500
a = li.makeelement(XHTML('a'), href=href)
504
parent = li.makeelement(XHTML('ul'))
506
a.tail = '\n\n' + (indent*(level+2))
507
parent.text = '\n'+(indent*(level+3))
508
parent.tail = '\n\n' + (indent*(level+1))
510
process_node(parent, child, level+3)
511
parent[-1].tail = '\n' + (indent*(level+2))
513
E = ElementMaker(namespace=XHTML_NS, nsmap={None:XHTML_NS})
518
li { list-style-type: none; padding-left: 2em; margin-left: 0}
519
a { text-decoration: none }
520
a:hover { color: red }''', type='text/css'),
525
id="calibre_generated_inline_toc",
531
process_node(html[1][1], child)
532
pretty_html_tree(container, html)
533
raw = serialize(html, 'text/html')
535
name, c = 'toc.xhtml', 0
536
while container.has_name(name):
538
name = 'toc%d.xhtml' % c
539
container.add_file(name, raw, spine_index=0)
541
with container.open(name, 'wb') as f: