diff --git a/src/calibre/ebooks/docx/to_html.py b/src/calibre/ebooks/docx/to_html.py index 22186bdbb8..bb021c1376 100644 --- a/src/calibre/ebooks/docx/to_html.py +++ b/src/calibre/ebooks/docx/to_html.py @@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' -import sys, os, re, math, errno +import sys, os, re, math, errno, uuid from collections import OrderedDict, defaultdict from lxml import html @@ -105,6 +105,7 @@ class Convert(object): self.anchor_map = {} self.link_map = defaultdict(list) self.link_source_map = {} + self.toc_anchor = None self.block_runs = [] paras = [] @@ -364,9 +365,13 @@ class Convert(object): opf.create_spine(['index.html']) if self.cover_image is not None: opf.guide.set_cover(self.cover_image) + def process_guide(E, guide): + if self.toc_anchor is not None: + guide.append(E.reference( + href='index.html#' + self.toc_anchor, title=_('Table of Contents'), type='toc')) toc_file = os.path.join(self.dest_dir, 'toc.ncx') with open(os.path.join(self.dest_dir, 'metadata.opf'), 'wb') as of, open(toc_file, 'wb') as ncx: - opf.render(of, ncx, 'toc.ncx') + opf.render(of, ncx, 'toc.ncx', process_guide=process_guide) if os.path.getsize(toc_file) == 0: os.remove(toc_file) return os.path.join(self.dest_dir, 'metadata.opf') @@ -413,7 +418,7 @@ class Convert(object): except AttributeError: break - for x in self.namespace.descendants(p, 'w:r', 'w:bookmarkStart', 'w:hyperlink'): + for x in self.namespace.descendants(p, 'w:r', 'w:bookmarkStart', 'w:hyperlink', 'w:instrText'): if p_parent(x) is not p: continue if x.tag.endswith('}r'): @@ -445,6 +450,16 @@ class Convert(object): self.anchor_map[a] = current_anchor elif x.tag.endswith('}hyperlink'): current_hyperlink = x + elif x.tag.endswith('}instrText') and x.text and x.text.strip().startswith('TOC '): + old_anchor = current_anchor + anchor = str(uuid.uuid4()) + self.anchor_map[anchor] = current_anchor = generate_anchor('toc', frozenset(self.anchor_map.itervalues())) + self.toc_anchor = current_anchor + if old_anchor is not None: + # The previous anchor was not applied to any element + for a, t in tuple(self.anchor_map.iteritems()): + if t == old_anchor: + self.anchor_map[a] = current_anchor m = re.match(r'heading\s+(\d+)$', style.style_name or '', re.IGNORECASE) if m is not None: diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 89d40b549a..262d59aa82 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -1396,7 +1396,7 @@ class OPFCreator(Metadata): self.guide.set_basedir(self.base_path) def render(self, opf_stream=sys.stdout, ncx_stream=None, - ncx_manifest_entry=None, encoding=None): + ncx_manifest_entry=None, encoding=None, process_guide=None): if encoding is None: encoding = 'utf-8' toc = getattr(self, 'toc', None) @@ -1519,6 +1519,8 @@ class OPFCreator(Metadata): if ref.title: item.set('title', ref.title) guide.append(item) + if process_guide is not None: + process_guide(E, guide) serialize_user_metadata(metadata, self.get_all_user_metadata(False))