New EPUB builder, other minor changes.
authorRadek Czajka <rczajka@rczajka.pl>
Mon, 5 Jul 2021 09:47:09 +0000 (11:47 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Mon, 5 Jul 2021 09:47:09 +0000 (11:47 +0200)
79 files changed:
src/librarian/builders/__init__.py
src/librarian/builders/epub.py [new file with mode: 0644]
src/librarian/builders/pdf.py [new file with mode: 0644]
src/librarian/builders/txt.py
src/librarian/cover.py
src/librarian/document.py
src/librarian/elements/__init__.py
src/librarian/elements/base.py
src/librarian/elements/blocks/dedykacja.py
src/librarian/elements/blocks/dlugi_cytat.py
src/librarian/elements/blocks/nota.py
src/librarian/elements/blocks/poezja_cyt.py
src/librarian/elements/blocks/ramka.py
src/librarian/elements/comments/abstrakt.py
src/librarian/elements/comments/nota_red.py
src/librarian/elements/comments/uwaga.py
src/librarian/elements/drama/didask_tekst.py
src/librarian/elements/drama/didaskalia.py
src/librarian/elements/drama/kwestia.py
src/librarian/elements/drama/lista_osob.py
src/librarian/elements/drama/lista_osoba.py
src/librarian/elements/drama/miejsce_czas.py
src/librarian/elements/drama/naglowek_listy.py
src/librarian/elements/drama/naglowek_osoba.py
src/librarian/elements/drama/osoba.py
src/librarian/elements/figures/ilustr.py
src/librarian/elements/figures/kol.py
src/librarian/elements/figures/tabela.py
src/librarian/elements/figures/wiersz.py
src/librarian/elements/footnotes/__init__.py
src/librarian/elements/front/autor_utworu.py
src/librarian/elements/front/dzielo_nadrzedne.py
src/librarian/elements/front/motto.py
src/librarian/elements/front/motto_podpis.py
src/librarian/elements/front/nazwa_utworu.py
src/librarian/elements/front/podtytul.py
src/librarian/elements/headers/__init__.py
src/librarian/elements/headers/naglowek_czesc.py
src/librarian/elements/headers/naglowek_rozdzial.py
src/librarian/elements/headers/naglowek_scena.py [new file with mode: 0644]
src/librarian/elements/headers/podtytul_czesc.py
src/librarian/elements/headers/podtytul_podrozdzial.py
src/librarian/elements/headers/podtytul_rozdzial.py
src/librarian/elements/paragraphs/akap.py
src/librarian/elements/poetry/strofa.py
src/librarian/elements/poetry/wers.py
src/librarian/elements/poetry/wers_akap.py
src/librarian/elements/poetry/wers_cd.py
src/librarian/elements/poetry/wers_do_prawej.py
src/librarian/elements/poetry/wers_wciety.py
src/librarian/elements/separators/sekcja_asterysk.py
src/librarian/elements/separators/sekcja_swiatlo.py
src/librarian/elements/separators/separator_linia.py
src/librarian/elements/styles/indeks_dolny.py
src/librarian/elements/styles/mat.py
src/librarian/elements/styles/slowo_obce.py
src/librarian/elements/styles/tytul_dziela.py
src/librarian/elements/styles/wieksze_odstepy.py
src/librarian/elements/styles/wyroznienie.py
src/librarian/elements/themes/motyw.py
src/librarian/epub.py
src/librarian/epub/style.css
src/librarian/epub/xsltAnnotations.xsl
src/librarian/epub/xsltScheme.xsl
src/librarian/fonts.py [new file with mode: 0644]
src/librarian/fundraising.py [new file with mode: 0644]
src/librarian/html.py
src/librarian/pdf.py
src/librarian/pdf/wl.cls
src/librarian/pdf/wl2tex.xslt
src/librarian/res/BN.png [new file with mode: 0644]
src/librarian/res/MKIDN.jpg [new file with mode: 0644]
src/librarian/res/atrium-logo.png
src/librarian/res/dofinansowano.png [new file with mode: 0644]
src/librarian/text.py
src/librarian/xslt/book2html.xslt
tests/uat/main.xml
tests/uat/parent.xml [new file with mode: 0644]
tests/uat/part2.xml [new file with mode: 0644]

index dc5bdee..e359cd6 100644 (file)
@@ -3,6 +3,8 @@ from .txt import TxtBuilder
 from .html import HtmlBuilder, StandaloneHtmlBuilder, DaisyHtmlBuilder
 from .sanitize import Sanitizer
 from .daisy import DaisyBuilder
+from .epub import EpubBuilder
+from .pdf import PdfBuilder
 
 
 builders = OrderedDict([
@@ -12,4 +14,7 @@ builders = OrderedDict([
     ("html-daisy", DaisyHtmlBuilder),
     ("daisy", DaisyBuilder),
     ("sanitizer", Sanitizer),
+
+    ("epub", EpubBuilder),
+    ("pdf", PdfBuilder),
 ])
diff --git a/src/librarian/builders/epub.py b/src/librarian/builders/epub.py
new file mode 100644 (file)
index 0000000..91405c3
--- /dev/null
@@ -0,0 +1,690 @@
+from datetime import date
+import os
+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
+import librarian.epub
+from librarian.fonts import strip_font
+from librarian.fundraising import FUNDRAISING
+
+
+
+
+class Xhtml:
+    def __init__(self):
+        self.element = etree.XML('''<html xmlns="http://www.w3.org/1999/xhtml"><head><link rel="stylesheet" href="style.css" type="text/css"/><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>WolneLektury.pl</title></head><body/></html>''')
+
+    @property
+    def title(self):
+        return self.element.find('.//' + XHTMLNS('title'))
+        
+    @property
+    def body(self):
+        return self.element.find('.//' + XHTMLNS('body'))
+
+
+class Builder:
+    file_extension = None
+
+    def __init__(self, base_url=None):
+        self._base_url = base_url or 'file:///home/rczajka/for/fnp/librarian/temp~/maly/img/'
+        self.footnotes = etree.Element('div', id='footnotes')
+
+        self.cursors = {
+#            None: None,
+#            'header': self.header,
+            'footnotes': self.footnotes,
+        }
+        self.current_cursors = []
+
+        self.toc_base = 0
+
+    @property
+    def cursor(self):
+        return self.current_cursors[-1]
+
+    def enter_fragment(self, fragment):
+        self.current_cursors.append(self.cursors[fragment])
+
+    def exit_fragment(self):
+        self.current_cursors.pop()
+
+    def create_fragment(self, name, element):
+        assert name not in self.cursors
+        self.cursors[name] = element
+
+    def forget_fragment(self, name):
+        del self.cursors[name]
+
+
+
+    @property
+    def base_url(self):
+        if self._base_url is not None:
+            return self._base_url
+        else:
+            return 'https://wolnelektury.pl/media/book/pictures/{}/'.format(self.document.meta.url.slug)
+
+
+    # Base URL should be on Document level, not builder.
+    def build(self, document, **kwargs):
+        """Should return an OutputFile with the output."""
+        raise NotImplementedError()
+
+
+class EpubBuilder(Builder):
+    file_extension = 'epub'
+
+    def __init__(self, *args, **kwargs):
+        self.chars = set()
+        self.fundr = 0
+        super().__init__(*args, **kwargs)
+    
+    def build(self, document, **kwargs):
+        # replace_characters -- nie, robimy to na poziomie elementów
+        
+        # hyphenator (\00ad w odp. miejscach) -- jeśli już, to też powinno to się dziać na poziomie elementów
+        # spójniki (\u00a0 po)-- jeśli już, to na poziomie elementów
+        # trick na dywizy: &#xad;&#8288;-
+
+        # do toc trafia:
+        #   początek z KAŻDEGO PLIKU xml
+        
+        # zliczamy zbiór użytych znaków
+
+        # flagi:
+        # mieliśmy taką flagę less-advertising, używaną tylko dla Prestigio; już nie używamy.
+
+        # @editors = document.editors() (jako str)
+        # @funders = join(meta.funders)
+        # @thanks = meta.thanks
+
+
+        self.output = output = epub.EpubBook()
+        self.document = document
+
+        self.set_metadata()
+        
+
+        self.add_cover()
+        
+        self.add_title_page()
+        self.add_toc()
+
+
+
+        self.start_chunk()
+
+        self.add_toc_entry(
+            None,
+            'Początek utworu', # i18n
+            0
+        )
+        self.output.guide.append({
+            "type": "text",
+            "title": "Początek",
+            "href": "part1.xhtml"
+        })
+
+
+        self.build_document(self.document)
+
+        
+        self.close_chunk()
+
+        self.add_annotations()
+        self.add_support_page()
+        self.add_last_page()
+
+
+        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)
+
+            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)
+
+        self.add_fonts()
+
+        output_file = tempfile.NamedTemporaryFile(
+            prefix='librarian', suffix='.epub',
+            delete=False)
+        output_file.close()
+        epub.write_epub(output_file.name, output, {'epub3_landmark': False})
+        return OutputFile.from_filename(output_file.name)
+
+    def build_document(self, document):
+        self.toc_precedences = []
+
+        self.start_chunk()
+
+
+        document.tree.getroot().epub_build(self)
+        if document.meta.parts:
+            self.start_chunk()
+
+            self.start_element('div', {'class': 'title-page'})
+            self.start_element('h1', {'class': 'title'})
+            self.push_text(document.meta.title)
+            self.end_element()
+            self.end_element()
+
+            ######
+            # 160
+            # translators
+            # working copy?
+            # ta lektura
+            # tanks
+            # utwor opracowany
+            # isbn
+            # logo
+
+            for child in document.children:
+                self.start_chunk()
+                self.add_toc_entry(None, child.meta.title, 0)
+                self.build_document(child)
+
+        self.shift_toc_base()
+            
+    
+    def add_title_page(self):
+        html = Xhtml()
+        html.title.text = "Strona tytułowa"
+        bt = etree.SubElement(html.body, 'div', **{'class': 'book-text'})
+        tp = etree.SubElement(bt, 'div', **{'class': 'title-page'})
+
+        # Tak jak jest teraz – czy może być jednocześnie
+        # no „autor_utworu”
+        # i „dzieło nadrzędne”
+        # wcześniej mogło być dzieło nadrzędne,
+
+        e = self.document.tree.find('//autor_utworu')
+        if e is not None:
+            etree.SubElement(tp, 'h2', **{'class': 'author'}).text = e.raw_printable_text()
+        e = self.document.tree.find('//nazwa_utworu')
+        if e is not None:
+            etree.SubElement(tp, 'h1', **{'class': 'title'}).text = e.raw_printable_text()
+
+        if not len(tp):
+            for author in self.document.meta.authors:
+                etree.SubElement(tp, 'h2', **{'class': 'author'}).text = author.readable()
+            etree.SubElement(tp, 'h1', **{'class': 'title'}).text = self.document.meta.title
+
+#                <xsl:apply-templates select="//nazwa_utworu | //podtytul | //dzielo_nadrzedne" mode="poczatek"/>
+#        else:
+#                            <xsl:apply-templates select="//dc:creator" mode="poczatek"/>
+#                <xsl:apply-templates select="//dc:title | //podtytul | //dzielo_nadrzedne" mode="poczatek"/>
+
+        etree.SubElement(tp, 'p', **{"class": "info"}).text = '\u00a0'
+
+        if self.document.meta.translators:
+            p = etree.SubElement(tp, 'p', **{'class': 'info'})
+            p.text = 'tłum. ' + ', '.join(t.readable() for t in self.document.meta.translators)
+                
+        #<p class="info">[Kopia robocza]</p>
+
+        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>.
+            </p>""")
+        p[0].attrib['href'] = str(self.document.meta.url)
+        tp.append(p)
+
+        if self.document.meta.thanks:
+            etree.SubElement(tp, 'p', **{'class': 'info'}).text = self.document.meta.thanks
+        
+        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>.
+          </p>
+        """))
+
+        if self.document.meta.isbn_epub:
+            etree.SubElement(tp, 'p', **{"class": "info"}).text = self.document.meta.isbn_epub
+
+        tp.append(etree.XML("""<p class="footer info">
+            <a href="http://www.wolnelektury.pl/"><img src="logo_wolnelektury.png" alt="WolneLektury.pl" /></a>
+        </p>"""))
+
+        self.add_html(
+            html.element,
+            file_name='title.xhtml',
+            spine=True,
+            toc='Strona tytułowa' # TODO: i18n
+        )
+
+        self.add_file(
+            get_resource('res/wl-logo-small.png'),
+            file_name='logo_wolnelektury.png',
+            media_type='image/png'
+        )
+    
+    def set_metadata(self):
+        self.output.set_identifier(
+            str(self.document.meta.url))
+        self.output.set_language(
+            functions.lang_code_3to2(self.document.meta.language)
+        )
+        self.output.set_title(self.document.meta.title)
+
+        for i, author in enumerate(self.document.meta.authors):
+            self.output.add_author(
+                author.readable(),
+                file_as=six.text_type(author),
+                uid='creator{}'.format(i)
+            )
+        for translator in self.document.meta.translators:
+            self.output.add_author(
+                translator.readable(),
+                file_as=six.text_type(translator),
+                role='trl',
+                uid='translator{}'.format(i)
+            )
+        for publisher in self.document.meta.publisher:
+            self.output.add_metadata("DC", "publisher", publisher)
+
+        self.output.add_metadata("DC", "date", self.document.meta.created_at)
+
+        
+
+
+    def add_toc(self):
+        item = epub.EpubNav()
+        self.output.add_item(item)
+        self.output.spine.append(item)
+        self.output.add_item(epub.EpubNcx())
+
+        self.output.toc.append(
+            epub.Link(
+                "nav.xhtml",
+                "Spis treści",
+                "nav"
+            )
+        )
+
+    
+
+    def add_support_page(self):
+        self.add_file(
+            get_resource('epub/support.xhtml'),
+            spine=True,
+            toc='Wesprzyj Wolne Lektury'
+        )
+
+        self.add_file(
+            get_resource('res/jedenprocent.png'),
+            media_type='image/png'
+        )
+        self.add_file(
+            get_resource('epub/style.css'),
+            media_type='text/css'
+        )
+
+
+    def add_file(self, path=None, content=None,
+                 media_type='application/xhtml+xml',
+                 file_name=None, uid=None,
+                 spine=False, toc=None):
+
+        # update chars?
+        # jakieś tam ścieśnianie białych znaków?
+
+        if content is None:
+            with open(path, 'rb') as f:
+                content = f.read()
+            if file_name is None:
+                file_name = path.rsplit('/', 1)[-1]
+
+        if uid is None:
+            uid = file_name.split('.', 1)[0]
+
+        item = epub.EpubItem(
+            uid=uid,
+            file_name=file_name,
+            media_type=media_type,
+            content=content
+        )
+
+        self.output.add_item(item)
+        if spine:
+            if spine is True:
+                self.output.spine.append(item)
+            else:
+                self.output.spine.insert(spine, item)
+
+        if toc:
+            self.output.toc.append(
+                epub.Link(
+                    file_name,
+                    toc,
+                    uid
+                )
+            )
+
+    def add_html(self, html_tree, **kwargs):
+        html = etree.tostring(
+            html_tree, pretty_print=True, xml_declaration=True,
+            encoding="utf-8",
+            doctype='<!DOCTYPE html>'
+        )
+
+        html = librarian.epub.squeeze_whitespace(html)
+
+        self.add_file(
+            content=html,
+            **kwargs
+        )
+            
+        
+    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(
+                content=strip_font(
+                    get_resource('fonts/' + fname),
+                    self.chars
+                ),
+                file_name=fname,
+                media_type='font/ttf'
+            )
+
+    def start_chunk(self):
+        if getattr(self, 'current_chunk', None) is not None:
+            if not len(self.current_chunk):
+                return
+            self.close_chunk()
+        self.current_chunk = etree.Element(
+            'div',
+            id="book-text"
+        )
+        self.cursors[None] = self.current_chunk
+        self.current_cursors.append(self.current_chunk)
+
+        self.section_number = 0
+        
+
+    def close_chunk(self):
+        assert self.cursor is self.current_chunk
+        ###### -- what if we're inside?
+
+        chunk_no = getattr(
+            self,
+            'chunk_counter',
+            1
+        )
+        self.chunk_counter = chunk_no + 1
+
+        html = Xhtml()
+        html.body.append(self.current_chunk)
+        
+        self.add_html(
+            ## html container from template.
+            #self.current_chunk,
+            html.element,
+            file_name='part%d.xhtml' % chunk_no,
+            spine=True,
+            
+        )
+        self.current_chunk = None
+        self.current_cursors.pop()
+
+    def start_element(self, tag, attr):
+        self.current_cursors.append(
+            etree.SubElement(self.cursor, tag, **attr)
+        )
+        
+    def end_element(self):
+        self.current_cursors.pop()
+        
+    def push_text(self, text):
+        self.chars.update(text)
+        if len(self.cursor):
+            self.cursor[-1].tail = (self.cursor[-1].tail or '') + text
+        else:
+            self.cursor.text = (self.cursor.text or '') + text
+
+
+    def assign_image_number(self):
+        image_number = getattr(self, 'image_number', 0)
+        self.image_number = image_number + 1
+        return image_number
+
+    def assign_footnote_number(self):
+        number = getattr(self, 'footnote_number', 1)
+        self.footnote_number = number + 1
+        return number
+
+    def assign_section_number(self):
+        number = getattr(self, 'section_number', 1)
+        self.section_number = number + 1
+        return number
+
+    def assign_mathml_number(self):
+        number = getattr(self, 'mathml_number', 0)
+        self.mathml_number = number + 1
+        return number
+
+    
+    def add_toc_entry(self, fragment, name, precedence):
+        if precedence:
+            while self.toc_precedences and self.toc_precedences[-1] >= precedence:
+                self.toc_precedences.pop()
+        else:
+            self.toc_precedences = []
+
+        real_level = self.toc_base + len(self.toc_precedences)
+        if precedence:
+            self.toc_precedences.append(precedence)
+        else:
+            self.toc_base += 1
+        
+        part_number = getattr(
+            self,
+            'chunk_counter',
+            1
+        )
+        filename = 'part%d.xhtml' % part_number
+        uid = filename.split('.')[0]
+        if fragment:
+            filename += '#' + fragment
+            uid += '-' + fragment
+
+        toc = self.output.toc
+        for l in range(1, real_level):
+            if isinstance(toc[-1], epub.Link):
+                toc[-1] = [toc[-1], []]
+            toc = toc[-1][1]
+
+        toc.append(
+            epub.Link(
+                filename,
+                name,
+                uid
+            )
+        )
+
+    def shift_toc_base(self):
+        self.toc_base -= 1
+        
+
+    def add_last_page(self):
+        html = Xhtml()
+        m = self.document.meta
+        
+        html.title.text = 'Strona redakcyjna'
+        d = etree.SubElement(html.body, 'div', id='book-text')
+
+        newp = lambda: etree.SubElement(d, 'p', {'class': 'info'})
+
+        p = newp()
+        if m.license:
+            p.text = """
+                      Ten utwór jest udostępniony na licencji
+                      """
+            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 = newp()
+        p.text = 'Źródło: '
+        etree.SubElement(
+            p, 'a', href=str(m.url),
+            title=', '.join((
+                ', '.join(p.readable() for p in m.authors),
+                m.title
+            ))
+        ).text = str(m.url)
+
+        newp().text = 'Tekst opracowany na podstawie: ' + m.source_name
+
+        newp().text = """
+              Wydawca:
+              """ + ", ".join(p for p in m.publisher)
+
+        if m.description:
+            newp().text = m.description
+
+
+        if m.editors:
+            newp().text = 'Opracowanie redakcyjne i przypisy: %s.' % (
+                ', '.join(e.readable() for e in sorted(self.document.editors())))
+
+        if m.funders:
+            etree.SubElement(d, 'p', {'class': 'minor-info'}).text = '''Publikację wsparli i wsparły:
+            %s.''' % (', '.join(m.funders))
+
+        if m.cover_by:
+            p = newp()
+            p.text = 'Okładka na podstawie: '
+            if m.cover_source:
+                etree.SubElement(
+                    p,
+                    'a',
+                    href=m.cover_source
+                ).text = m.cover_by
+            else:
+                p.text += m.cover_by
+            
+        if m.isbn_epub:
+            newp().text = m.isbn_epub
+
+        newp().text = '\u00a0'
+
+        p = newp()
+        p.attrib['class'] = 'minor-info'
+        p.text = '''
+              Plik wygenerowany dnia '''
+        span = etree.SubElement(p, 'span', id='file_date')
+        span.text = str(date.today())
+        span.tail = '''.
+          '''
+        
+        self.add_html(
+            html.element,
+            file_name='last.xhtml',
+            toc='Strona redakcyjna',
+            spine=True
+        )
+
+
+    def add_annotations(self):
+        if not len(self.footnotes):
+            return
+
+        html = Xhtml()
+        html.title.text = 'Przypisy'
+        d = etree.SubElement(
+            etree.SubElement(
+                html.body,
+                'div',
+                id='book-text'
+            ),
+            'div',
+            id='footnotes'
+        )
+        
+        etree.SubElement(
+            d,
+            'h2',
+        ).text = 'Przypisy:'
+
+        d.extend(self.footnotes)
+        
+        self.add_html(
+            html.element,
+            file_name='annotations.xhtml',
+            spine=True,
+            toc='Przypisy'
+        )
+
+    def add_cover(self):
+        # TODO: allow other covers
+
+        cover_maker = make_cover
+
+        cover_file = six.BytesIO()
+        cover = cover_maker(self.document.meta)
+        cover.save(cover_file)
+        cover_name = 'cover.%s' % cover.ext()
+
+        self.output.set_cover(
+            file_name=cover_name,
+            content=cover_file.getvalue(),
+            create_page = False
+        )
+        ci = ('''<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="en" xml:lang="en">
+ <head>
+  <title>Okładka</title>
+  <style>
+    body { margin: 0em; padding: 0em; }
+    img { width: 100%%; }
+  </style>
+ </head>
+ <body>
+   <img src="cover.%s" alt="Okładka" />
+ </body>
+</html>''' % cover.ext()).encode('utf-8')
+        self.add_file(file_name='cover.xhtml', content=ci)
+
+        self.output.spine.append('cover')
+        self.output.guide.append({
+            'type': 'cover',
+            'href': 'cover.xhtml',
+            'title': 'Okładka'
+        })
+
+    def mathml(self, element):
+        name = "math%d.png" % self.assign_mathml_number()
+        self.add_file(
+            content=MathML(element).to_latex().to_png().data,
+            media_type='image/png',
+            file_name=name
+        )
+        return name
diff --git a/src/librarian/builders/pdf.py b/src/librarian/builders/pdf.py
new file mode 100644 (file)
index 0000000..6b2501e
--- /dev/null
@@ -0,0 +1,12 @@
+from librarian import OutputFile
+
+
+class PdfBuilder:
+    # Obowiązkowe
+    file_extension = 'pdf'
+    def build(self, document, mp3):
+        # stub
+        return OutputFile.from_bytes(b'')
+
+
+
index 3f19346..8dba4ae 100644 (file)
@@ -50,20 +50,40 @@ class TxtBuilder:
 
     default_license_description = {
         "pol": (
-            "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 "
-            "Creative Commons Uznanie Autorstwa – Na Tych Samych "
-            "Warunkach 3.0 PL "
-            "(http://creativecommons.org/licenses/by-sa/3.0/)"
+            "Wszystkie zasoby Wolnych Lektur możesz swobodnie wykorzystywać, "
+            "publikować i rozpowszechniać pod warunkiem zachowania warunków "
+            "licencji i zgodnie z Zasadami wykorzystania Wolnych Lektur.\n"
+            "Ten utwór jest w domenie publicznej. "
+            "Wszystkie materiały dodatkowe (przypisy, motywy literackie) są "
+            "udostępnione na Licencji Wolnej Sztuki 1.3 "
+            "(https://artlibre.org/licence/lal/pl/).\n"
+            "Fundacja Nowoczesna Polska 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 Zasadach wykorzystania Wolnych Lektur "
+            "(https://wolnelektury.pl/info/zasady-wykorzystania/). Zapoznaj "
+            "się z nimi, zanim udostępnisz dalej nasze książki."
         )
     }
     license_description = {
-        "pol": "Ten utwór jest udostępniony na licencji {meta.license_description}: \n{meta.license}",
+        "pol": (
+            #"Ten utwór jest udostępniony na licencji {meta.license_description}: \n{meta.license}",
+            "Wszystkie zasoby Wolnych Lektur możesz swobodnie wykorzystywać, "
+            "publikować i rozpowszechniać pod warunkiem zachowania warunków "
+            "licencji i zgodnie z Zasadami wykorzystania Wolnych Lektur.\n"
+            "Ten utwór jest jest udostępniony na licencji {meta.license_description} ({meta.license}). "
+            "Wszystkie materiały dodatkowe (przypisy, motywy literackie) są "
+            "udostępnione na Licencji Wolnej Sztuki 1.3 "
+            "(https://artlibre.org/licence/lal/pl/).\n"
+            "Fundacja Nowoczesna Polska 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 Zasadach wykorzystania Wolnych Lektur "
+            "(https://wolnelektury.pl/info/zasady-wykorzystania/). Zapoznaj "
+            "się z nimi, zanim udostępnisz dalej nasze książki."
+        )
     }
 
     def __init__(self):
