mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
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:
parent
9dbe10d2a1
commit
a93ca1eaa0
@ -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:
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user