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)