index 1873be9..7d7964d 100644 (file)
@@ -363,10 +363,10 @@ class WLCover(Cover):
         box_img = box.image()
 
         # Find box position.
-        if self.box_position == 'top':
-            box_top = metr.box_top_margin
-        elif self.box_position == 'bottom':
+        if self.box_position == 'bottom' or box_img.size[1] + metr.box_top_margin + metr.box_bottom_margin > metr.height:
             box_top = metr.height - metr.box_bottom_margin - box_img.size[1]
+        elif self.box_position == 'top':
+            box_top = metr.box_top_margin
         else:   # Middle.
             box_top = (metr.height - box_img.size[1]) // 2
 
@@ -470,6 +470,7 @@ class WLNoBoxCover(WLCover):
 class LogoWLCover(WLCover):
     gradient_height = 90
     gradient_logo_height = 60
+    gradient_logo_max_width = 1000
     gradient_logo_margin_right = 30
     gradient_logo_spacing = 40
     gradient_color = '#000'
@@ -478,6 +479,8 @@ class LogoWLCover(WLCover):
         'res/wl-logo-white.png',
         'res/fnp-logo-white.png',
     ]
+    annotation = None
+    annotation_height = 10
 
     def __init__(self, book_info, *args, **kwargs):
         super(LogoWLCover, self).__init__(book_info, *args, **kwargs)
