render missing tags for epub
[librarian.git] / librarian / formats / epub / __init__.py
index b9d1c7a..80f9d5c 100644 (file)
@@ -4,6 +4,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 import os
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 import os
+import re
 import urllib
 from copy import deepcopy
 from mimetypes import guess_type
 import urllib
 from copy import deepcopy
 from mimetypes import guess_type
@@ -15,7 +16,7 @@ from lxml import etree
 from librarian import OPFNS, NCXNS, XHTMLNS, DCNS
 from librarian import core
 from librarian.formats import Format
 from librarian import OPFNS, NCXNS, XHTMLNS, DCNS
 from librarian import core
 from librarian.formats import Format
-from librarian.formats.cover.wolnelektury import WLCover
+from librarian.formats.cover.evens import EvensCover
 from librarian.output import OutputFile
 from librarian.renderers import Register, TreeRenderer, UnknownElement
 from librarian.utils import Context, get_resource, extend_element
 from librarian.output import OutputFile
 from librarian.renderers import Register, TreeRenderer, UnknownElement
 from librarian.utils import Context, get_resource, extend_element
@@ -25,7 +26,7 @@ class EpubFormat(Format):
     format_name = 'EPUB'
     format_ext = 'epub'
 
     format_name = 'EPUB'
     format_ext = 'epub'
 
-    cover = WLCover
+    cover = EvensCover
     renderers = Register()
 
     def __init__(self, doc, cover=None, with_fonts=True):
     renderers = Register()
 
     def __init__(self, doc, cover=None, with_fonts=True):
@@ -34,6 +35,9 @@ class EpubFormat(Format):
         if cover is not None:
             self.cover = cover
 
         if cover is not None:
             self.cover = cover
 
+    def dc(self, tag):
+        return self.doc.meta.get_one(DCNS(tag))
+
     def build(self, ctx=None):
 
         def add_file(url, file_id):
     def build(self, ctx=None):
 
         def add_file(url, file_id):
@@ -52,7 +56,7 @@ class EpubFormat(Format):
         guide = opf.find(OPFNS('guide'))
         spine = opf.find(OPFNS('spine'))
 
         guide = opf.find(OPFNS('guide'))
         spine = opf.find(OPFNS('spine'))
 
-        author = ", ". join(self.doc.meta.get(DCNS('creator')) or '')
+        author = ", ". join(self.doc.meta.get(DCNS('creator')) or [])
         title = self.doc.meta.title()
         opf.find('.//' + DCNS('creator')).text = author
         opf.find('.//' + DCNS('title')).text = title
         title = self.doc.meta.title()
         opf.find('.//' + DCNS('creator')).text = author
         opf.find('.//' + DCNS('title')).text = title
@@ -79,8 +83,9 @@ class EpubFormat(Format):
         # nav_map = toc_file[-1]
 
         if self.cover is not None:
         # nav_map = toc_file[-1]
 
         if self.cover is not None:
-            cover_image = self.doc.meta.get(DCNS('relation.coverimage.url'))[0]
+            cover_image = self.doc.meta.get(DCNS('relation.coverimage.url'))[0]
             cover = self.cover(self.doc)
             cover = self.cover(self.doc)
+            cover.set_images(ctx)
             cover_output = cover.build()
             cover_name = 'cover.%s' % cover.format_ext
             zip.writestr(os.path.join('OPS', cover_name), cover_output.get_string())
             cover_output = cover.build()
             cover_name = 'cover.%s' % cover.format_ext
             zip.writestr(os.path.join('OPS', cover_name), cover_output.get_string())
@@ -117,7 +122,7 @@ class EpubFormat(Format):
 
         wrap_tmpl = etree.parse(get_resource('formats/epub/res/chapter.html'))
         for e in self.render(self.doc.edoc.getroot(), ctx):
 
         wrap_tmpl = etree.parse(get_resource('formats/epub/res/chapter.html'))
         for e in self.render(self.doc.edoc.getroot(), ctx):
-            if not len(e) and not e.text.strip():
+            if not len(e) and not (e.text and e.text.strip()):
                 continue
             wrap = deepcopy(wrap_tmpl)
             extend_element(wrap.find('//*[@id="book-text"]'), e)
                 continue
             wrap = deepcopy(wrap_tmpl)
             extend_element(wrap.find('//*[@id="book-text"]'), e)
@@ -149,6 +154,31 @@ class EpubFormat(Format):
             zip.writestr('OPS/footnotes.html', etree.tostring(
                                 wrap, method="html", pretty_print=True))
 
             zip.writestr('OPS/footnotes.html', etree.tostring(
                                 wrap, method="html", pretty_print=True))
 
+        footer_text = [
+            'Information about the resource',
+            'Publisher: %s' % self.dc('publisher'),
+            'Rights: %s' % self.dc('rights'),
+            'Intended audience: %s' % self.dc('audience'),
+            self.dc('description'),
+            'Resource prepared using MIL/PEER editing platform.',
+            'Source available at %s' % ctx.source_url,
+        ]
+        footer_wrap = deepcopy(wrap_tmpl)
+        footer_body = footer_wrap.find('//*[@id="book-text"]')
+        for line in footer_text:
+            footer_line = etree.Element('p')
+            footer_line.text = line
+            footer_body.append(footer_line)
+        manifest.append(manifest.makeelement(OPFNS('item'), attrib={
+            'id': 'footer',
+            'href': "footer.html",
+            'media-type': 'application/xhtml+xml',
+        }))
+        spine.append(spine.makeelement(OPFNS('itemref'), attrib={
+            'idref': 'footer',
+        }))
+        zip.writestr('OPS/footer.html', etree.tostring(footer_wrap, method='html'))
+
         zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True))
         ctx.toc.render(toc_file[-1])
         zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True))
         zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True))
         ctx.toc.render(toc_file[-1])
         zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True))
