Basic biblical tools.
[librarian.git] / src / librarian / builders / epub.py
index 91405c3..3ef95f4 100644 (file)
@@ -1,17 +1,17 @@
+# This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
+#
 from datetime import date
 from datetime import date
+import io
 import os
 import os
+import re
 import tempfile
 from ebooklib import epub
 from lxml import etree
 import tempfile
 from ebooklib import epub
 from lxml import etree
-import six
 from librarian import functions, OutputFile, get_resource, XHTMLNS
 from librarian.cover import make_cover
 from librarian.embeds.mathml import MathML
 from librarian import functions, OutputFile, get_resource, XHTMLNS
 from librarian.cover import make_cover
 from librarian.embeds.mathml import MathML
-import librarian.epub
 from librarian.fonts import strip_font
 from librarian.fonts import strip_font
-from librarian.fundraising import FUNDRAISING
-
-
 
 
 class Xhtml:
 
 
 class Xhtml:
@@ -30,9 +30,11 @@ class Xhtml:
 class Builder:
     file_extension = None
 
 class Builder:
     file_extension = None
 
-    def __init__(self, base_url=None):
+    def __init__(self, base_url=None, fundraising=None, cover=None):
         self._base_url = base_url or 'file:///home/rczajka/for/fnp/librarian/temp~/maly/img/'
         self._base_url = base_url or 'file:///home/rczajka/for/fnp/librarian/temp~/maly/img/'
+        self.fundraising = fundraising
         self.footnotes = etree.Element('div', id='footnotes')
         self.footnotes = etree.Element('div', id='footnotes')