@@ -537,7 +540,10 @@ class LogoWLCover(WLCover):
             - 2 * metr.gradient_logo_margin_right
         )
         widths = [
-            logo.size[0] * metr.gradient_logo_height / logo.size[1]
+            min(
+                metr.gradient_logo_max_width,
+                logo.size[0] * metr.gradient_logo_height / logo.size[1]
+            )
             for logo in logos]
         taken_space = (
             sum(widths)
@@ -553,13 +559,36 @@ class LogoWLCover(WLCover):
             logo = logo.resize(
                 (
                     int(round(widths[i] * logo_scale)),
-                    int(round(metr.gradient_logo_height * logo_scale))
+                    int(round(
+                        logo.size[1] * widths[i] / logo.size[0] * logo_scale
+                    ))
                 ),
                 Image.ANTIALIAS)
             cursor -= logo.size[0]
-            img.paste(logo, (cursor, logo_top), mask=logo)
+            img.paste(
+                logo,
+                (
+                    cursor,
+                    int(round(logo_top + (metr.gradient_logo_height - logo.size[1]) * logo_scale / 2))
+                ),
+                mask=logo
+            )
             cursor -= int(round(metr.gradient_logo_spacing * logo_scale))
 
+        if self.annotation:
+            img2 = Image.new('RGBA', (metr.height, metr.height), color=None)
+            draw = ImageDraw.Draw(img2)
+            author_font = ImageFont.truetype(
+                self.author_font_ttf, metr.annotation_height,
+                layout_engine=ImageFont.LAYOUT_BASIC)
+            draw.text((self.annotation_height, self.annotation_height), self.annotation, font=author_font, fill='#FFFFFF')
+            img2.show()
+            img2 = img2.rotate(90)
+            img2.show()
+            img.putalpha(0)
+            img.alpha_composite(img2, (0, 0))
+            img = img.convert('RGB')
+
         return img
 
 
@@ -676,11 +705,22 @@ class AtriumCover(LogoWLCover):
     ]
 
 
+class BNCover(LogoWLCover):
+    gradient_logos = [
+        'res/dofinansowano.png',
+        'res/MKIDN.jpg',
+        'res/BN.png',
+        'res/wl-logo-white.png',
+    ]
+#    annotation = 'Zadanie „Udostępnienie publikacji w formatach cyfrowych” w ramach Narodowego Programu Rozwoju Czytelnictwa. Dofinansowano ze środków Ministra Kultury, Dziedzictwa Narodowego i Sportu.'
+
+
 COVER_CLASSES = {
     'default': LogoWLCover,
     'kmlu': KMLUCover,
     'mpw': MPWCover,
     'atrium': AtriumCover,
+    'bn': BNCover,
 }
 
 
index 6e94ff2..0dbb14f 100644 (file)
@@ -4,12 +4,12 @@ import re
 from lxml import etree
 import six
 from .parser import parser
-from . import dcparser, DCNS
+from . import dcparser, DCNS, DirDocProvider
 from .functions import lang_code_3to2
 
 
 class WLDocument:
-    def __init__(self, filename=None, url=None):
+    def __init__(self, filename=None, url=None, provider=None):
         source = filename or six.moves.urllib.request.urlopen(url)
         tree = etree.parse(source, parser=parser)
         self.tree = tree
@@ -18,6 +18,8 @@ class WLDocument:
             DCNS('language'): ["pol"],
         }, validate_required=False)
 
+        self.provider = provider if provider is not None else DirDocProvider('.')
+
     @property
     def meta(self):
         # Allow metadata of the master element as document meta.
@@ -25,6 +27,15 @@ class WLDocument:
         return self.tree.getroot().meta
         return master.meta
 
+    @property
+    def children(self):
+        for part_uri in self.meta.parts or []:
+            yield type(self)(
+                filename=self.provider.by_uri(part_uri),
+                provider=self.provider
+            )
+            
+    
     def build(self, builder, **kwargs):
         return builder().build(self, **kwargs)
 
@@ -66,3 +77,13 @@ class WLDocument:
                 _compat_assigns_section_ids_in_elem(child, idfier + '-')
         _compat_assigns_section_ids_in_elem(self.tree.getroot().master)
 
