diff --git a/src/calibre/ebooks/conversion/plugins/epub_input.py b/src/calibre/ebooks/conversion/plugins/epub_input.py index 553b04ffa6..f021b16124 100644 --- a/src/calibre/ebooks/conversion/plugins/epub_input.py +++ b/src/calibre/ebooks/conversion/plugins/epub_input.py @@ -289,7 +289,7 @@ class EPUBInput(InputFormatPlugin): epub3_nav = opf.epub3_nav 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]: delta = '/'.join(parts[:-1])+'/' @@ -346,11 +346,11 @@ class EPUBInput(InputFormatPlugin): 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 calibre.ebooks.chardet import xml_to_unicode 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 tempfile import NamedTemporaryFile 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) for spine in opf.root.xpath('//*[local-name()="spine"]'): 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): rc = getattr(self, 'removed_cover', None) diff --git a/src/calibre/ebooks/conversion/plugins/epub_output.py b/src/calibre/ebooks/conversion/plugins/epub_output.py index 72ceac308e..e1523f452d 100644 --- a/src/calibre/ebooks/conversion/plugins/epub_output.py +++ b/src/calibre/ebooks/conversion/plugins/epub_output.py @@ -295,7 +295,8 @@ class EPUBOutput(OutputFormatPlugin): from calibre.ebooks.oeb.polish.container import EpubContainer container = EpubContainer(tdir, self.log) 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() os.remove(f.name) try: diff --git a/src/calibre/ebooks/oeb/polish/toc.py b/src/calibre/ebooks/oeb/polish/toc.py index 242b7d307d..68da36ebbe 100644 --- a/src/calibre/ebooks/oeb/polish/toc.py +++ b/src/calibre/ebooks/oeb/polish/toc.py @@ -655,14 +655,17 @@ def ensure_single_nav_of_type(root, ntype='toc'): 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 tocname = find_existing_nav_toc(container) if tocname is None: item = container.generate_item('nav.xhtml', id_prefix='nav') item.set('properties', 'nav') 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) else: root = container.parsed(tocname) diff --git a/src/calibre/ebooks/oeb/polish/upgrade.py b/src/calibre/ebooks/oeb/polish/upgrade.py index 200634ca33..5dfe37a7ac 100644 --- a/src/calibre/ebooks/oeb/polish/upgrade.py +++ b/src/calibre/ebooks/oeb/polish/upgrade.py @@ -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) if lang == 'und': lang = None @@ -109,10 +109,10 @@ def create_nav(container, toc, landmarks): entry['type'] = guide_epubtype_map.get(entry['type'].lower()) if entry['type'] == 'cover' and container.mime_map.get(entry['dest'], '').lower() in OEB_DOCS: 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) collect_properties(container) toc = get_toc(container) @@ -123,7 +123,7 @@ def epub_2_to_3(container, report): landmarks = get_landmarks(container) for guide in container.opf_xpath('./opf:guide'): guide.getparent().remove(guide) - create_nav(container, toc, landmarks) + create_nav(container, toc, landmarks, previous_nav) container.opf.set('version', '3.0') fix_font_mime_types(container) container.dirty(container.opf_name) diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py index 5f8443aa5e..7f43fc2447 100644 --- a/src/calibre/ebooks/oeb/transforms/flatcss.py +++ b/src/calibre/ebooks/oeb/transforms/flatcss.py @@ -179,6 +179,11 @@ class CSSFlattener(object): titlepage = titlepage.item if titlepage is not None and titlepage not in self.items: 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() if self.opts.filter_css: @@ -210,6 +215,8 @@ class CSSFlattener(object): self.sbase = self.baseline_spine() if self.fbase else None self.fmap = FontMapper(self.sbase, self.fbase, self.fkey) 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): efi = [] @@ -436,12 +443,11 @@ class CSSFlattener(object): cssdict['font-weight'] = 'normal' # ADE chokes on font-weight medium fsize = font_size - is_drop_cap = (cssdict.get('float', None) == 'left' and 'font-size' in - 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))) + is_drop_cap = (cssdict.get('float', None) == 'left' and 'font-size' in 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))) # 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 - not node.tail and 'line-height' in cssdict and 'font-size' in cssdict): + 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: dp = node.getparent() 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': @@ -473,8 +479,8 @@ class CSSFlattener(object): if cssdict: for x in self.filter_css: popval = cssdict.pop(x, None) - if (self.body_font_family and popval and x == 'font-family' and - popval.partition(',')[0][1:-1] == self.body_font_family.partition(',')[0][1:-1]): + if self.body_font_family and popval and x == 'font-family' \ + and popval.partition(',')[0][1:-1] == self.body_font_family.partition(',')[0][1:-1]: cssdict[x] = popval if cssdict: @@ -499,8 +505,7 @@ class CSSFlattener(object): lineh = self.lineh / psize cssdict['line-height'] = "%0.5fem" % lineh - if (self.context.remove_paragraph_spacing or - self.context.insert_blank_line) and tag in ('p', 'div'): + if (self.context.remove_paragraph_spacing or self.context.insert_blank_line) and tag in ('p', 'div'): if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle': for prop in ('margin', 'padding', 'border'): for edge in ('top', 'bottom'): @@ -510,8 +515,7 @@ class CSSFlattener(object): '%fem'%self.context.insert_blank_line_size indent_size = self.context.remove_paragraph_spacing_indent_size keep_indents = indent_size < 0.0 - if (self.context.remove_paragraph_spacing and not keep_indents and - cssdict.get('text-align', None) not in ('center', 'right')): + if (self.context.remove_paragraph_spacing and not keep_indents and cssdict.get('text-align', None) not in ('center', 'right')): cssdict['text-indent'] = "%1.1fem" % indent_size pseudo_classes = style.pseudo_classes(self.filter_css) @@ -618,8 +622,7 @@ class CSSFlattener(object): items = sorted(stylizer.page_rule.items()) css = ';\n'.join("%s: %s" % (key, val) for key, val in items) css = ('@page {\n%s\n}\n'%css) if items else '' - rules = [r.cssText for r in stylizer.font_face_rules + - self.embed_font_rules] + rules = [r.cssText for r in stylizer.font_face_rules + self.embed_font_rules] raw = '\n\n'.join(rules) css += '\n\n' + raw global_css[css].append(item)