+        self.make_cover = cover or make_cover
 
         self.cursors = {
 #            None: None,
 
         self.cursors = {
 #            None: None,
@@ -60,8 +62,6 @@ class Builder:
     def forget_fragment(self, name):
         del self.cursors[name]
 
     def forget_fragment(self, name):
         del self.cursors[name]
 
-
-
     @property
     def base_url(self):
         if self._base_url is not None:
     @property
     def base_url(self):
         if self._base_url is not None:
@@ -77,11 +77,16 @@ class Builder:
 
 
 class EpubBuilder(Builder):
 
 
 class EpubBuilder(Builder):
+    build_method_fn = 'epub_build'
     file_extension = 'epub'
     file_extension = 'epub'
+    isbn_field = 'isbn_epub'
+    orphans = True
 
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args, debug=False, **kwargs):
         self.chars = set()
         self.fundr = 0
         self.chars = set()
         self.fundr = 0
+        self.debug = debug
+        self.splits = []
         super().__init__(*args, **kwargs)
     
     def build(self, document, **kwargs):
         super().__init__(*args, **kwargs)
     
     def build(self, document, **kwargs):
@@ -109,7 +114,6 @@ class EpubBuilder(Builder):
 
         self.set_metadata()
         
 
         self.set_metadata()
         
-
         self.add_cover()
         
         self.add_title_page()
         self.add_cover()
         
         self.add_title_page()
@@ -140,21 +144,21 @@ class EpubBuilder(Builder):
         self.add_support_page()
         self.add_last_page()
 
         self.add_support_page()
         self.add_last_page()
 
+        if self.fundraising:
+            e = len(self.output.spine) - 3 - 3
+            nfunds = len(self.fundraising)
+            if e > 4 * nfunds:
+                nfunds *= 2
 
 
-        e = len(self.output.spine) - 3 - 3
-        nfunds = len(FUNDRAISING)
-        if e > 16:
-            nfunds *= 2
+            # COUNTING CHARACTERS?
+            for f in range(nfunds):
+                spine_index = int(4 + (f / nfunds * e) + f)
 
 
-        # COUNTING CHARACTERS?
-        for f in range(nfunds):
-            spine_index = int(4 + (f / nfunds * e) + f)
-
-            h = Xhtml()
-            h.body.append(
-                etree.XML('<div id="book-text"><div class="fundraising">' + FUNDRAISING[f % len(FUNDRAISING)] + '</div></div>')
-            )
-            self.add_html(h.element, file_name='fund%d.xhtml' % f, spine=spine_index)
+                h = Xhtml()
+                h.body.append(
+                    etree.XML('<div id="book-text"><div class="fundraising">' + self.fundraising[f % len(self.fundraising)] + '</div></div>')
+                )
+                self.add_html(h.element, file_name='fund%d.xhtml' % f, spine=spine_index)
 
         self.add_fonts()
 
 
         self.add_fonts()
 
@@ -202,7 +206,7 @@ class EpubBuilder(Builder):
     def add_title_page(self):
         html = Xhtml()
         html.title.text = "Strona tytułowa"
     def add_title_page(self):
         html = Xhtml()
         html.title.text = "Strona tytułowa"
-        bt = etree.SubElement(html.body, 'div', **{'class': 'book-text'})
+        bt = etree.SubElement(html.body, 'div', **{'id': 'book-text'})
         tp = etree.SubElement(bt, 'div', **{'class': 'title-page'})
 
         # Tak jak jest teraz – czy może być jednocześnie
         tp = etree.SubElement(bt, 'div', **{'class': 'title-page'})
 
         # Tak jak jest teraz – czy może być jednocześnie
@@ -212,10 +216,10 @@ class EpubBuilder(Builder):
 
         e = self.document.tree.find('//autor_utworu')
         if e is not None:
 
         e = self.document.tree.find('//autor_utworu')
         if e is not None:
-            etree.SubElement(tp, 'h2', **{'class': 'author'}).text = e.raw_printable_text()
+            etree.SubElement(tp, 'h2', **{'class': 'author'}).text = e.raw_printable_text(self)
         e = self.document.tree.find('//nazwa_utworu')
         if e is not None:
         e = self.document.tree.find('//nazwa_utworu')
         if e is not None:
-            etree.SubElement(tp, 'h1', **{'class': 'title'}).text = e.raw_printable_text()
+            etree.SubElement(tp, 'h1', **{'class': 'title'}).text = e.raw_printable_text(self)
 
         if not len(tp):
             for author in self.document.meta.authors:
 
         if not len(tp):
             for author in self.document.meta.authors:
@@ -237,7 +241,7 @@ class EpubBuilder(Builder):
 
         p = etree.XML("""<p class="info">
               <a>Ta lektura</a>, podobnie jak tysiące innych, jest dostępna on-line na stronie
 
         p = etree.XML("""<p class="info">
               <a>Ta lektura</a>, podobnie jak tysiące innych, jest dostępna on-line na stronie
-              <a href="http://www.wolnelektury.pl/">wolnelektury.pl</a>.
+              <a href="https://wolnelektury.pl/">wolnelektury.pl</a>.
             </p>""")
         p[0].attrib['href'] = str(self.document.meta.url)
         tp.append(p)
             </p>""")
         p[0].attrib['href'] = str(self.document.meta.url)
         tp.append(p)
@@ -247,15 +251,15 @@ class EpubBuilder(Builder):
         
         tp.append(etree.XML("""
           <p class="info">
         
         tp.append(etree.XML("""
           <p class="info">
-            Utwór opracowany został w&#160;ramach projektu<a href="http://www.wolnelektury.pl/"> Wolne Lektury</a> przez<a href="http://www.nowoczesnapolska.org.pl/"> fundację Nowoczesna Polska</a>.
+            Utwór opracowany został w&#160;ramach projektu<a href="https://wolnelektury.pl/"> Wolne Lektury</a> przez<a href="https://fundacja.wolnelektury.pl/"> fundację Wolne Lektury</a>.
           </p>
         """))
 
           </p>
         """))
 
-        if self.document.meta.isbn_epub:
-            etree.SubElement(tp, 'p', **{"class": "info"}).text = self.document.meta.isbn_epub
+        if getattr(self.document.meta, self.isbn_field):
+            etree.SubElement(tp, 'p', **{"class": "info"}).text = getattr(self.document.meta, self.isbn_field)
 
         tp.append(etree.XML("""<p class="footer info">
 
         tp.append(etree.XML("""<p class="footer info">
-            <a href="http://www.wolnelektury.pl/"><img src="logo_wolnelektury.png" alt="WolneLektury.pl" /></a>
+            <a href="https://wolnelektury.pl/"><img src="logo_wolnelektury.png" alt="WolneLektury.pl" /></a>
         </p>"""))
 
         self.add_html(
         </p>"""))
 
         self.add_html(
@@ -282,13 +286,13 @@ class EpubBuilder(Builder):
         for i, author in enumerate(self.document.meta.authors):
             self.output.add_author(
                 author.readable(),
         for i, author in enumerate(self.document.meta.authors):
             self.output.add_author(
                 author.readable(),
-                file_as=six.text_type(author),
+                file_as=str(author),
                 uid='creator{}'.format(i)
             )
         for translator in self.document.meta.translators:
             self.output.add_author(
                 translator.readable(),
                 uid='creator{}'.format(i)
             )
         for translator in self.document.meta.translators:
             self.output.add_author(
                 translator.readable(),
-                file_as=six.text_type(translator),
+                file_as=str(translator),
                 role='trl',
                 uid='translator{}'.format(i)
             )
                 role='trl',
                 uid='translator{}'.format(i)
             )
@@ -302,6 +306,7 @@ class EpubBuilder(Builder):
 
     def add_toc(self):
         item = epub.EpubNav()
 
     def add_toc(self):
         item = epub.EpubNav()
+        item.add_link(href='style.css', rel='stylesheet', type='text/css')
         self.output.add_item(item)
         self.output.spine.append(item)
         self.output.add_item(epub.EpubNcx())
         self.output.add_item(item)
         self.output.spine.append(item)
         self.output.add_item(epub.EpubNcx())
@@ -318,7 +323,7 @@ class EpubBuilder(Builder):
 
     def add_support_page(self):
         self.add_file(
 
     def add_support_page(self):
         self.add_file(
-            get_resource('epub/support.xhtml'),
+            get_resource('res/epub/support.xhtml'),
             spine=True,
             toc='Wesprzyj Wolne Lektury'
         )
             spine=True,
             toc='Wesprzyj Wolne Lektury'
         )
@@ -328,7 +333,7 @@ class EpubBuilder(Builder):
             media_type='image/png'
         )
         self.add_file(
             media_type='image/png'
         )
         self.add_file(
-            get_resource('epub/style.css'),
+            get_resource('res/epub/style.css'),
             media_type='text/css'
         )
 
             media_type='text/css'
         )
 