+
+    def editors(self):
+        persons = set(self.meta.editors
+                      + self.meta.technical_editors)
+        #for child in self.parts():
+        #    persons.update(child.editors())
+        #if None in persons:
+        #    persons.remove(None)
+        return persons
+
index f3a8521..c3a55fb 100644 (file)
@@ -82,7 +82,7 @@ WL_ELEMENTS = {
 
     "naglowek_czesc": headers.NaglowekCzesc,
     "naglowek_akt": headers.NaglowekCzesc,
-    "naglowek_scena": headers.NaglowekRozdzial,
+    "naglowek_scena": headers.NaglowekScena,
     "naglowek_rozdzial": headers.NaglowekRozdzial,
     "naglowek_podrozdzial": headers.NaglowekPodrozdzial,
     "srodtytul": headers.NaglowekCzesc,
index b9df185..5317268 100644 (file)
@@ -3,10 +3,13 @@
 import re
 from lxml import etree
 from librarian import dcparser, RDFNS
+from librarian.html import raw_printable_text
 from librarian.util import get_translation
 
 
 class WLElement(etree.ElementBase):
+    SECTION_PRECEDENCE = None
+
     TXT_TOP_MARGIN = 0
     TXT_BOTTOM_MARGIN = 0
     TXT_PREFIX = ""
@@ -15,17 +18,24 @@ class WLElement(etree.ElementBase):
     HTML_TAG = None
     HTML_ATTR = {}
     HTML_CLASS = None
-    
+
+    EPUB_TAG = None
+    EPUB_ATTR = {}
+    EPUB_CLASS = None
+    EPUB_START_CHUNK = False
+   
     CAN_HAVE_TEXT = True
     STRIP = False
 
     text_substitutions = [
         (u'---', u'—'),
         (u'--', u'–'),
-        (u'...', u'…'),
+        #(u'...', u'…'),  # Temporary turnoff for epub
         (u',,', u'„'),
         (u'"', u'”'),
         ('\ufeff', ''),
+
+        ("'", "\u2019"),    # This was enabled for epub.
     ]
 
     @property
@@ -52,11 +62,25 @@ class WLElement(etree.ElementBase):
     def gettext(self):
         return get_translation(self.meta.language).gettext
 
+    def raw_printable_text(self):
+        # TODO: podtagi, wyroznienia, etc
+        t = ''
+        t += self.normalize_text(self.text)
+        for c in self:
+            if c.tag not in ('pe', 'pa', 'pt', 'pr', 'motyw'):
+                t += c.raw_printable_text()
+            t += self.normalize_text(c.tail)
+        return t
+    
     def normalize_text(self, text):
         text = text or ''
         for e, s in self.text_substitutions:
             text = text.replace(e, s)
-        text = re.sub(r'\s+', ' ', text)
+            # FIXME: TEmporary turnoff
+#        text = re.sub(r'\s+', ' ', text)
+### TODO: Added now for epub
+        text = re.sub(r'(?<=\s\w)\s+', u'\u00A0', text)
+
         return text
 
     def _build_inner(self, builder, build_method):
@@ -118,6 +142,48 @@ class WLElement(etree.ElementBase):
         if self.HTML_TAG:
             builder.end_element()
 
+    def _epub_build_inner(self, builder):
+        self._build_inner(builder, 'epub_build')
+
+    def get_epub_attr(self, builder):
+        attr = self.EPUB_ATTR.copy()
+        if self.EPUB_CLASS:
+            attr['class'] = self.EPUB_CLASS
+        return attr
+
+    def epub_build(self, builder):
+        # TEMPORARY
+        self.CAN_HAVE_TEXT = True
+        self.STRIP = False
+        
+        if self.EPUB_START_CHUNK:
+            builder.start_chunk()
+
+        fragment = None
+        if self.SECTION_PRECEDENCE:
+            if not self.EPUB_START_CHUNK:
+                fragment = 'sub%d' % builder.assign_section_number()
+                self.attrib['id'] = fragment
+
+            builder.add_toc_entry(
+                fragment,
+                self.raw_printable_text(),
+                self.SECTION_PRECEDENCE
+            )
+            
+        if self.EPUB_TAG:
+            attr = self.get_epub_attr(builder)
+            if fragment:
+                attr['id'] = fragment
+            builder.start_element(
+                self.EPUB_TAG,
+                attr
+            )
+
+        self._epub_build_inner(builder)
+        if self.EPUB_TAG:
+            builder.end_element()
+            
     def sanitize(self):
         # TODO: Remove insanity here.
         for e in self:
index 5436271..7ac809d 100644 (file)
@@ -4,5 +4,5 @@ from ..base import WLElement
 class Dedykacja(WLElement):
     TXT_LEGACY_TOP_MARGIN = 2
 
-    HTML_TAG = "div"
-    HTML_CLASS = "dedication"
+    EPUB_TAG = HTML_TAG = "div"
+    EPUB_CLASS = HTML_CLASS = "dedication"
index c660583..22e98dd 100644 (file)
@@ -10,3 +10,6 @@ class DlugiCytat(WLElement):
     TXT_LEGACY_BOTTOM_MARGIN = 0
 
     HTML_TAG = 'blockquote'
+
+    EPUB_TAG = 'div'
+    EPUB_CLASS = 'block'
index a01bf29..0e3f61f 100644 (file)
@@ -4,5 +4,5 @@ from ..base import WLElement
 class Nota(WLElement):
     CAN_HAVE_TEXT = False
 
-    HTML_TAG = "div"
-    HTML_CLASS = "note"
+    EPUB_TAG = HTML_TAG = "div"
+    EPUB_CLASS = HTML_CLASS = "note"
index 0c103b1..14cd539 100644 (file)
@@ -10,3 +10,6 @@ class PoezjaCyt(WLElement):
     TXT_LEGACY_BOTTOM_MARGIN = 0
 
     HTML_TAG = 'blockquote'
+
+    EPUB_TAG = 'div'
+    EPUB_CLASS = 'block'
index d8dd5f0..b85f83f 100644 (file)
@@ -5,3 +5,5 @@ class Ramka(WLElement):
     HTML_TAG = "div"
     HTML_CLASS = "ramka"
 
+    EPUB_TAG = "div"
+    EPUB_CLASS = "frame"
index 9b43dc3..887712b 100644 (file)
@@ -7,3 +7,6 @@ class Abstrakt(WLElement):
 
     def html_build(self, builder):
         pass
+
+    def epub_build(self, builder):
+        pass
index faa5dd1..0ce970e 100644 (file)
@@ -9,3 +9,6 @@ class NotaRed(WLElement):
         builder.enter_fragment('nota_red')
         super(NotaRed, self).html_build(builder)
         builder.exit_fragment()
+
+    def epub_build(self, builder):
+        pass
index adf908b..1bdeb39 100644 (file)
@@ -7,3 +7,6 @@ class Uwaga(WLElement):
 
     def html_build(self, builder):
         pass
+
+    def epub_build(self, builder):
+        pass
index 7227c17..088e373 100644 (file)
@@ -5,5 +5,5 @@ class DidaskTekst(WLElement):
     TXT_PREFIX = "/ "
     TXT_SUFFIX = " /"
 
-    HTML_TAG = "em"
-    HTML_CLASS = "didaskalia"
+    EPUB_TAG = HTML_TAG = "em"
+    EPUB_CLASS = HTML_CLASS = "didaskalia"
index af0520f..bf81b69 100644 (file)
@@ -9,5 +9,5 @@ class Didaskalia(WLElement):
     TXT_PREFIX = "/ "
     TXT_SUFFIX = " /"
 
-    HTML_TAG = "div"
-    HTML_CLASS = "didaskalia"
+    EPUB_TAG =_HTML_TAG = "div"
+    EPUB_CLASS = HTML_CLASS = "didaskalia"
index 27dca30..56463d6 100644 (file)
@@ -4,5 +4,5 @@ from ..base import WLElement
 class Kwestia(WLElement):
     CAN_HAVE_TEXT = False
 
-    HTML_TAG = "div"
-    HTML_CLASS = "kwestia"
+    EPUB_TAG = HTML_TAG = "div"
+    EPUB_CLASS = HTML_CLASS = "kwestia"
index 5beca64..269b05c 100644 (file)
@@ -19,3 +19,10 @@ class ListaOsob(WLElement):
         super(ListaOsob, self)._html_build_inner(builder)
         builder.cursor.append(ol)
         builder.forget_fragment('list')
+
+    def _epub_build_inner(self, builder):
+        ol = etree.Element('ol')
+        builder.create_fragment('list', ol)
+        super(ListaOsob, self)._epub_build_inner(builder)
+        builder.cursor.append(ol)
+        builder.forget_fragment('list')
index fe55838..c5770ee 100644 (file)
@@ -8,10 +8,14 @@ class ListaOsoba(WLElement):
     TXT_LEGACY_BOTTOM_MARGIN = 0
     TXT_PREFIX = " * "
 
-    HTML_TAG = "li"
+    EPUB_TAG = HTML_TAG = "li"
 
     def html_build(self, builder):
         builder.enter_fragment('list')
         super(ListaOsoba, self).html_build(builder)
         builder.exit_fragment()
         
+    def epub_build(self, builder):
+        builder.enter_fragment('list')
+        super(ListaOsoba, self).epub_build(builder)
+        builder.exit_fragment()
index a4e9453..cf47ed2 100644 (file)
@@ -2,4 +2,7 @@ from ..paragraphs import Akap
 
 
 class MiejsceCzas(Akap):
-    HTML_CLASS = 'place-and-time'
+    EPUB_CLASS = HTML_CLASS = 'place-and-time'
+
+    EPUB_TAG = "div"
+    
index 4db9111..1f164a4 100644 (file)
@@ -3,3 +3,6 @@ from ..base import WLElement
 
 class NaglowekListy(WLElement):
     HTML_TAG = "h3"
+
+    EPUB_TAG = "div"
+    EPUB_CLASS = "h3"
index 5ab78fd..afa16ce 100644 (file)
@@ -8,3 +8,6 @@ class NaglowekOsoba(WLElement):
     TXT_LEGACY_BOTTOM_MARGIN = 0
 
     HTML_TAG = "h4"
+
+    EPUB_TAG = "h2"
+    EPUB_CLASS = "h4"
index b0fb793..ffd9494 100644 (file)
@@ -2,6 +2,6 @@ from ..base import WLElement
 
 
 class Osoba(WLElement):
-    HTML_TAG = "em"
-    HTML_CLASS = "person"
+    EPUB_TAG = HTML_TAG = "em"
+    EPUB_CLASS = HTML_CLASS = "person"
 
index 3c3026c..ab7c2b7 100644 (file)
@@ -1,12 +1,51 @@
+import six.moves
+from PIL import Image
 from ..base import WLElement
 
 
 class Ilustr(WLElement):
-    HTML_TAG = 'img'
+    EPUB_TAG = HTML_TAG = 'img'
 
     def get_html_attr(self, builder):
+        ## TODO: thumbnail.
+
+        url = six.moves.urllib.parse.urljoin(
+            builder.base_url,
+            self.get('src')
+        )
+        
+        imgfile = six.moves.urllib.request.urlopen(url)
+        img = Image.open(imgfile)
+        th_format, ext, media_type = {
+            'GIF': ('GIF', 'gif', 'image/gif'),
+            'PNG': ('PNG', 'png', 'image/png'),
+        }.get(img.format, ('JPEG', 'jpg', 'image/jpeg'))
+
+        width = 1200
+        if img.size[0] < width:
+            th = img
+        else:
+            th = img.resize((width, round(width * img.size[1] / img.size[0])))
+
+        imgfile.close()
+        buffer = six.BytesIO()
+        th.save(buffer, format=th_format)
+        ## TODO: Counter
+        file_name = 'image%d.%s' % (
+            builder.assign_image_number(),
+            ext
+        )
+
+        builder.add_file(
+            content=buffer.getvalue(),
+            file_name=file_name,
+            media_type=media_type,
+        )
+        
         return {
-            'src': builder.base_url + self.attrib['src'],
+            'src': file_name,
             'alt': self.attrib['alt'],
             'title': self.attrib['alt'],
         }
+
+    get_epub_attr = get_html_attr
index e0dae02..c94f20d 100644 (file)
@@ -2,4 +2,4 @@ from ..base import WLElement
 
 
 class Kol(WLElement):
-    HTML_TAG = 'td'
+    EPUB_TAG = HTML_TAG = 'td'
index af4a436..7da7877 100644 (file)
@@ -2,7 +2,7 @@ from ..base import WLElement
 
 
 class Tabela(WLElement):
-    HTML_TAG = 'table'
+    EPUB_TAG = HTML_TAG = 'table'
 
     def get_html_attr(self, builder):
         if self.attrib.get('ramka', '') == '1':
@@ -10,3 +10,7 @@ class Tabela(WLElement):
                 'class': 'border'
             }
         return {}
+
+    get_epub_attr = get_html_attr
+
+                
index bc61f9d..2c7d91a 100644 (file)
@@ -2,4 +2,4 @@ from ..base import WLElement
 
 
 class Wiersz(WLElement):
-    HTML_TAG = 'tr'
+    EPUB_TAG = HTML_TAG = 'tr'
index 0f30747..433e881 100644 (file)
@@ -49,6 +49,49 @@ class Footnote(WLElement):
         builder.end_element()
         builder.exit_fragment()
 
+    def epub_build(self, builder):
+        fn_no = builder.assign_footnote_number()
+        part_number = getattr(
+            builder,
+            'chunk_counter',
+            1
+        )
+
+        builder.start_element(
+            'a',
+            {
+                'class': 'anchor',
+                'id': f'anchor-{fn_no}',
+                'href': f'annotations.xhtml#annotation-{fn_no}',
+            }
+        )
+        builder.start_element('sup', {})
+        builder.push_text(str(fn_no))
+        builder.end_element()
+        builder.end_element()
+
+        
+        builder.enter_fragment('footnotes')
+        builder.start_element('p', {
+            'id': f'annotation-{fn_no}',
+            'class': "annotation"
+        })
+        builder.start_element('a', {
+            'href': f"part{part_number}.xhtml#anchor-{fn_no}"
+        })
+        builder.push_text(str(fn_no))
+        builder.end_element()
+        builder.push_text('. ')
+
+        super().epub_build(builder)
+        builder.push_text(' [' + self.qualifier + ']')
+        builder.end_element()
+
+        builder.push_text('\n')
+
+        builder.exit_fragment()
+
+        
 
 class PA(Footnote):
     """Przypis autorski."""
index fd6b2e8..736a24c 100644 (file)
@@ -6,3 +6,6 @@ class AutorUtworu(HeaderElement):
     TXT_LEGACY_BOTTOM_MARGIN = 2
 
     HTML_CLASS = 'author'
+
+    def epub_build(self, builder):
+        return
index a034ae7..5f114bc 100644 (file)
@@ -6,3 +6,6 @@ class DzieloNadrzedne(HeaderElement):
     TXT_LEGACY_BOTTOM_MARGIN = 1
 
     HTML_CLASS = "collection"
+
+    def epub_build(self, builder):
+        return
index 98c7334..7f23ea6 100644 (file)
@@ -5,5 +5,5 @@ class Motto(WLElement):
     TXT_LEGACY_TOP_MARGIN = 4
     TXT_LEGACY_BOTTOM_MARGIN = 2
 
-    HTML_TAG = "div"
-    HTML_CLASS = "motto"
+    EPUB_TAG = HTML_TAG = "div"
+    EPUB_CLASS = HTML_CLASS = "motto"
index 58fc9db..8fee127 100644 (file)
@@ -3,5 +3,7 @@ from ..base import WLElement
 
 class MottoPodpis(WLElement):
     HTML_TAG = "p"
-    HTML_CLASS = "motto_podpis"
+    EPUB_CLASS = HTML_CLASS = "motto_podpis"
 
+    EPUB_TAG = "div"
+    
index aa68082..4d06b47 100644 (file)
@@ -6,3 +6,6 @@ class NazwaUtworu(HeaderElement):
     TXT_LEGACY_BOTTOM_MARGIN = 1
 
     HTML_CLASS = 'title'
+
+    EPUB_TAG = 'h2'
+    EPUB_CLASS = 'intitle'
index 4431bc2..71e28a8 100644 (file)
@@ -6,3 +6,7 @@ class Podtytul(HeaderElement):
     TXT_LEGACY_BOTTOM_MARGIN = 1
 
     HTML_CLASS = 'subtitle'
