diff --git a/src/calibre/ebooks/oeb/polish/tests/structure.py b/src/calibre/ebooks/oeb/polish/tests/structure.py index 703d937984..8d8666cfcf 100644 --- a/src/calibre/ebooks/oeb/polish/tests/structure.py +++ b/src/calibre/ebooks/oeb/polish/tests/structure.py @@ -15,7 +15,7 @@ from calibre.ebooks.oeb.polish.create import create_book from calibre.ebooks.oeb.polish.cover import ( find_cover_image, mark_as_cover, find_cover_page, mark_as_titlepage, clean_opf ) -from calibre.ebooks.oeb.polish.toc import get_toc, from_xpaths as toc_from_xpaths +from calibre.ebooks.oeb.polish.toc import get_toc, from_xpaths as toc_from_xpaths, get_landmarks from calibre.ebooks.oeb.polish.utils import guess_type from calibre.ebooks.oeb.base import OEB_DOCS from calibre.ebooks.metadata.book.base import Metadata @@ -35,6 +35,8 @@ OPF_TEMPLATE = ''' def create_manifest_item(name, data=b'', properties=None): return (name, data, properties) + + cmi = create_manifest_item @@ -50,7 +52,7 @@ def create_epub(manifest, spine=(), guide=(), meta_cover=None, ver=3): if not spine: spine = [x[0] for x in manifest if guess_type(x[0]) in OEB_DOCS] spine = ''.join('' % name for name in spine) - guide = ''.join('' % (name, typ) for name, typ in guide) + guide = ''.join('' % (name, typ, title) for name, typ, title in guide) opf = OPF_TEMPLATE.format(manifest=mo, ver='%d.0'%ver, metadata=metadata, spine=spine, guide=guide) buf = BytesIO() with ZipFile(buf, 'w', ZIP_STORED) as zf: @@ -68,6 +70,7 @@ def create_epub(manifest, spine=(), guide=(), meta_cover=None, ver=3): buf.seek(0) return buf + counter = count() @@ -120,6 +123,23 @@ class Structure(BaseTest): tfx('32123', '321[2[3]]') tfx('123123', '1[2[3]]1[2[3]]') + def test_landmarks_detection(self): + c = self.create_epub([cmi('xxx.html'), cmi('a.html')], guide=[('xxx.html#moo', 'x', 'XXX'), ('a.html', '', 'YYY')], ver=2) + self.assertEqual(2, c.opf_version_parsed.major) + self.assertEqual([ + {'dest':'xxx.html', 'frag':'moo', 'type':'x', 'title':'XXX'}, {'dest':'a.html', 'frag':'', 'type':'', 'title':'YYY'} + ], get_landmarks(c)) + c = self.create_epub([cmi('xxx.html'), cmi('a.html')], ver=3) + self.assertEqual(3, c.opf_version_parsed.major) + c.add_file('xxx/nav.html', b'' + '', + process_manifest_item=lambda item:item.set('properties', 'nav')) + self.assertEqual([ + {'dest':'xxx.html', 'frag':'moo', 'type':'x', 'title':'XXX'}, {'dest':'a.html', 'frag':'', 'type':'', 'title':'YYY'} + ], get_landmarks(c)) + def test_epub3_covers(self): # cover image ce = partial(self.create_epub, ver=3) @@ -145,7 +165,7 @@ class Structure(BaseTest): # clean opf of all cover information c = ce([cmi('c.jpg', b'z', 'cover-image'), cmi('c.html', b'', 'calibre:title-page'), cmi('d.html')], - meta_cover='c.jpg', guide=[('c.jpg', 'cover'), ('d.html', 'cover')]) + meta_cover='c.jpg', guide=[('c.jpg', 'cover', ''), ('d.html', 'cover', '')]) self.assertEqual(set(clean_opf(c)), {'c.jpg', 'c.html', 'd.html'}) self.assertFalse(c.opf_xpath('//*/@name')) self.assertFalse(c.opf_xpath('//*/@type')) @@ -159,7 +179,7 @@ class Structure(BaseTest): self.assertIsNone(find_cover_image(c)) c = ce([cmi('c.jpg')], meta_cover='c.jpg') self.assertEqual('c.jpg', find_cover_image(c)) - c = ce([cmi('c.jpg'), cmi('d.jpg')], guide=[('c.jpg', 'cover')]) + c = ce([cmi('c.jpg'), cmi('d.jpg')], guide=[('c.jpg', 'cover', '')]) self.assertEqual('c.jpg', find_cover_image(c)) mark_as_cover(c, 'd.jpg') self.assertEqual('d.jpg', find_cover_image(c)) diff --git a/src/calibre/ebooks/oeb/polish/toc.py b/src/calibre/ebooks/oeb/polish/toc.py index 6869541a39..fa659628ac 100644 --- a/src/calibre/ebooks/oeb/polish/toc.py +++ b/src/calibre/ebooks/oeb/polish/toc.py @@ -281,6 +281,43 @@ def get_toc(container, verify_destinations=True): return ans +def get_guide_landmarks(container): + for ref in container.opf_xpath('./opf:guide/opf:reference'): + href, title, rtype = ref.get('href'), ref.get('title'), ref.get('type') + href, frag = href.partition('#')[::2] + name = container.href_to_name(href, container.opf_name) + if container.has_name(name): + yield {'dest':name, 'frag':frag, 'title':title or '', 'type':rtype or ''} + + +def get_nav_landmarks(container): + nav = find_existing_nav_toc(container) + if nav and container.has_name(nav): + root = container.parsed(nav) + et = '{%s}type' % EPUB_NS + for elem in root.iterdescendants(XHTML('nav')): + if elem.get(et) == 'landmarks': + for li in elem.iterdescendants(XHTML('li')): + for a in li.iterdescendants(XHTML('a')): + href, rtype = a.get('href'), a.get(et) + title = etree.tostring(a, method='text', encoding=unicode, with_tail=False).strip() + href, frag = href.partition('#')[::2] + name = container.href_to_name(href, nav) + if container.has_name(name): + yield {'dest':name, 'frag':frag, 'title':title or '', 'type':rtype or ''} + break + + +def get_landmarks(container): + ver = container.opf_version_parsed + if ver.major < 3: + return list(get_guide_landmarks(container)) + ans = list(get_nav_landmarks(container)) + if len(ans) == 0: + ans = list(get_guide_landmarks(container)) + return ans + + def ensure_id(elem): if elem.tag == XHTML('a'): anchor = elem.get('name', None)