~ubuntu-branches/debian/sid/calibre/sid

« back to all changes in this revision

Viewing changes to src/calibre/ebooks/oeb/polish/toc.py

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2014-02-27 07:48:06 UTC
  • mto: This revision was merged to the branch mainline in revision 74.
  • Revision ID: package-import@ubuntu.com-20140227074806-64wdebb3ptosxhhx
Tags: upstream-1.25.0+dfsg
ImportĀ upstreamĀ versionĀ 1.25.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 
10
10
import re
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
15
15
 
16
16
from lxml import etree
 
17
from lxml.builder import ElementMaker
17
18
 
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
22
25
 
23
26
ns = etree.FunctionNamespace('calibre_xpath_extensions')
347
350
        toc.add(text, name)
348
351
    return toc
349
352
 
350
 
def node_from_loc(root, loc):
351
 
    body = root.xpath('//*[local-name()="body"]')[0]
352
 
    locs = deque(loc)
353
 
    node = body
354
 
    while locs:
 
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
 
        locs.popleft()
 
357
        if totals is not None and totals[i] != len(children):
 
358
            raise MalformedMarkup()
 
359
        node = children[loc]
358
360
    return node
359
361
 
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)
 
364
    try:
 
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)
 
373
        try:
 
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)
 
379
 
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)
458
475
        return True
459
476
    return False
 
477
 
 
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"]'):
 
481
            return name
 
482
 
 
483
def create_inline_toc(container, title=None):
 
484
    title = title or _('Table of Contents')
 
485
    toc = get_toc(container)
 
486
    if len(toc) == 0:
 
487
        return None
 
488
    toc_name = find_inline_toc(container)
 
489
 
 
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
 
495
        href = '#'
 
496
        if name:
 
497
            href = container.name_to_href(name, toc_name)
 
498
            if frag:
 
499
                href += '#' + frag
 
500
        a = li.makeelement(XHTML('a'), href=href)
 
501
        a.text = toc.title
 
502
        li.append(a)
 
503
        if len(toc) > 0:
 
504
            parent = li.makeelement(XHTML('ul'))
 
505
            li.append(parent)
 
506
            a.tail = '\n\n' + (indent*(level+2))
 
507
            parent.text = '\n'+(indent*(level+3))
 
508
            parent.tail = '\n\n' + (indent*(level+1))
 
509
            for child in toc:
 
510
                process_node(parent, child, level+3)
 
511
            parent[-1].tail = '\n' + (indent*(level+2))
 
512
 
 
513
    E = ElementMaker(namespace=XHTML_NS, nsmap={None:XHTML_NS})
 
514
    html = E.html(
 
515
        E.head(
 
516
            E.title(title),
 
517
            E.style('''
 
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'),
 
521
        ),
 
522
        E.body(
 
523
            E.h2(title),
 
524
            E.ul(),
 
525
            id="calibre_generated_inline_toc",
 
526
        )
 
527
    )
 
528
 
 
529
    name = toc_name
 
530
    for child in toc:
 
531
        process_node(html[1][1], child)
 
532
    pretty_html_tree(container, html)
 
533
    raw = serialize(html, 'text/html')
 
534
    if name is None:
 
535
        name, c = 'toc.xhtml', 0
 
536
        while container.has_name(name):
 
537
            c += 1
 
538
            name = 'toc%d.xhtml' % c
 
539
        container.add_file(name, raw, spine_index=0)
 
540
    else:
 
541
        with container.open(name, 'wb') as f:
 
542
            f.write(raw)
 
543
    return name
 
544