+
+    EPUB_TAG = 'h2'
+    EPUB_CLASS = 'insubtitle'
+
index 3389eec..f3bda2b 100644 (file)
@@ -1,6 +1,9 @@
 from .naglowek_czesc import NaglowekCzesc
 from .naglowek_podrozdzial import NaglowekPodrozdzial
 from .naglowek_rozdzial import NaglowekRozdzial
+from .naglowek_scena import NaglowekScena
 from .podtytul_czesc import PodtytulCzesc
 from .podtytul_rozdzial import PodtytulRozdzial
 from .podtytul_podrozdzial import PodtytulPodrozdzial
+
+
index c7b2d9e..829e4f4 100644 (file)
@@ -2,9 +2,14 @@ from ..base import WLElement
 
 
 class NaglowekCzesc(WLElement):
+    SECTION_PRECEDENCE = 1
+    
     TXT_TOP_MARGIN = 5
     TXT_BOTTOM_MARGIN = 2
     TXT_LEGACY_TOP_MARGIN = 5
     TXT_LEGACY_BOTTOM_MARGIN = 0
 
-    HTML_TAG = "h2"
+    EPUB_TAG = HTML_TAG = "h2"
+
+    EPUB_CLASS = "h2"
+    EPUB_START_CHUNK = True
index ded615f..33ff355 100644 (file)
@@ -2,9 +2,15 @@ from ..base import WLElement
 
 
 class NaglowekRozdzial(WLElement):
+    SECTION_PRECEDENCE = 2
+    
     TXT_TOP_MARGIN = 4
     TXT_BOTTOM_MARGIN = 2
     TXT_LEGACY_TOP_MARGIN = 4
     TXT_LEGACY_BOTTOM_MARGIN = 0
 
     HTML_TAG = 'h3'
+
+    EPUB_TAG = 'h2'
+    EPUB_CLASS = 'h3'
+    EPUB_START_CHUNK = True
diff --git a/src/librarian/elements/headers/naglowek_scena.py b/src/librarian/elements/headers/naglowek_scena.py
new file mode 100644 (file)
index 0000000..8a52ca2
--- /dev/null
@@ -0,0 +1,17 @@
+from ..base import WLElement
+
+
+class NaglowekScena(WLElement):
+    SECTION_PRECEDENCE = 2
+
+    TXT_TOP_MARGIN = 4
+    TXT_BOTTOM_MARGIN = 2
+    TXT_LEGACY_TOP_MARGIN = 4
+    TXT_LEGACY_BOTTOM_MARGIN = 0
+
+    HTML_TAG = 'h3'
+
+    EPUB_TAG = 'h2'
+    EPUB_CLASS = 'h3'
+    EPUB_START_CHUNK = False
+
index 9825211..df8fd5c 100644 (file)
@@ -7,3 +7,11 @@ class PodtytulCzesc(WLElement):
 
     HTML_TAG = "div"
     HTML_CLASS = "subtitle2"
+
+    EPUB_TAG = "h2"
+    EPUB_CLASS = "h2"
+
+    def _epub_build_inner(self, builder):
+        builder.start_element('small', {})
+        super()._epub_build_inner(builder)
+        builder.end_element()
index 74aef13..cc00207 100644 (file)
@@ -7,3 +7,11 @@ class PodtytulPodrozdzial(WLElement):
 
     HTML_TAG = "div"
     HTML_CLASS = "subtitle4"
+
+    EPUB_TAG = "h2"
+    EPUB_CLASS = "h4"
+
+    def _epub_build_inner(self, builder):
+        builder.start_element('small', {})
+        super()._epub_build_inner(builder)
+        builder.end_element()
index 675e19b..f8db548 100644 (file)
@@ -7,3 +7,11 @@ class PodtytulRozdzial(WLElement):
 
     HTML_TAG = "div"
     HTML_CLASS = "subtitle3"
+
+    EPUB_TAG = "h2"
+    EPUB_CLASS = "h3"
+
+    def _epub_build_inner(self, builder):
+        builder.start_element('small', {})
+        super()._epub_build_inner(builder)
+        builder.end_element()
index 0a76c52..b0c0329 100644 (file)
@@ -9,5 +9,5 @@ class Akap(WLElement):
     TXT_LEGACY_TOP_MARGIN = 2
     TXT_LEGACY_BOTTOM_MARGIN = 0
 
-    HTML_TAG = 'p'
-    HTML_CLASS = 'paragraph'
+    EPUB_TAG = HTML_TAG = 'p'
+    EPUB_CLASS = HTML_CLASS = 'paragraph'
index 7df549f..a843d20 100644 (file)
@@ -1,4 +1,5 @@
 from copy import copy
+import re
 from ..base import WLElement
 from .wers import Wers
 
@@ -9,8 +10,19 @@ class Strofa(WLElement):
     TXT_LEGACY_TOP_MARGIN = 1
     TXT_LEGACY_BOTTOM_MARGIN = 0
 
-    HTML_TAG = 'div'
-    HTML_CLASS = 'stanza'
+    EPUB_TAG = HTML_TAG = 'div'
+    EPUB_CLASS = HTML_CLASS = 'stanza'
+
+    def epub_build(self, builder):
+        super().epub_build(builder)
+        builder.start_element(
+            'div',
+            {
+                'class': 'stanza-spacer'
+            }
+        )
+        builder.push_text('\u00a0');
+        builder.end_element()
     
     def get_verses(self):
         from librarian.parser import parser
@@ -20,7 +32,7 @@ class Strofa(WLElement):
         ]
         if self.text:
             # Before any tags. These are text-only verses.
-            pieces = self.text.split('/')
+            pieces = re.split(r"/\s+", self.text)
             for piece in pieces[:-1]:
                 verses[-1].text = piece
                 verses.append(parser.makeelement('wers'))
@@ -28,7 +40,7 @@ class Strofa(WLElement):
 
         for child in self:
             if child.tail:
-                pieces = child.tail.split('/')
+                pieces = re.split(r"/\s+", child.tail)
                 child_copy = copy(child)
                 child_copy.tail = pieces[0]
                 verses[-1].append(child_copy)
index 5c28058..55cf537 100644 (file)
@@ -9,11 +9,16 @@ class Wers(WLElement):
     TXT_LEGACY_TOP_MARGIN = 1
     TXT_LEGACY_BOTTOM_MARGIN = 0
 
-    HTML_TAG = 'div'
-    HTML_CLASS = 'verse'
+    EPUB_TAG = HTML_TAG = 'div'
+    EPUB_CLASS = HTML_CLASS = 'verse'
 
     @property
     def meta(self):
         if hasattr(self, 'stanza'):
             return self.stanza.meta
         return super(Wers, self).meta
+
+    def _epub_build_inner(self, builder):
+        super()._epub_build_inner(builder)
+        builder.push_text('''\u00a0''')
+
index 03b8187..c0d70f4 100644 (file)
@@ -7,3 +7,7 @@ class WersAkap(Wers):
     HTML_ATTR = {
         "style": "padding-left: 1em"
     }
+
+    EPUB_ATTR = {
+        "style": "margin-left: 1em"
+    }
index a61d5bc..df1f563 100644 (file)
@@ -8,3 +8,7 @@ class WersCd(Wers):
     HTML_ATTR = {
         "style": "padding-left: 12em",
     }
+
+    EPUB_ATTR = {
+        "style": "margin-left: 12em",
+    }
index 9ab5ff0..81af0d6 100644 (file)
@@ -4,6 +4,6 @@ from .wers import Wers
 class WersDoPrawej(Wers):
     TXT_PREFIX = '                       '
 
-    HTML_ATTR = {
+    EPUB_ATTR = HTML_ATTR = {
         "style": "text-align: right",
     }
index 8ac2bb3..662e57a 100644 (file)
@@ -18,3 +18,8 @@ class WersWciety(Wers):
         attr = super(WersWciety, self).get_html_attr(builder)
         attr['style'] = "padding-left: {}em".format(self.typ)
         return attr
+
+    def get_epub_attr(self, builder):
+        attr = super(WersWciety, self).get_html_attr(builder)
+        attr['style'] = "margin-left: {}em".format(self.typ)
+        return attr
index e68430d..46be85b 100644 (file)
@@ -7,8 +7,8 @@ class SekcjaAsterysk(WLElement):
     TXT_LEGACY_TOP_MARGIN = 2
     TXT_LEGACY_BOTTOM_MARGIN = 2
 
-    HTML_TAG = "p"
-    HTML_CLASS = "spacer-asterisk"
+    EPUB_TAG = HTML_TAG = "p"
+    HTML_CLASS = HTML_CLASS = "spacer-asterisk"
 
     def _txt_build_inner(self, builder):
         builder.push_text('*')
@@ -16,3 +16,5 @@ class SekcjaAsterysk(WLElement):
     def _html_build_inner(self, builder):
         builder.push_text("*")
 
+    _epub_build_inner = _html_build_inner
+
index 7d950da..28ba6e5 100644 (file)
@@ -7,3 +7,9 @@ class SekcjaSwiatlo(WLElement):
 
     HTML_TAG = "hr"
     HTML_CLASS = "spacer"
+
+    EPUB_TAG = 'p'
+    EPUB_CLASS = 'spacer'
+
+    def _epub_build_inner(self, builder):
+        builder.push_text("\u00a0")
index 5249691..49ea17f 100644 (file)
@@ -7,8 +7,8 @@ class SeparatorLinia(WLElement):
     TXT_LEGACY_TOP_MARGIN = 2
     TXT_LEGACY_BOTTOM_MARGIN = 2
 
-    HTML_TAG = "hr"
-    HTML_CLASS = "spacer-line"
+    EPUB_TAG = HTML_TAG = "hr"
+    EPUB_CLASS = HTML_CLASS = "spacer-line"
     
     def _txt_build_inner(self, builder):
         builder.push_text('-' * 48)
index 5d19a44..bac93c0 100644 (file)
@@ -4,4 +4,4 @@ from ..base import WLElement
 class IndeksDolny(WLElement):
     TXT_PREFIX = "_"
 
-    HTML_TAG = "sub"
+    EPUB_TAG = HTML_TAG = "sub"
index f284695..fa353f9 100644 (file)
@@ -8,3 +8,7 @@ class Mat(WLElement):
         e.tag = 'math'
         e.attrib['xmlns'] = 'http://www.w3.org/1998/Math/MathML'
         builder.cursor.append(e)
+
+    def epub_build(self, builder):
+        builder.start_element('img', {"src": builder.mathml(self)})
+        builder.end_element()
index 3592a1e..5848aa6 100644 (file)
@@ -2,5 +2,5 @@ from ..base import WLElement
 
 
 class SlowoObce(WLElement):
-    HTML_TAG = 'em'
-    HTML_CLASS = 'foreign-word'
+    EPUB_TAG = HTML_TAG = 'em'
+    EPUB_CLASS = HTML_CLASS = 'foreign-word'
index ef3618c..906d98c 100644 (file)
@@ -3,8 +3,8 @@ from ..base import WLElement
 
 
 class TytulDziela(WLElement):
-    HTML_TAG = 'em'
-    HTML_CLASS = 'book-title'
+    EPUB_TAG = HTML_TAG = 'em'
+    EPUB_CLASS = HTML_CLASS = 'book-title'
 
     def normalize_text(self, text):
         txt = super(TytulDziela, self).normalize_text(text)
index 3229402..953e797 100644 (file)
@@ -5,5 +5,5 @@ class WiekszeOdstepy(WLElement):
     TXT_PREFIX = "*"
     TXT_SUFFIX = "*"
 
-    HTML_TAG = "em"
-    HTML_CLASS = "wieksze-odstepy"
+    EPUB_TAG = HTML_TAG = "em"
+    EPUB_CLASS = HTML_CLASS = "wieksze-odstepy"
index c76b4cf..2a71a29 100644 (file)
@@ -5,5 +5,5 @@ class Wyroznienie(WLElement):
     TXT_PREFIX = "*"
     TXT_SUFFIX = "*"
 
-    HTML_TAG = "em"
-    HTML_CLASS = "author-emphasis"
+    EPUB_TAG = HTML_TAG = "em"
+    EPUB_CLASS = HTML_CLASS = "author-emphasis"
index f9ab197..25369a7 100644 (file)
@@ -18,3 +18,6 @@ class Motyw(WLElement):
             "fid": fid,
             "name": "m" + fid,
         }
