Code to read landmarks from book

This commit is contained in:
Kovid Goyal 2017-01-02 11:55:54 +05:30
parent cb8c641e4f
commit 1600c3a347
2 changed files with 61 additions and 4 deletions

View File

@ -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('<itemref idref="%s"/>' % name for name in spine)
guide = ''.join('<reference href="%s" type="%s"/>' % (name, typ) for name, typ in guide)
guide = ''.join('<reference href="%s" type="%s" title="%s"/>' % (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'<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">'
'<body><nav epub:type="landmarks"><ol><li><a epub:type="x" href="../xxx.html#moo">XXX </a></li>'
'<li><a href="../a.html"> YYY </a></li>'
'</ol></nav></body></html>',
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))

View File

@ -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)