@@ -380,8 +385,6 @@ class EpubBuilder(Builder):
             doctype='<!DOCTYPE html>'
         )
 
             doctype='<!DOCTYPE html>'
         )
 
-        html = librarian.epub.squeeze_whitespace(html)
-
         self.add_file(
             content=html,
             **kwargs
         self.add_file(
             content=html,
             **kwargs
@@ -389,8 +392,6 @@ class EpubBuilder(Builder):
             
         
     def add_fonts(self):
             
         
     def add_fonts(self):
-        print("".join(sorted(self.chars)))
-        # TODO: optimizer
         for fname in ('DejaVuSerif.ttf', 'DejaVuSerif-Bold.ttf',
                       'DejaVuSerif-Italic.ttf', 'DejaVuSerif-BoldItalic.ttf'):
             self.add_file(
         for fname in ('DejaVuSerif.ttf', 'DejaVuSerif-Bold.ttf',
                       'DejaVuSerif-Italic.ttf', 'DejaVuSerif-BoldItalic.ttf'):
             self.add_file(
@@ -531,27 +532,46 @@ class EpubBuilder(Builder):
         newp = lambda: etree.SubElement(d, 'p', {'class': 'info'})
 
         p = newp()
         newp = lambda: etree.SubElement(d, 'p', {'class': 'info'})
 
         p = newp()
+        p.text = (
+            "Wszystkie zasoby Wolnych Lektur możesz swobodnie wykorzystywać, "
+            "publikować i rozpowszechniać pod warunkiem zachowania warunków "
+            "licencji i zgodnie z "
+        )
+        a = etree.SubElement(p, "a", href="https://wolnelektury.pl/info/zasady-wykorzystania/")
+        a.text = "Zasadami wykorzystania Wolnych Lektur"
+        a.tail = "."
+
+        etree.SubElement(p, "br")
+        
+
         if m.license:
         if m.license:
-            p.text = """
-                      Ten utwór jest udostępniony na licencji
-                      """
+            p[-1].tail = "Ten utwór jest udostępniony na licencji "
             etree.SubElement(p, 'a', href=m.license).text = m.license_description
         else:
             etree.SubElement(p, 'a', href=m.license).text = m.license_description
         else:
-            p.text = """
-                    Ten utwór nie jest objęty majątkowym prawem autorskim i znajduje się w domenie
-                    publicznej, co oznacza że możesz go swobodnie wykorzystywać, publikować
-                    i rozpowszechniać. Jeśli utwór opatrzony jest dodatkowymi materiałami
-                    (przypisy, motywy literackie etc.), które podlegają prawu autorskiemu, to
-                    te dodatkowe materiały udostępnione są na licencji
-                    """
-            a = etree.SubElement(p, "a", href="http://creativecommons.org/licenses/by-sa/3.0/")
-            a.text = """Creative Commons
-                    Uznanie Autorstwa – Na Tych Samych Warunkach 3.0 PL"""
-            a.tail = "."
+            p[-1].tail = 'Ten utwór jest w domenie publicznej.'
 
 
+        etree.SubElement(p, "br")
+        
+        p[-1].tail = (
+            "Wszystkie materiały dodatkowe (przypisy, motywy literackie) są "
+            "udostępnione na "
+            )
+        etree.SubElement(p, 'a', href='https://artlibre.org/licence/lal/pl/').text = 'Licencji Wolnej Sztuki 1.3'
+        p[-1].tail = '.'
+        etree.SubElement(p, "br")
+        p[-1].tail = (
+            "Fundacja Wolne Lektury zastrzega sobie prawa do wydania "
+            "krytycznego zgodnie z art. Art.99(2) Ustawy o prawach autorskich "
+            "i prawach pokrewnych. Wykorzystując zasoby z Wolnych Lektur, "
+            "należy pamiętać o zapisach licencji oraz zasadach, które "
+            "spisaliśmy w "
+        )
+
+        etree.SubElement(p, 'a', href='https://wolnelektury.pl/info/zasady-wykorzystania/').text = 'Zasadach wykorzystania Wolnych Lektur'
+        p[-1].tail = '. Zapoznaj się z nimi, zanim udostępnisz dalej nasze książki.'
 
         p = newp()
 
         p = newp()
-        p.text = 'Źródło: '
+        p.text = 'E-book można pobrać ze strony: '
         etree.SubElement(
             p, 'a', href=str(m.url),
             title=', '.join((
         etree.SubElement(
             p, 'a', href=str(m.url),
             title=', '.join((
@@ -560,7 +580,8 @@ class EpubBuilder(Builder):
             ))
         ).text = str(m.url)
 
             ))
         ).text = str(m.url)
 
-        newp().text = 'Tekst opracowany na podstawie: ' + m.source_name
+        if m.source_name:
+            newp().text = 'Tekst opracowany na podstawie: ' + m.source_name
 
         newp().text = """
               Wydawca:
 
         newp().text = """
               Wydawca:
@@ -570,9 +591,11 @@ class EpubBuilder(Builder):
             newp().text = m.description
 
 
             newp().text = m.description
 
 
-        if m.editors:
+        editors = self.document.editors()
+        if editors:
             newp().text = 'Opracowanie redakcyjne i przypisy: %s.' % (
             newp().text = 'Opracowanie redakcyjne i przypisy: %s.' % (
-                ', '.join(e.readable() for e in sorted(self.document.editors())))
+                ', '.join(e.readable() for e in sorted(editors))
+            )
 
         if m.funders:
             etree.SubElement(d, 'p', {'class': 'minor-info'}).text = '''Publikację wsparli i wsparły:
 
         if m.funders:
             etree.SubElement(d, 'p', {'class': 'minor-info'}).text = '''Publikację wsparli i wsparły:
@@ -590,8 +613,8 @@ class EpubBuilder(Builder):
             else:
                 p.text += m.cover_by
             
             else:
                 p.text += m.cover_by
             
-        if m.isbn_epub:
-            newp().text = m.isbn_epub
+        if getattr(m, self.isbn_field):
+            newp().text = getattr(m, self.isbn_field)
 
         newp().text = '\u00a0'
 
 
         newp().text = '\u00a0'
 
@@ -645,10 +668,10 @@ class EpubBuilder(Builder):
     def add_cover(self):
         # TODO: allow other covers
 
     def add_cover(self):
         # TODO: allow other covers
 
-        cover_maker = make_cover
+        cover_maker = self.make_cover
 
 
-        cover_file = six.BytesIO()
-        cover = cover_maker(self.document.meta)
+        cover_file = io.BytesIO()
+        cover = cover_maker(self.document.meta, width=600)
         cover.save(cover_file)
         cover_name = 'cover.%s' % cover.ext()
 
         cover.save(cover_file)
         cover_name = 'cover.%s' % cover.ext()
 
@@ -673,7 +696,7 @@ class EpubBuilder(Builder):
 </html>''' % cover.ext()).encode('utf-8')
         self.add_file(file_name='cover.xhtml', content=ci)
 
 </html>''' % cover.ext()).encode('utf-8')
         self.add_file(file_name='cover.xhtml', content=ci)
 
-        self.output.spine.append('cover')
+        self.output.spine.append(('cover', 'no'))
         self.output.guide.append({
             'type': 'cover',
             'href': 'cover.xhtml',
         self.output.guide.append({
             'type': 'cover',
             'href': 'cover.xhtml',
@@ -688,3 +711,8 @@ class EpubBuilder(Builder):
             file_name=name
         )
         return name
             file_name=name
         )
         return name
+
+    def process_comment(self, comment):
+        m = re.match(r'TRIM:(\d+)', comment.text)
+        if m is not None:
+            self.splits.append(comment.sourceline - int(m.group(1)))