+
+    def epub_build(self, builder):
+        pass
index e2cdae7..0fb91e5 100644 (file)
@@ -30,6 +30,7 @@ functions.reg_person_name()
 
 
 def squeeze_whitespace(s):
+    return s
     return re.sub(b'\\s+', b' ', s)
 
 
@@ -62,33 +63,6 @@ def hyphenate_and_fix_conjunctions(source_tree, hyph):
             parent.tail = newt
 
 
-def inner_xml(node):
-    """ returns node's text and children as a string
-
-    >>> print(inner_xml(etree.fromstring('<a>x<b>y</b>z</a>')))
-    x<b>y</b>z
-    """
-
-    nt = node.text if node.text is not None else ''
-    return ''.join(
-        [nt] + [etree.tostring(child, encoding='unicode') for child in node]
-    )
-
-
-def set_inner_xml(node, text):
-    """ sets node's text and children from a string
-
-    >>> e = etree.fromstring('<a>b<b>x</b>x</a>')
-    >>> set_inner_xml(e, 'x<b>y</b>z')
-    >>> print(etree.tostring(e, encoding='unicode'))
-    <a>x<b>y</b>z</a>
-    """
-
-    p = etree.fromstring('<x>%s</x>' % text)
-    node.text = p.text
-    node[:] = p[:]
-
-
 def node_name(node):
     """ Find out a node's name
 
@@ -377,17 +351,8 @@ def remove_empty_lists_from_toc(toc):
                 toc[i] = e[0]
 
 
-def transform(wldoc, verbose=False, style=None,
-              sample=None, cover=None, flags=None, hyphenate=False,
-              base_url='file://./', output_type='epub'):
-    """ produces a EPUB file
-
-    sample=n: generate sample e-book (with at least n paragraphs)
-    cover: a cover.Cover factory or True for default
-    flags: less-advertising, without-fonts, working-copy
-    """
 
-    def transform_file(wldoc, chunk_counter=1, first=True, sample=None):
+def transform_file(wldoc, chunk_counter=1, first=True, sample=None, hyphenate=False, output_type='epub', spine=None, output=None, annotations=None):
         """ processes one input file and proceeds to its children """
 
         replace_characters(wldoc.edoc.getroot())
@@ -518,12 +483,27 @@ def transform(wldoc, verbose=False, style=None,
 
         for child in wldoc.parts():
             child_toc, chunk_counter, chunk_chars, sample = transform_file(
-                child, chunk_counter, first=False, sample=sample)
+                child, chunk_counter, first=False, sample=sample,
+                hyphenate=hyphenate, output_type=output_type,
+                spine=spine, output=output, annotations=annotations,
+            )
             toc[-1][1].extend(child_toc)
             chars = chars.union(chunk_chars)
 
         return toc, chunk_counter, chars, sample
 
+                
+def transform(wldoc, verbose=False, style=None,
+              sample=None, cover=None, flags=None, hyphenate=False,
+              base_url='file://./', output_type='epub'):
+    """ produces a EPUB file
+
+    sample=n: generate sample e-book (with at least n paragraphs)
+    cover: a cover.Cover factory or True for default
+    flags: less-advertising, without-fonts, working-copy
+    """
+
+
     document = deepcopy(wldoc)
     del wldoc
 
@@ -584,8 +564,8 @@ def transform(wldoc, verbose=False, style=None,
             base_url,
             ilustr.get('src')
         )
-        with six.moves.urllib.request.urlopen(url) as imgfile:
-            img = Image.open(imgfile)
+        imgfile = six.moves.urllib.request.urlopen(url)
+        img = Image.open(imgfile)
 
         th_format, ext, media_type = {
             'GIF': ('GIF', 'gif', 'image/gif'),
@@ -598,6 +578,8 @@ def transform(wldoc, verbose=False, style=None,
         else:
             th = img.resize((width, round(width * img.size[1] / img.size[0])))
 
+        imgfile.close()
+            
         buffer = six.BytesIO()
         th.save(buffer, format=th_format)
 
@@ -677,7 +659,11 @@ def transform(wldoc, verbose=False, style=None,
 
     annotations = etree.Element('annotations')
 
-    toc, chunk_counter, chars, sample = transform_file(document, sample=sample)
+    toc, chunk_counter, chars, sample = transform_file(
+        document, sample=sample,
+        hyphenate=hyphenate, output_type=output_type,
+        spine=spine, output=output, annotations=annotations
+    )
     output.toc = toc[0][1]
 
     # Last modifications in container files and EPUB creation
@@ -786,6 +772,7 @@ def transform(wldoc, verbose=False, style=None,
             os.chdir(cwd)
 
     remove_empty_lists_from_toc(output.toc)
+    print(output.toc)
 
     output_file = NamedTemporaryFile(prefix='librarian', suffix='.epub',
                                      delete=False)
index 82ad4b9..a740f85 100644 (file)
@@ -387,4 +387,13 @@ table.border th, table.border td {
 
 th, td {
     vertical-align: top;
-}
\ No newline at end of file
+}
+
+.fundraising {
+       margin: 2em 1em 0;
+       border: 2px solid black;
+       padding: 2em 2em;
+       text-align: center;
+       font-size: 1.3em;
+       line-height: 1.4em;
+}
index cd22462..90f038e 100644 (file)
@@ -15,7 +15,7 @@
         <div id="book-text" xmlns="http://www.w3.org/1999/xhtml">
           <div id="footnotes" xmlns="http://www.w3.org/1999/xhtml">
             <h2 xmlns="http://www.w3.org/1999/xhtml">
-              Przypisy:
+              <xsl:text>Przypisy:</xsl:text>
             </h2>
             <xsl:apply-templates mode="przypis" />
           </div>
@@ -29,7 +29,7 @@
   </xsl:template>
 
   <xsl:template match="pa|pe|pr|pt" mode="przypis">
-    <p id="annotation-{@number}" class="annotation" xmlns="http://www.w3.org/1999/xhtml"><a href="part{@part}.xhtml#anchor-{@number}" xmlns="http://www.w3.org/1999/xhtml"><xsl:value-of select="@number" /></a>. <xsl:apply-templates /><xsl:if test="name()='pa'"> [przypis autorski]</xsl:if><xsl:if test="name()='pt'"> [przypis tłumacza]</xsl:if><xsl:if test="name()='pr'"> [przypis redakcyjny]</xsl:if><xsl:if test="name()='pe'"> [przypis edytorski]</xsl:if></p>
+    <p class="annotation" id="annotation-{@number}" xmlns="http://www.w3.org/1999/xhtml"><a href="part{@part}.xhtml#anchor-{@number}" xmlns="http://www.w3.org/1999/xhtml"><xsl:value-of select="@number" /></a>. <xsl:apply-templates /><xsl:if test="name()='pa'"> [przypis autorski]</xsl:if><xsl:if test="name()='pt'"> [przypis tłumacza]</xsl:if><xsl:if test="name()='pr'"> [przypis redakcyjny]</xsl:if><xsl:if test="name()='pe'"> [przypis edytorski]</xsl:if></p>
     <xsl:text>&#xa;</xsl:text>
   </xsl:template>
 
index 93767cf..0fae871 100644 (file)
@@ -7,9 +7,7 @@
       <xsl:element name="head">
         <link rel="stylesheet" href="style.css" type="text/css" />
         <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-        <title>
-          WolneLektury.pl
-        </title>
+        <title>WolneLektury.pl</title>
       </xsl:element>
       <xsl:element name="body" xmlns="http://www.w3.org/1999/xhtml">
         <xsl:element name="div" xmlns="http://www.w3.org/1999/xhtml">
 
   <xsl:template match="ilustr">
     <img>
-      <xsl:attribute name="src">
-        <xsl:value-of select="@src" />
-      </xsl:attribute>
       <xsl:attribute name="alt">
         <xsl:value-of select="@alt" />
       </xsl:attribute>
+      <xsl:attribute name="src">
+        <xsl:value-of select="@src" />
+      </xsl:attribute>
       <xsl:attribute name="title">
         <xsl:value-of select="@alt" />
       </xsl:attribute>
   <xsl:template match="extra" />
 
   <xsl:template match="pe|pa|pr|pt" >
-    <a class="anchor" id="anchor-{.}" href="annotations.xhtml#annotation-{.}"
+    <a class="anchor" href="annotations.xhtml#annotation-{.}" id="anchor-{.}"
        xmlns="http://www.w3.org/1999/xhtml"><sup xmlns="http://www.w3.org/1999/xhtml"><xsl:apply-templates /></sup></a>
   </xsl:template>
 
diff --git a/src/librarian/fonts.py b/src/librarian/fonts.py
new file mode 100644 (file)
index 0000000..adaeae4
--- /dev/null
@@ -0,0 +1,38 @@
+import os
+from shutil import rmtree
+import subprocess
+from tempfile import mkdtemp
+
+
+def strip_font(path, chars, verbose=False):
+    tmpdir = mkdtemp('-librarian-epub')
+    try:
+        cwd = os.getcwd()
+    except OSError:
+        cwd = None
+
+    os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)),
+                          'font-optimizer'))
+    optimizer_call = [
+        'perl', 'subset.pl', '--chars',
+        ''.join(chars).encode('utf-8'),
+        path,
+        os.path.join(tmpdir, 'font.ttf')
+    ]
+    env = {"PERL_USE_UNSAFE_INC": "1"}
+    if verbose:
+        print("Running font-optimizer")
+        subprocess.check_call(optimizer_call, env=env)
+    else:
+        dev_null = open(os.devnull, 'w')
+        subprocess.check_call(optimizer_call, stdout=dev_null,
+                              stderr=dev_null, env=env)
+    with open(os.path.join(tmpdir, 'font.ttf'), 'rb') as f:
+        content = f.read()
+
+    rmtree(tmpdir)
+
+    if cwd is not None:
+        os.chdir(cwd)
+
+    return content
diff --git a/src/librarian/fundraising.py b/src/librarian/fundraising.py
new file mode 100644 (file)
index 0000000..18d7774
--- /dev/null
@@ -0,0 +1,13 @@
+FUNDRAISING = [
+        'Przyjaciele Wolnych Lektur otrzymują dostęp do nowych tekstów współczesnych autorek i autorów wcześniej niż inni. <a href="https://wolnelektury.pl/towarzystwo/">Kliknij, by przejść do strony płatności.</a> Zadeklaruj stałą wpłatę i dołącz do Towarzystwa Przyjaciół Wolnych Lektur.',
+        'Czytaj teksty współczesnych autorek i autorów wcześniej niż inni. Ty decydujesz, ile płacisz! <a href="https://wolnelektury.pl/towarzystwo/">Zadeklaruj stałą wpłatę i dołącz do Towarzystwa Przyjaciół Wolnych Lektur</a>.',
+        'Informacje o nowościach w naszej bibliotece w Twojej skrzynce mailowej? Nic prostszego, zapisz się do newslettera.<br/><a href="https://wolnelektury.pl/newsletter/">Kliknij, by pozostawić swój adres e-mail</a>.',
+        '''Przekaż 1% podatku na Wolne Lektury.<br/>
+KRS: 0000070056<br/>
+Nazwa organizacji: Fundacja Nowoczesna Polska<br/>
+<br/>
+Możesz to zrobić w swoim formularzu PIT dostępnym od 15 lutego na stronie: <a href="https://www.podatki.gov.pl/pit">www.podatki.gov.pl/pit</a>.<br/>
+<br/>
+Każda wpłacona kwota zostanie przeznaczona na rozwój Wolnych Lektur.<br/>
+Dziękujemy, że jesteście z nami!''',
+]
index 363286c..f0f11db 100644 (file)
@@ -58,9 +58,8 @@ def add_image_sizes(tree, gallery_path, gallery_url, base_url):
         rel_path = ilustr.attrib['src']
         img_url = six.moves.urllib.parse.urljoin(base_url, rel_path)
 
-        with six.moves.urllib.request.urlopen(img_url) as f:
-            img = Image.open(f)
-
+        f = six.moves.urllib.request.urlopen(img_url)
+        img = Image.open(f)
         ext = {'GIF': 'gif', 'PNG': 'png'}.get(img.format, 'jpg')
 
         srcset = []
@@ -75,10 +74,12 @@ def add_image_sizes(tree, gallery_path, gallery_url, base_url):
         ]
         largest = None
         for w in widths:
-            height = round(img.size[1] * w / img.size[0])
-            th = img.resize((w, height))
             fname = '%d.W%d.%s' % (i, w, ext)
-            th.save(gallery_path + fname)
+            fpath = gallery_path + fname
+            if not os.path.exists(fpath):
+                height = round(img.size[1] * w / img.size[0])
+                th = img.resize((w, height))
+                th.save(fpath)
             th_url = gallery_url + fname
             srcset.append(" ".join((
                 th_url,
@@ -88,6 +89,8 @@ def add_image_sizes(tree, gallery_path, gallery_url, base_url):
         ilustr.attrib['srcset'] = ", ".join(srcset)
         ilustr.attrib['src'] = largest_url
 
+        f.close()
+
 
 def transform(wldoc, stylesheet='legacy', options=None, flags=None, css=None, gallery_path='img/', gallery_url='img/', base_url='file://./'):
     """Transforms the WL document to XHTML.