@@ -200,6 +230,24 @@ class EpubRenderer(TreeRenderer):
         yield wrapper
 
 
         yield wrapper
 
 
+class NaturalText(EpubRenderer):
+    def render_text(self, text, ctx):
+        root, inner = self.text_container()
+        chunks = re.split('(?<=\s\w) ', text)
+        inner.text = chunks[0]
+        for chunk in chunks[1:]:
+            x = etree.Entity("nbsp")
+            x.tail = chunk
+            inner.append(x)
+        return root
+
+
+class Silent(EpubRenderer):
+    def render_text(self, text, ctx):
+        root, inner = self.text_container()
+        return root
+
+
 class Footnotes(object):
     def __init__(self):
         self.counter = 0
 class Footnotes(object):
     def __init__(self):
         self.counter = 0
@@ -261,7 +309,7 @@ class TOC(object):
 
 # Renderers
 
 
 # Renderers
 
-class AsideR(EpubRenderer):
+class AsideR(NaturalText):
     def render(self, element, ctx):
         outputs = list(super(AsideR, self).render(element, ctx))
         anchor = ctx.footnotes.append(outputs)
     def render(self, element, ctx):
         outputs = list(super(AsideR, self).render(element, ctx))
         anchor = ctx.footnotes.append(outputs)
@@ -270,8 +318,10 @@ class AsideR(EpubRenderer):
         yield wrapper
 EpubFormat.renderers.register(core.Aside, None, AsideR('div'))
 
         yield wrapper
 EpubFormat.renderers.register(core.Aside, None, AsideR('div'))
 
+EpubFormat.renderers.register(core.Aside, 'comment', Silent())
 
 
-class DivR(EpubRenderer):
+
+class DivR(NaturalText):
     def container(self, ctx):
         root, inner = super(DivR, self).container(ctx)
         if getattr(ctx, 'inline', False):
     def container(self, ctx):
         root, inner = super(DivR, self).container(ctx)
         if getattr(ctx, 'inline', False):
@@ -279,6 +329,11 @@ class DivR(EpubRenderer):
             inner.set('style', 'display: block;')
         return root, inner
 EpubFormat.renderers.register(core.Div, None, DivR('div'))
             inner.set('style', 'display: block;')
         return root, inner
 EpubFormat.renderers.register(core.Div, None, DivR('div'))
+EpubFormat.renderers.register(core.Div, 'p', NaturalText('p'))
+
+EpubFormat.renderers.register(core.Div, 'list', NaturalText('ul'))
+EpubFormat.renderers.register(core.Div, 'list.enum', NaturalText('ol'))
+EpubFormat.renderers.register(core.Div, 'item', NaturalText('li'))
 
 
 class DivImageR(EpubRenderer):
 
 
 class DivImageR(EpubRenderer):
@@ -297,13 +352,13 @@ class DivImageR(EpubRenderer):
 EpubFormat.renderers.register(core.Div, 'img', DivImageR('img'))
 
 
 EpubFormat.renderers.register(core.Div, 'img', DivImageR('img'))
 
 
-class HeaderR(EpubRenderer):
+class HeaderR(NaturalText):
     def subcontext(self, element, ctx):
         return Context(ctx, inline=True)
 EpubFormat.renderers.register(core.Header, None, HeaderR('h1'))
 
 
     def subcontext(self, element, ctx):
         return Context(ctx, inline=True)
 EpubFormat.renderers.register(core.Header, None, HeaderR('h1'))
 
 
-class SectionR(EpubRenderer):
+class SectionR(NaturalText):
     epub_separate = True
 
     def render(self, element, ctx):
     epub_separate = True
 
     def render(self, element, ctx):
@@ -315,6 +370,21 @@ class SectionR(EpubRenderer):
 EpubFormat.renderers.register(core.Section, None, SectionR())
 
 
 EpubFormat.renderers.register(core.Section, None, SectionR())
 
 
-class SpanR(EpubRenderer):
+class SpanR(NaturalText):
     pass
 EpubFormat.renderers.register(core.Span, None, SpanR('span'))
     pass
 EpubFormat.renderers.register(core.Span, None, SpanR('span'))
+EpubFormat.renderers.register(core.Span, 'cite', SpanR('i'))
+EpubFormat.renderers.register(core.Span, 'emp', SpanR('b'))
+EpubFormat.renderers.register(core.Span, 'emph', SpanR('i'))
+
+
+class SpanLink(EpubRenderer):
+    def render(self, element, ctx):
+        parts = super(SpanLink, self).render(element, ctx)
+        for part in parts:
+            src = element.attrib.get('href', '')
+            if src.startswith('file://'):
+                src = ctx.files_path + src[7:]
+            part[0].attrib['href'] = src
+            yield part
+EpubFormat.renderers.register(core.Span, 'link', SpanLink('a'))