From 5f7e5bf155bd05a3acfb9efea5ac00edb31f060b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 28 May 2018 13:02:18 +0530 Subject: [PATCH] EPUB3 Input: Fix Table of Contents not being recognized for some EPUB 3 books that placed their nav document in-side a sub-folder. Fixes #1773627 [Table of Contents](https://bugs.launchpad.net/calibre/+bug/1773627) --- .../ebooks/conversion/plugins/epub_input.py | 3 ++- src/calibre/ebooks/metadata/opf2.py | 26 ++++++------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/calibre/ebooks/conversion/plugins/epub_input.py b/src/calibre/ebooks/conversion/plugins/epub_input.py index e13a7990a0..31e2237af9 100644 --- a/src/calibre/ebooks/conversion/plugins/epub_input.py +++ b/src/calibre/ebooks/conversion/plugins/epub_input.py @@ -399,7 +399,8 @@ class EPUBInput(InputFormatPlugin): with NamedTemporaryFile(suffix='.ncx', dir=os.path.dirname(nav_path), delete=False) as f: f.write(etree.tostring(ncx, encoding='utf-8')) - ncx_id = opf.add_path_to_manifest(f.name, NCX_MIME) + ncx_href = os.path.relpath(f.name, os.getcwdu()).replace(os.sep, '/') + ncx_id = opf.create_manifest_item(ncx_href, NCX_MIME, append=True).get('id') for spine in opf.root.xpath('//*[local-name()="spine"]'): spine.set('toc', ncx_id) opts.epub3_nav_href = urlnormalize(os.path.relpath(nav_path).replace(os.sep, '/')) diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index aa63047edc..c5957d1d23 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -533,16 +533,16 @@ class OPF(object): # {{{ authors_path = XPath('descendant::*[re:match(name(), "creator", "i") and (@role="aut" or @opf:role="aut" or (not(@role) and not(@opf:role)))]') bkp_path = XPath('descendant::*[re:match(name(), "contributor", "i") and (@role="bkp" or @opf:role="bkp")]') tags_path = XPath('descendant::*[re:match(name(), "subject", "i")]') - isbn_path = XPath('descendant::*[re:match(name(), "identifier", "i") and '+ + isbn_path = XPath('descendant::*[re:match(name(), "identifier", "i") and ' '(re:match(@scheme, "isbn", "i") or re:match(@opf:scheme, "isbn", "i"))]') pubdate_path = XPath('descendant::*[re:match(name(), "date", "i")]') - raster_cover_path = XPath('descendant::*[re:match(name(), "meta", "i") and ' + + raster_cover_path = XPath('descendant::*[re:match(name(), "meta", "i") and ' 're:match(@name, "cover", "i") and @content]') guide_cover_path = XPath('descendant::*[local-name()="guide"]/*[local-name()="reference" and re:match(@type, "cover", "i")]/@href') identifier_path = XPath('descendant::*[re:match(name(), "identifier", "i")]') - application_id_path = XPath('descendant::*[re:match(name(), "identifier", "i") and '+ + application_id_path = XPath('descendant::*[re:match(name(), "identifier", "i") and ' '(re:match(@opf:scheme, "calibre|libprs500", "i") or re:match(@scheme, "calibre|libprs500", "i"))]') - uuid_id_path = XPath('descendant::*[re:match(name(), "identifier", "i") and '+ + uuid_id_path = XPath('descendant::*[re:match(name(), "identifier", "i") and ' '(re:match(@opf:scheme, "uuid", "i") or re:match(@scheme, "uuid", "i"))]') languages_path = XPath('descendant::*[local-name()="language"]') @@ -705,7 +705,7 @@ class OPF(object): # {{{ def itermanifest(self): return self.manifest_path(self.root) - def create_manifest_item(self, href, media_type): + def create_manifest_item(self, href, media_type, append=False): ids = [i.get('id', None) for i in self.itermanifest()] id = None for c in xrange(1, sys.maxint): @@ -717,6 +717,9 @@ class OPF(object): # {{{ ans = etree.Element('{%s}item'%self.NAMESPACES['opf'], attrib={'id':id, 'href':href, 'media-type':media_type}) ans.tail = '\n\t\t' + if append: + manifest = self.manifest_ppath(self.root)[0] + manifest.append(ans) return ans def replace_manifest_item(self, item, items): @@ -728,19 +731,6 @@ class OPF(object): # {{{ manifest[index:index+1] = items return [i.get('id') for i in items] - def add_path_to_manifest(self, path, media_type): - path = os.path.abspath(path) - for i in self.itermanifest(): - xpath = os.path.join(self.base_dir, *(i.get('href', '').split('/'))) - if os.path.abspath(xpath) == path: - return i.get('id') - href = os.path.relpath(path, self.base_dir).replace(os.sep, '/') - item = self.create_manifest_item(href, media_type) - manifest = self.manifest_ppath(self.root)[0] - manifest.append(item) - self.manifest.append_from_opf_manifest_item(item, self.basedir) - return item.get('id') - def iterspine(self): return self.spine_path(self.root)