index cad66a4..a025b9b 100644 (file)
@@ -320,8 +320,8 @@ def transform(wldoc, verbose=False, save_tex=None, morefloats=None,
                 base_url,
                 ilustr.get('src')
             )
-            with six.moves.urllib.request.urlopen(url) as imgfile:
-                img = Image.open(imgfile)
+            imgfile = six.moves.urllib.request.urlopen(url)
+            img = Image.open(imgfile)
 
             th_format, ext, media_type = {
                 'GIF': ('GIF', 'gif', 'image/gif'),
@@ -338,6 +338,8 @@ def transform(wldoc, verbose=False, save_tex=None, morefloats=None,
             th.save(os.path.join(temp, file_name))
             ilustr.set('src', file_name)
 
+            imgfile.close()
+
         for sponsor in book_info.sponsors:
             ins = etree.Element("data-sponsor", name=sponsor)
             logo = sponsor_logo(sponsor)
index f8b7731..c46a0ec 100644 (file)
 \usepackage{unicode-math}
 \setmathfont{Latin Modern Math}
 
+\usepackage{wrapfig}
+
 \usepackage[overload]{textcase}
 \usepackage{scalefnt}
 \usepackage[colorlinks=true,linkcolor=black,setpagesize=false,urlcolor=black,xetex]{hyperref}
index 43a3274..820033d 100644 (file)
 </xsl:template>
 
 <xsl:template match="ilustr">
-    <cmd name="ilustr">
+  <xsl:choose>
+    <xsl:when test="@oblew = 'true'">
+      <cmd name="par"/>
+      <env name="wrapfigure">
+        <parm>R</parm>
+        <parm>5cm</parm>
+        <cmd name="includegraphics">
+          <opt>width=<cmd name="linewidth"/></opt>
+          <parm><xsl:value-of select="@src"/></parm>
+        </cmd>
+      </env>
+    </xsl:when>
+    <xsl:otherwise>
+      <cmd name="ilustr">
         <parm><xsl:value-of select="@src" /></parm>
         <parm><xsl:value-of select="@alt" /></parm>
-    </cmd>
+      </cmd>
+    </xsl:otherwise>
+  </xsl:choose>
 </xsl:template>
 
 <!-- ========================================== -->
diff --git a/src/librarian/res/BN.png b/src/librarian/res/BN.png
new file mode 100644 (file)
index 0000000..2e6df23
Binary files /dev/null and b/src/librarian/res/BN.png differ
diff --git a/src/librarian/res/MKIDN.jpg b/src/librarian/res/MKIDN.jpg
new file mode 100644 (file)
index 0000000..373062c
Binary files /dev/null and b/src/librarian/res/MKIDN.jpg differ
index 35af904..4555c55 100644 (file)
Binary files a/src/librarian/res/atrium-logo.png and b/src/librarian/res/atrium-logo.png differ
diff --git a/src/librarian/res/dofinansowano.png b/src/librarian/res/dofinansowano.png
new file mode 100644 (file)
index 0000000..80c531b
Binary files /dev/null and b/src/librarian/res/dofinansowano.png differ
index e425552..5b03525 100644 (file)
@@ -51,27 +51,39 @@ def transform(wldoc, flags=None, **options):
             description = parsed_dc.description
             url = document.book_info.url
 
-            license_description = parsed_dc.license_description
+            license_name = parsed_dc.license_description
             license = parsed_dc.license
+            license_description = [
+                (
+                    "Wszystkie zasoby Wolnych Lektur możesz swobodnie wykorzystywać, "
+                    "publikować i rozpowszechniać pod warunkiem zachowania warunków "
+                    "licencji i zgodnie z Zasadami wykorzystania Wolnych Lektur."
+                )
+            ]
+
             if license:
-                license_description = (
-                    u"Ten utwór jest udostępniony na licencji %s: \n%s" % (
-                        license_description, license
+                license_description.append(
+                    "Ten utwór jest udostępniony na licencji %s: %s" % (
+                        license_name, license
                     )
                 )
             else:
-                license_description = (
-                    "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 "
-                    "Creative Commons Uznanie Autorstwa – Na Tych Samych "
-                    "Warunkach 3.0 PL "
-                    "(http://creativecommons.org/licenses/by-sa/3.0/)"
+                license_description.append(
+                    "Ten utwór jest w domenie publicznej."
                 )
+            license_description.append(
+                "Wszystkie materiały dodatkowe (przypisy, motywy literackie) są "
+                "udostępnione na Licencji Wolnej Sztuki 1.3: "
+                "https://artlibre.org/licence/lal/pl/\n"
+                "Fundacja Nowoczesna Polska zastrzega sobie prawa do wydania "
+                "krytycznego zgodnie z art. Art.99(2) Ustawy o prawach autorskich "
+                "i prawach pokrewnych.\nWykorzystując zasoby z Wolnych Lektur, "
+                "należy pamiętać o zapisach licencji oraz zasadach, które "
+                "spisaliśmy w Zasadach wykorzystania Wolnych Lektur: "
+                "https://wolnelektury.pl/info/zasady-wykorzystania/\nZapoznaj "
+                "się z nimi, zanim udostępnisz dalej nasze książki"
+            )
+            license_description = "\n".join(license_description)
 
             source = parsed_dc.source_name
             if source:
index 04e2658..54f522e 100644 (file)
                                <xsl:value-of select="$css" />
                        </xsl:attribute>
                </link>
+               <style>
+                 .ilustr.prawo img {
+                 float: right;
+                 }
+                 .ilustr.lewo img {
+                 float: left;
+                 }
+                 .ilustr.srodek {
+                 text-align: center;
+                 }
+                 .ilustr .stop {
+                 clear: both;
+                 }
+                 .ilustr.oblew .stop {
+                 clear: none;
+                 }
+
+                  .ilustr.oblew img {
+                    margin-bottom: 1em;
+                  }
+                  .ilustr.oblew.lewo img {
+                    margin-right: 2em;
+                    }
+                    .ilustr.oblew.prawo img {
+                    margin-left: 2em;
+                    }
+
+
+                    .ilustr img {
+                    min-width: 200px !important;
+                    }
+                    @media screen and (max-width: 320px) {
+                    .ilustr img {
+                    width: 100% !important;
+                    min-width: auto !important;
+                    }
+                    .ilustr.lewo img {
+                    float: none;
+                    }
+                    .ilustr.prawo img {
+                    float:none;
+                    }
+                    .ilustr.oblew.prawo img {
+                    margin-left: 0;
+                    }
+                    .ilustr.oblew.lewo img{
+                    margin-right: 0;
+                    }
+                    .ilustr .stop {
+                   clear: none;
+                    }
+
+                    }
+               </style>
            </head>
             <body>
               <xsl:call-template name="book-text" />
 </xsl:template>
 
 <xsl:template match="ilustr">
+  <div>
+
+    <xsl:attribute name="class">
+      <xsl:text>ilustr </xsl:text>
+      <xsl:value-of select="@wyrownanie"/>
+      <xsl:if test="@oblew">
+        <xsl:text> oblew</xsl:text>
+      </xsl:if>
+    </xsl:attribute>
+    
     <img>
-        <xsl:attribute name="src">
-            <xsl:value-of select="@src" />
-        </xsl:attribute>
-        <xsl:attribute name="srcset">
-          <xsl:value-of select="@srcset" />
-        </xsl:attribute>
-        <xsl:attribute name="sizes">
-          (min-width: 718px) 600px,
-          (min-width: 600px) calc(100vw - 118px),
-          (min-width: 320px) calc(100vw - 75px),
-          (min-width: 15em) calc(100wv - 60px),
-          calc(100wv - 40px)
-        </xsl:attribute>
-        <xsl:attribute name="alt">
-            <xsl:value-of select="@alt" />
-        </xsl:attribute>
-        <xsl:attribute name="title">
-            <xsl:value-of select="@alt" />
+      <xsl:attribute name="src">
+        <xsl:value-of select="@src" />
+      </xsl:attribute>
+      <xsl:attribute name="srcset">
+        <xsl:value-of select="@srcset" />
+      </xsl:attribute>
+      <xsl:attribute name="sizes">
+        (min-width: 718px) 600px,
+        (min-width: 600px) calc(100vw - 118px),
+        (min-width: 320px) calc(100vw - 75px),
+        (min-width: 15em) calc(100wv - 60px),
+        calc(100wv - 40px)
+      </xsl:attribute>
+      <xsl:attribute name="alt">
+        <xsl:value-of select="@alt" />
+      </xsl:attribute>
+      <xsl:attribute name="title">
+        <xsl:value-of select="@alt" />
+      </xsl:attribute>
+
+      <xsl:if test="@szer">
+        <xsl:attribute name="style">
+          <xsl:text>width: </xsl:text>
+          <xsl:value-of select="@szer"/>
         </xsl:attribute>
+      </xsl:if>
     </img>
+
+    <div class="stop"></div>
+  </div>
 </xsl:template>
 
 <xsl:template match="animacja">
index 4b77300..ceae2e7 100644 (file)
@@ -6,28 +6,29 @@
       <dc:date>2020-01-08</dc:date>
       <dc:publisher>Fundacja Nowoczesna Polska</dc:publisher>
       <dc:language>pol</dc:language>
-      <dc:identifier.url>wl-test</dc:identifier.url>
+      <dc:identifier.url>main</dc:identifier.url>
       <dc:rights>gnu gpl</dc:rights>
     </rdf:Description>
   </rdf:RDF>
 
   <opowiadanie>
 
-<autor_utworu>Wolne Lektury</autor_utworu>
-<nazwa_utworu>Testy</nazwa_utworu>
-<podtytul>Podtytuł</podtytul>
-
+    <dzielo_nadrzedne>Dzieło nadrzędne<pa>Przypis w dziele nadrzędnym</pa></dzielo_nadrzedne>
+    <autor_utworu>Wolne<pa>Przypis w nazwie utworu</pa> Lektury</autor_utworu>
+    <nazwa_utworu>Testy<pa>Przypis w tytule utworu</pa></nazwa_utworu>
+    <podtytul>Podtytuł<pa>Przypis w podtytule</pa></podtytul>
+    
     <naglowek_czesc>Testy struktury</naglowek_czesc>
-
+    
     <naglowek_czesc>Nagłówek części</naglowek_czesc>
     <podtytul_czesc>Podtytuł części</podtytul_czesc>
-
+    
     <naglowek_rozdzial>Nagłówek rozdziału</naglowek_rozdzial>
     <podtytul_rozdzial>Podtytuł rozdziału</podtytul_rozdzial>
-
+    
     <naglowek_podrozdzial>Nagłówek podrozdziału</naglowek_podrozdzial>
     <podtytul_podrozdzial>Podtytuł podrozdziału</podtytul_podrozdzial>
-
+    
     <naglowek_akt>Nagłówek aktu</naglowek_akt>
     <podtytul_akt>Podtytuł aktu</podtytul_akt>
     
     <srodtytul>Śródtytuł</srodtytul>
 
     <akap>Nagłówki powinny tworzyć prawidłową strukturę. Użyte elementy: tytuły i podtytuły części, rozdziału, podrozdziału, aktu i sceny, oraz śródtytuł.</akap>
+
+
+
+    <naglowek_czesc>Akapity</naglowek_czesc>
+
+    <akap>Zwykły akapit.</akap>
+
+    <akap_cd>Kontynuacja akapitu.</akap_cd>
+
+    <akap_dialog>--- Akapit dialogowy.</akap_dialog>
+
+    <akap>Zastępnik wersu w akapicie:</akap>
+    <akap><zastepnik_wersu>. . . . . . . .</zastepnik_wersu></akap>
+
+    <akap>Poezja cytowana w akapicie dialogowym (?):</akap>
+    <akap_dialog><poezja_cyt><strofa>--- Cytat</strofa></poezja_cyt></akap_dialog>
+    
+
+
+
+
+
+    <naglowek_czesc>Dramat</naglowek_czesc>
+
+    <lista_osob>
+      <naglowek_listy>
+       Lista osób
+      </naglowek_listy>
+      <lista_osoba>
+       <osoba>Pustelnik</osoba> --- pustelnik
+      </lista_osoba>
+      <lista_osoba>
+       <osoba>Hesia</osoba> --- Hesia
+      </lista_osoba>
+      
+    </lista_osob>
+
+    <miejsce_czas>
+    </miejsce_czas>
+    
+    <naglowek_osoba>PUSTELNIK</naglowek_osoba>
+
+    <kwestia>
+      <strofa>
+       Więc jako dawniej czynili mocarze,/
+       Z Lechem się mieniał Scyta na obrączki;/
+       A pokochawszy mocniej sercem, w darze/
+       Dał mu koronę... stąd nasza korona./
+       Zbawiciel niegdyś wyciągając rączki/
+       Szedł do niej z matki zadumanej łona/
+       I ku rubinom podawał się cały/
+       Jako różyczka z liści wychylona,/
+       I wołał: caca! i na brylant biały/
+       Różanych ustek perełkami świecił.
+      </strofa>
+    </kwestia>
+
+    <didaskalia>Słychać głos Hesi.</didaskalia>
+    
+    <naglowek_osoba>GŁOS HESI</naglowek_osoba>
+
+    <kwestia>
+      <akap>
+       Mamuńciu, tak zimno! troszkę ciepłej wody...
+      </akap>
+    </kwestia>
+
+    <naglowek_osoba>DULSKA</naglowek_osoba>
+
+    <kwestia>
+      <akap>
+       Jeszcze czego? Hartujcie się... Felicjan! wstajesz? Wiesz? ten błazen, twój syn, nie wrócił jeszcze do domu! Co? nic nie mówisz? naturalnie. 
+       Ojciec toleruje. Niedaleko padło jabłko od jabłoni. Ale jak będą dłużki małe --- nie zapłacę.
+      </akap>
+    </kwestia>
+    
+
+
+    
+    <naglowek_czesc>Wyróżnienia</naglowek_czesc>
+    
+    <akap>Wyróżnienia:
+    <didask_tekst>didaskalia tekstowe</didask_tekst>,
+    <didaskalia>didaskalia</didaskalia>,
+    <indeks_dolny>indeks dolny</indeks_dolny>,
+    <osoba>osoba</osoba>,
+    <slowo_obce>słowo obce</slowo_obce>,
+    <tytul_dziela>tytuł dzieła</tytul_dziela>,
+    <wieksze_odstepy>większe odstępny</wieksze_odstepy>,
+    <wyroznienie>wyróżnienie</wyroznienie>,
+    <www>https://wolnelektury.pl/</www>.
+    </akap>
+    
+
+
+
+    <naglowek_czesc>Grupy</naglowek_czesc>
+
+    <akap>Długi cytat:</akap>
+    
+    <dlugi_cytat>
+      <akap>Długi cytat, akapit.</akap>
+      <akap_cd>Kontynuacja akapitu.</akap_cd>
+      <akap_dialog>--- Akapit dialogowy</akap_dialog>
+
+      <strofa>Strofa/ w/ cytacie</strofa>
+
+      <didaskalia>Didaskalia (czy tak?)</didaskalia>
+
+      <dlugi_cytat>
+       <akap>Cytat w cytacie!</akap>
+       <akap_cd>Kontynuacja akapitu w cytacie</akap_cd>
+       <akap_dialog>--- Akapit dialogowy w cytacie</akap_dialog>
+      </dlugi_cytat>
+
+      
+      <poezja_cyt>
+       <strofa>Poezja/ cytowana/ w cytacie</strofa>
+      </poezja_cyt>
+
+      
+      <naglowek_osoba>
+      </naglowek_osoba>
+      <kwestia>
+       ...
+      </kwestia>
+
+
+      <naglowek_rozdzial>Rozdział w cytacie</naglowek_rozdzial>
+      <naglowek_podrozdzial>Podrozdział w cytacie</naglowek_podrozdzial>
+      <srodtytul>Śródtytuł w cytacie</srodtytul>
+
+      <motto_podpis>
+      </motto_podpis>
+
+      <nota>
+       <akap>Nota w cytacie</akap>
+      </nota>
+
+      <akap>Asterysk:</akap>
+      <sekcja_asterysk/>
+
+      <akap>Światło:</akap>
+      <sekcja_swiatlo/>
+
+      <akap>Separator linia:</akap>
+      <separator_linia/>
+
+      <akap>Koniec cytatu.</akap>
+
+    </dlugi_cytat>
+
+    <akap>Tekst za cytatem.</akap>
+
+
+    <akap>Cytat poetycki:</akap>
+    
+    <poezja_cyt>
+      <naglowek_rozdzial>Nagłówek rozdziału</naglowek_rozdzial>
+      <naglowek_podrozdzial>Nagłówek podrozdziału</naglowek_podrozdzial>
+      <srodtytul>Śródtytuł</srodtytul>
+      <naglowek_osoba>Nagłówek: osoba</naglowek_osoba>
+
+      <nota>
+       <akap>
+         Nota
+       </akap>
+      </nota>
+      
+      <akap>Akapit</akap>
+
+      <strofa>Strofa/ w/ cytacie</strofa>
+      
+      <didaskalia>didaskalia?</didaskalia>
+
+      <kwestia>
+       <strofa>
+         Strofa/ w/ kwestii/ w cytacie.
+       </strofa>
+      </kwestia>
+
+      <motto_podpis>
+       Motto-podpis
+      </motto_podpis>
+
+      <poezja_cyt>
+       <strofa>
+         Poezja/
+         cytowana/
+         w cytacie/
+         poetyckim
+       </strofa>
+      </poezja_cyt>
+
+      <akap>Asterysk:</akap>
+      <sekcja_asterysk/>
+
+      
+      
+    </poezja_cyt>
+
+
+    
+    
     
     
     <naglowek_czesc>Strofa w przypisie</naglowek_czesc>
     </akap>
 
 
+
+
+
+
+    <naglowek_czesc>Wzory</naglowek_czesc>
+
+    <akap>
+      Wzór:
+      <mat><mrow>
+       <mi>δ</mi>
+       <mo>=</mo>
+       <mfrac>
+         <mrow>
+           <mi>e</mi>
+           <mo>⁢</mo>
+           <mi>F</mi>
+           <mo>⁢</mo>
+           <mi>l</mi>
+           <mo>×</mo>
+           <mfenced><mrow>
+             <mfrac>
+               <mi>l</mi>
+               <mn>2</mn>
+             </mfrac>
+             <mo>+</mo>
+             <mi>h</mi>
+           </mrow>
+           </mfenced>
+         </mrow>
+         <mrow>
+           <mi>m</mi>
+           <mo>⁢</mo>
+           <msup>
+             <mi>v</mi>
+             <mn>2</mn>
+           </msup>
+         </mrow>
+       </mfrac>
+      </mrow></mat>
+    </akap>
+
+
+
+
+    
+    <naglowek_czesc>Separatory</naglowek_czesc>
+
+    
+    <akap>Asterysk:</akap>
+    <sekcja_asterysk/>
+
+    <akap>Światło:</akap>
+    <sekcja_swiatlo/>
+
+    <akap>Separator linia:</akap>
+    <separator_linia/>
+
+
+    
+
+    <naglowek_czesc>Tabele</naglowek_czesc>
+
+    <akap>Tabela bez ramek:</akap>
+    
+    <tabela>
+      <wiersz>
+       <kol>
+         Komórka 1
+       </kol>
+       <kol>
+         Komórka 2
+       </kol>
+      </wiersz>
+      <wiersz>
+       <kol>
+         Komórka 3
+       </kol>
+       <kol>
+         Komórka 4
+       </kol>
+      </wiersz>
+    </tabela>
+
+    <akap>Tabela z ramkami:</akap>
+         
+
+    <tabela ramka="1">
+      <wiersz>
+       <kol>
+         Komórka 1
+       </kol>
+       <kol>
+         Komórka 2
+       </kol>
+      </wiersz>
+      <wiersz>
+       <kol>
+         Komórka 3
+       </kol>
+       <kol>
+         Komórka 4
+       </kol>
+      </wiersz>
+    </tabela>
+
+    <naglowek_czesc>Ilustracje</naglowek_czesc>
+
+    
     
   </opowiadanie>
 </utwor>
diff --git a/tests/uat/parent.xml b/tests/uat/parent.xml
new file mode 100644 (file)
index 0000000..544032a
--- /dev/null
@@ -0,0 +1,15 @@
+<utwor>
+  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+    <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/">
+      <dc:creator>Radek Czajka</dc:creator>
+      <dc:title>Test Parent Document</dc:title>
+      <dc:date>2020-01-08</dc:date>
+      <dc:publisher>Fundacja Nowoczesna Polska</dc:publisher>
+      <dc:language>pol</dc:language>
+      <dc:identifier.url>wl-test-parent</dc:identifier.url>
+      <dc:rights>gnu agpl</dc:rights>
+      <dc:relation.hasPart>main</dc:relation.hasPart>
+      <dc:relation.hasPart>part2</dc:relation.hasPart>
+    </rdf:Description>
+  </rdf:RDF>
+</utwor>
diff --git a/tests/uat/part2.xml b/tests/uat/part2.xml
new file mode 100644 (file)
index 0000000..2459748
--- /dev/null
@@ -0,0 +1,25 @@
+<utwor>
+  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+    <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/">
+      <dc:creator>Radek Czajka</dc:creator>
+      <dc:title>Test Document, Part 2</dc:title>
+      <dc:date>2020-01-08</dc:date>
+      <dc:publisher>Fundacja Nowoczesna Polska</dc:publisher>
+      <dc:language>pol</dc:language>
+      <dc:identifier.url>part2</dc:identifier.url>
+      <dc:rights>gnu gpl</dc:rights>
+    </rdf:Description>
+  </rdf:RDF>
+
+  <opowiadanie>
+
+<autor_utworu>Wolne Lektury</autor_utworu>
+<nazwa_utworu>Testy</nazwa_utworu>
+<podtytul>Podtytuł</podtytul>
+
+<akap>
+       Druga część. Powinna być widoczna
+</akap>
+
+  </opowiadanie>
+</utwor>