DOCX Input: When the document has a Table of Contents created using the Word Table of Contents tool, keep a reference to it in the metadata. This is useful when converting to old style MOBI and you want to precisely control placement of the ToC. Fixes #1556983 [ENHANCEMENT: FRONT TOC FOR MOBI](https://bugs.launchpad.net/calibre/+bug/1556983)

This commit is contained in:
Kovid Goyal 2016-03-14 21:29:44 +05:30
parent 9dbe10d2a1
commit a93ca1eaa0
2 changed files with 21 additions and 4 deletions

View File

@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import sys, os, re, math, errno import sys, os, re, math, errno, uuid
from collections import OrderedDict, defaultdict from collections import OrderedDict, defaultdict
from lxml import html from lxml import html
@ -105,6 +105,7 @@ class Convert(object):
self.anchor_map = {} self.anchor_map = {}
self.link_map = defaultdict(list) self.link_map = defaultdict(list)
self.link_source_map = {} self.link_source_map = {}
self.toc_anchor = None
self.block_runs = [] self.block_runs = []
paras = [] paras = []
@ -364,9 +365,13 @@ class Convert(object):
opf.create_spine(['index.html']) opf.create_spine(['index.html'])
if self.cover_image is not None: if self.cover_image is not None:
opf.guide.set_cover(self.cover_image) 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') 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: 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: if os.path.getsize(toc_file) == 0:
os.remove(toc_file) os.remove(toc_file)
return os.path.join(self.dest_dir, 'metadata.opf') return os.path.join(self.dest_dir, 'metadata.opf')
@ -413,7 +418,7 @@ class Convert(object):
except AttributeError: except AttributeError:
break 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: if p_parent(x) is not p:
continue continue
if x.tag.endswith('}r'): if x.tag.endswith('}r'):
@ -445,6 +450,16 @@ class Convert(object):
self.anchor_map[a] = current_anchor self.anchor_map[a] = current_anchor
elif x.tag.endswith('}hyperlink'): elif x.tag.endswith('}hyperlink'):
current_hyperlink = x 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) m = re.match(r'heading\s+(\d+)$', style.style_name or '', re.IGNORECASE)
if m is not None: if m is not None:

View File

@ -1396,7 +1396,7 @@ class OPFCreator(Metadata):
self.guide.set_basedir(self.base_path) self.guide.set_basedir(self.base_path)
def render(self, opf_stream=sys.stdout, ncx_stream=None, 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: if encoding is None:
encoding = 'utf-8' encoding = 'utf-8'
toc = getattr(self, 'toc', None) toc = getattr(self, 'toc', None)
@ -1519,6 +1519,8 @@ class OPFCreator(Metadata):
if ref.title: if ref.title:
item.set('title', ref.title) item.set('title', ref.title)
guide.append(item) guide.append(item)
if process_guide is not None:
process_guide(E, guide)
serialize_user_metadata(metadata, self.get_all_user_metadata(False)) serialize_user_metadata(metadata, self.get_all_user_metadata(False))