mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Preserve existing nav markup when converting from EPUB 3 to EPUB 3
This commit is contained in:
parent
b7d88ff218
commit
09ffa06cc4
@ -289,7 +289,7 @@ class EPUBInput(InputFormatPlugin):
|
|||||||
|
|
||||||
epub3_nav = opf.epub3_nav
|
epub3_nav = opf.epub3_nav
|
||||||
if epub3_nav is not None:
|
if epub3_nav is not None:
|
||||||
self.convert_epub3_nav(epub3_nav, opf, log)
|
self.convert_epub3_nav(epub3_nav, opf, log, options)
|
||||||
|
|
||||||
if len(parts) > 1 and parts[0]:
|
if len(parts) > 1 and parts[0]:
|
||||||
delta = '/'.join(parts[:-1])+'/'
|
delta = '/'.join(parts[:-1])+'/'
|
||||||
@ -346,11 +346,11 @@ class EPUBInput(InputFormatPlugin):
|
|||||||
|
|
||||||
return os.path.abspath(u'content.opf')
|
return os.path.abspath(u'content.opf')
|
||||||
|
|
||||||
def convert_epub3_nav(self, nav_path, opf, log):
|
def convert_epub3_nav(self, nav_path, opf, log, opts):
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from calibre.ebooks.chardet import xml_to_unicode
|
from calibre.ebooks.chardet import xml_to_unicode
|
||||||
from calibre.ebooks.oeb.polish.parsing import parse
|
from calibre.ebooks.oeb.polish.parsing import parse
|
||||||
from calibre.ebooks.oeb.base import EPUB_NS, XHTML, NCX_MIME, NCX
|
from calibre.ebooks.oeb.base import EPUB_NS, XHTML, NCX_MIME, NCX, urlnormalize
|
||||||
from calibre.ebooks.oeb.polish.toc import first_child
|
from calibre.ebooks.oeb.polish.toc import first_child
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
with lopen(nav_path, 'rb') as f:
|
with lopen(nav_path, 'rb') as f:
|
||||||
@ -401,6 +401,9 @@ class EPUBInput(InputFormatPlugin):
|
|||||||
ncx_id = opf.add_path_to_manifest(f.name, NCX_MIME)
|
ncx_id = opf.add_path_to_manifest(f.name, NCX_MIME)
|
||||||
for spine in opf.root.xpath('//*[local-name()="spine"]'):
|
for spine in opf.root.xpath('//*[local-name()="spine"]'):
|
||||||
spine.set('toc', ncx_id)
|
spine.set('toc', ncx_id)
|
||||||
|
href = os.path.relpath(nav_path).replace(os.sep, '/')
|
||||||
|
opts.epub3_nav_href = urlnormalize(href)
|
||||||
|
opts.epub3_nav_parsed = root
|
||||||
|
|
||||||
def postprocess_book(self, oeb, opts, log):
|
def postprocess_book(self, oeb, opts, log):
|
||||||
rc = getattr(self, 'removed_cover', None)
|
rc = getattr(self, 'removed_cover', None)
|
||||||
|
@ -295,7 +295,8 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
from calibre.ebooks.oeb.polish.container import EpubContainer
|
from calibre.ebooks.oeb.polish.container import EpubContainer
|
||||||
container = EpubContainer(tdir, self.log)
|
container = EpubContainer(tdir, self.log)
|
||||||
from calibre.ebooks.oeb.polish.upgrade import epub_2_to_3
|
from calibre.ebooks.oeb.polish.upgrade import epub_2_to_3
|
||||||
epub_2_to_3(container, self.log.info)
|
existing_nav = getattr(self.opts, 'epub3_nav_parsed', None)
|
||||||
|
epub_2_to_3(container, self.log.info, previous_nav=existing_nav)
|
||||||
container.commit()
|
container.commit()
|
||||||
os.remove(f.name)
|
os.remove(f.name)
|
||||||
try:
|
try:
|
||||||
|
@ -655,14 +655,17 @@ def ensure_single_nav_of_type(root, ntype='toc'):
|
|||||||
return nav
|
return nav
|
||||||
|
|
||||||
|
|
||||||
def commit_nav_toc(container, toc, lang=None, landmarks=None):
|
def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None):
|
||||||
from calibre.ebooks.oeb.polish.pretty import pretty_xml_tree
|
from calibre.ebooks.oeb.polish.pretty import pretty_xml_tree
|
||||||
tocname = find_existing_nav_toc(container)
|
tocname = find_existing_nav_toc(container)
|
||||||
if tocname is None:
|
if tocname is None:
|
||||||
item = container.generate_item('nav.xhtml', id_prefix='nav')
|
item = container.generate_item('nav.xhtml', id_prefix='nav')
|
||||||
item.set('properties', 'nav')
|
item.set('properties', 'nav')
|
||||||
tocname = container.href_to_name(item.get('href'), base=container.opf_name)
|
tocname = container.href_to_name(item.get('href'), base=container.opf_name)
|
||||||
root = container.parse_xhtml(P('templates/new_nav.html', data=True).decode('utf-8'))
|
if previous_nav is not None:
|
||||||
|
root = previous_nav
|
||||||
|
else:
|
||||||
|
root = container.parse_xhtml(P('templates/new_nav.html', data=True).decode('utf-8'))
|
||||||
container.replace(tocname, root)
|
container.replace(tocname, root)
|
||||||
else:
|
else:
|
||||||
root = container.parsed(tocname)
|
root = container.parsed(tocname)
|
||||||
|
@ -100,7 +100,7 @@ guide_epubtype_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def create_nav(container, toc, landmarks):
|
def create_nav(container, toc, landmarks, previous_nav=None):
|
||||||
lang = get_book_language(container)
|
lang = get_book_language(container)
|
||||||
if lang == 'und':
|
if lang == 'und':
|
||||||
lang = None
|
lang = None
|
||||||
@ -109,10 +109,10 @@ def create_nav(container, toc, landmarks):
|
|||||||
entry['type'] = guide_epubtype_map.get(entry['type'].lower())
|
entry['type'] = guide_epubtype_map.get(entry['type'].lower())
|
||||||
if entry['type'] == 'cover' and container.mime_map.get(entry['dest'], '').lower() in OEB_DOCS:
|
if entry['type'] == 'cover' and container.mime_map.get(entry['dest'], '').lower() in OEB_DOCS:
|
||||||
container.apply_unique_properties(entry['dest'], 'calibre:title-page')
|
container.apply_unique_properties(entry['dest'], 'calibre:title-page')
|
||||||
commit_nav_toc(container, toc, lang=lang, landmarks=landmarks)
|
commit_nav_toc(container, toc, lang=lang, landmarks=landmarks, previous_nav=previous_nav)
|
||||||
|
|
||||||
|
|
||||||
def epub_2_to_3(container, report):
|
def epub_2_to_3(container, report, previous_nav=None):
|
||||||
upgrade_metadata(container.opf)
|
upgrade_metadata(container.opf)
|
||||||
collect_properties(container)
|
collect_properties(container)
|
||||||
toc = get_toc(container)
|
toc = get_toc(container)
|
||||||
@ -123,7 +123,7 @@ def epub_2_to_3(container, report):
|
|||||||
landmarks = get_landmarks(container)
|
landmarks = get_landmarks(container)
|
||||||
for guide in container.opf_xpath('./opf:guide'):
|
for guide in container.opf_xpath('./opf:guide'):
|
||||||
guide.getparent().remove(guide)
|
guide.getparent().remove(guide)
|
||||||
create_nav(container, toc, landmarks)
|
create_nav(container, toc, landmarks, previous_nav)
|
||||||
container.opf.set('version', '3.0')
|
container.opf.set('version', '3.0')
|
||||||
fix_font_mime_types(container)
|
fix_font_mime_types(container)
|
||||||
container.dirty(container.opf_name)
|
container.dirty(container.opf_name)
|
||||||
|
@ -179,6 +179,11 @@ class CSSFlattener(object):
|
|||||||
titlepage = titlepage.item
|
titlepage = titlepage.item
|
||||||
if titlepage is not None and titlepage not in self.items:
|
if titlepage is not None and titlepage not in self.items:
|
||||||
self.items.append(titlepage)
|
self.items.append(titlepage)
|
||||||
|
epub3_nav = None
|
||||||
|
if getattr(self.opts, 'epub3_nav_href', None):
|
||||||
|
epub3_nav = self.oeb.manifest.hrefs.get(self.opts.epub3_nav_href)
|
||||||
|
if epub3_nav is not None and epub3_nav not in self.items:
|
||||||
|
self.items.append(epub3_nav)
|
||||||
|
|
||||||
self.filter_css = frozenset()
|
self.filter_css = frozenset()
|
||||||
if self.opts.filter_css:
|
if self.opts.filter_css:
|
||||||
@ -210,6 +215,8 @@ class CSSFlattener(object):
|
|||||||
self.sbase = self.baseline_spine() if self.fbase else None
|
self.sbase = self.baseline_spine() if self.fbase else None
|
||||||
self.fmap = FontMapper(self.sbase, self.fbase, self.fkey)
|
self.fmap = FontMapper(self.sbase, self.fbase, self.fkey)
|
||||||
self.flatten_spine()
|
self.flatten_spine()
|
||||||
|
if epub3_nav is not None:
|
||||||
|
self.opts.epub3_nav_parsed = epub3_nav.data
|
||||||
|
|
||||||
def get_embed_font_info(self, family, failure_critical=True):
|
def get_embed_font_info(self, family, failure_critical=True):
|
||||||
efi = []
|
efi = []
|
||||||
@ -436,12 +443,11 @@ class CSSFlattener(object):
|
|||||||
cssdict['font-weight'] = 'normal' # ADE chokes on font-weight medium
|
cssdict['font-weight'] = 'normal' # ADE chokes on font-weight medium
|
||||||
|
|
||||||
fsize = font_size
|
fsize = font_size
|
||||||
is_drop_cap = (cssdict.get('float', None) == 'left' and 'font-size' in
|
is_drop_cap = (cssdict.get('float', None) == 'left' and 'font-size' in cssdict and len(node) == 0 and node.text and (
|
||||||
cssdict and len(node) == 0 and node.text and
|
len(node.text) == 1 or (len(node.text) == 2 and 0x2000 <= ord(node.text[0]) <= 0x206f)))
|
||||||
(len(node.text) == 1 or (len(node.text) == 2 and 0x2000 <= ord(node.text[0]) <= 0x206f)))
|
|
||||||
# Detect drop caps generated by the docx input plugin
|
# Detect drop caps generated by the docx input plugin
|
||||||
if (node.tag and node.tag.endswith('}p') and len(node) == 0 and node.text and len(node.text.strip()) == 1 and
|
if node.tag and node.tag.endswith('}p') and len(node) == 0 and node.text and len(node.text.strip()) == 1 and \
|
||||||
not node.tail and 'line-height' in cssdict and 'font-size' in cssdict):
|
not node.tail and 'line-height' in cssdict and 'font-size' in cssdict:
|
||||||
dp = node.getparent()
|
dp = node.getparent()
|
||||||
if dp.tag and dp.tag.endswith('}div') and len(dp) == 1 and not dp.text:
|
if dp.tag and dp.tag.endswith('}div') and len(dp) == 1 and not dp.text:
|
||||||
if stylizer.style(dp).cssdict().get('float', None) == 'left':
|
if stylizer.style(dp).cssdict().get('float', None) == 'left':
|
||||||
@ -473,8 +479,8 @@ class CSSFlattener(object):
|
|||||||
if cssdict:
|
if cssdict:
|
||||||
for x in self.filter_css:
|
for x in self.filter_css:
|
||||||
popval = cssdict.pop(x, None)
|
popval = cssdict.pop(x, None)
|
||||||
if (self.body_font_family and popval and x == 'font-family' and
|
if self.body_font_family and popval and x == 'font-family' \
|
||||||
popval.partition(',')[0][1:-1] == self.body_font_family.partition(',')[0][1:-1]):
|
and popval.partition(',')[0][1:-1] == self.body_font_family.partition(',')[0][1:-1]:
|
||||||
cssdict[x] = popval
|
cssdict[x] = popval
|
||||||
|
|
||||||
if cssdict:
|
if cssdict:
|
||||||
@ -499,8 +505,7 @@ class CSSFlattener(object):
|
|||||||
lineh = self.lineh / psize
|
lineh = self.lineh / psize
|
||||||
cssdict['line-height'] = "%0.5fem" % lineh
|
cssdict['line-height'] = "%0.5fem" % lineh
|
||||||
|
|
||||||
if (self.context.remove_paragraph_spacing or
|
if (self.context.remove_paragraph_spacing or self.context.insert_blank_line) and tag in ('p', 'div'):
|
||||||
self.context.insert_blank_line) and tag in ('p', 'div'):
|
|
||||||
if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle':
|
if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle':
|
||||||
for prop in ('margin', 'padding', 'border'):
|
for prop in ('margin', 'padding', 'border'):
|
||||||
for edge in ('top', 'bottom'):
|
for edge in ('top', 'bottom'):
|
||||||
@ -510,8 +515,7 @@ class CSSFlattener(object):
|
|||||||
'%fem'%self.context.insert_blank_line_size
|
'%fem'%self.context.insert_blank_line_size
|
||||||
indent_size = self.context.remove_paragraph_spacing_indent_size
|
indent_size = self.context.remove_paragraph_spacing_indent_size
|
||||||
keep_indents = indent_size < 0.0
|
keep_indents = indent_size < 0.0
|
||||||
if (self.context.remove_paragraph_spacing and not keep_indents and
|
if (self.context.remove_paragraph_spacing and not keep_indents and cssdict.get('text-align', None) not in ('center', 'right')):
|
||||||
cssdict.get('text-align', None) not in ('center', 'right')):
|
|
||||||
cssdict['text-indent'] = "%1.1fem" % indent_size
|
cssdict['text-indent'] = "%1.1fem" % indent_size
|
||||||
|
|
||||||
pseudo_classes = style.pseudo_classes(self.filter_css)
|
pseudo_classes = style.pseudo_classes(self.filter_css)
|
||||||
@ -618,8 +622,7 @@ class CSSFlattener(object):
|
|||||||
items = sorted(stylizer.page_rule.items())
|
items = sorted(stylizer.page_rule.items())
|
||||||
css = ';\n'.join("%s: %s" % (key, val) for key, val in items)
|
css = ';\n'.join("%s: %s" % (key, val) for key, val in items)
|
||||||
css = ('@page {\n%s\n}\n'%css) if items else ''
|
css = ('@page {\n%s\n}\n'%css) if items else ''
|
||||||
rules = [r.cssText for r in stylizer.font_face_rules +
|
rules = [r.cssText for r in stylizer.font_face_rules + self.embed_font_rules]
|
||||||
self.embed_font_rules]
|
|
||||||
raw = '\n\n'.join(rules)
|
raw = '\n\n'.join(rules)
|
||||||
css += '\n\n' + raw
|
css += '\n\n' + raw
|
||||||
global_css[css].append(item)
|
global_css[css].append(item)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user