From db91f942ce46e3af1420f3469a83257ef5aca4c2 Mon Sep 17 00:00:00 2001
From: Radek Czajka
Date: Mon, 5 Jul 2021 11:47:09 +0200
Subject: [PATCH] New EPUB builder, other minor changes.
---
src/librarian/builders/__init__.py | 5 +
src/librarian/builders/epub.py | 690 ++++++++++++++++++
src/librarian/builders/pdf.py | 12 +
src/librarian/builders/txt.py | 42 +-
src/librarian/cover.py | 52 +-
src/librarian/document.py | 25 +-
src/librarian/elements/__init__.py | 2 +-
src/librarian/elements/base.py | 72 +-
src/librarian/elements/blocks/dedykacja.py | 4 +-
src/librarian/elements/blocks/dlugi_cytat.py | 3 +
src/librarian/elements/blocks/nota.py | 4 +-
src/librarian/elements/blocks/poezja_cyt.py | 3 +
src/librarian/elements/blocks/ramka.py | 2 +
src/librarian/elements/comments/abstrakt.py | 3 +
src/librarian/elements/comments/nota_red.py | 3 +
src/librarian/elements/comments/uwaga.py | 3 +
src/librarian/elements/drama/didask_tekst.py | 4 +-
src/librarian/elements/drama/didaskalia.py | 4 +-
src/librarian/elements/drama/kwestia.py | 4 +-
src/librarian/elements/drama/lista_osob.py | 7 +
src/librarian/elements/drama/lista_osoba.py | 6 +-
src/librarian/elements/drama/miejsce_czas.py | 5 +-
.../elements/drama/naglowek_listy.py | 3 +
.../elements/drama/naglowek_osoba.py | 3 +
src/librarian/elements/drama/osoba.py | 4 +-
src/librarian/elements/figures/ilustr.py | 43 +-
src/librarian/elements/figures/kol.py | 2 +-
src/librarian/elements/figures/tabela.py | 6 +-
src/librarian/elements/figures/wiersz.py | 2 +-
src/librarian/elements/footnotes/__init__.py | 43 ++
src/librarian/elements/front/autor_utworu.py | 3 +
.../elements/front/dzielo_nadrzedne.py | 3 +
src/librarian/elements/front/motto.py | 4 +-
src/librarian/elements/front/motto_podpis.py | 4 +-
src/librarian/elements/front/nazwa_utworu.py | 3 +
src/librarian/elements/front/podtytul.py | 4 +
src/librarian/elements/headers/__init__.py | 3 +
.../elements/headers/naglowek_czesc.py | 7 +-
.../elements/headers/naglowek_rozdzial.py | 6 +
.../elements/headers/naglowek_scena.py | 17 +
.../elements/headers/podtytul_czesc.py | 8 +
.../elements/headers/podtytul_podrozdzial.py | 8 +
.../elements/headers/podtytul_rozdzial.py | 8 +
src/librarian/elements/paragraphs/akap.py | 4 +-
src/librarian/elements/poetry/strofa.py | 20 +-
src/librarian/elements/poetry/wers.py | 9 +-
src/librarian/elements/poetry/wers_akap.py | 4 +
src/librarian/elements/poetry/wers_cd.py | 4 +
.../elements/poetry/wers_do_prawej.py | 2 +-
src/librarian/elements/poetry/wers_wciety.py | 5 +
.../elements/separators/sekcja_asterysk.py | 6 +-
.../elements/separators/sekcja_swiatlo.py | 6 +
.../elements/separators/separator_linia.py | 4 +-
src/librarian/elements/styles/indeks_dolny.py | 2 +-
src/librarian/elements/styles/mat.py | 4 +
src/librarian/elements/styles/slowo_obce.py | 4 +-
src/librarian/elements/styles/tytul_dziela.py | 4 +-
.../elements/styles/wieksze_odstepy.py | 4 +-
src/librarian/elements/styles/wyroznienie.py | 4 +-
src/librarian/elements/themes/motyw.py | 3 +
src/librarian/epub.py | 69 +-
src/librarian/epub/style.css | 11 +-
src/librarian/epub/xsltAnnotations.xsl | 4 +-
src/librarian/epub/xsltScheme.xsl | 12 +-
src/librarian/fonts.py | 38 +
src/librarian/fundraising.py | 13 +
src/librarian/html.py | 15 +-
src/librarian/pdf.py | 6 +-
src/librarian/pdf/wl.cls | 2 +
src/librarian/pdf/wl2tex.xslt | 19 +-
src/librarian/res/BN.png | Bin 0 -> 10125 bytes
src/librarian/res/MKIDN.jpg | Bin 0 -> 68055 bytes
src/librarian/res/atrium-logo.png | Bin 22137 -> 292850 bytes
src/librarian/res/dofinansowano.png | Bin 0 -> 68733 bytes
src/librarian/text.py | 42 +-
src/librarian/xslt/book2html.xslt | 110 ++-
tests/uat/main.xml | 331 ++++++++-
tests/uat/parent.xml | 15 +
tests/uat/part2.xml | 25 +
79 files changed, 1769 insertions(+), 171 deletions(-)
create mode 100644 src/librarian/builders/epub.py
create mode 100644 src/librarian/builders/pdf.py
create mode 100644 src/librarian/elements/headers/naglowek_scena.py
create mode 100644 src/librarian/fonts.py
create mode 100644 src/librarian/fundraising.py
create mode 100644 src/librarian/res/BN.png
create mode 100644 src/librarian/res/MKIDN.jpg
create mode 100644 src/librarian/res/dofinansowano.png
create mode 100644 tests/uat/parent.xml
create mode 100644 tests/uat/part2.xml
diff --git a/src/librarian/builders/__init__.py b/src/librarian/builders/__init__.py
index dc5bdee..e359cd6 100644
--- a/src/librarian/builders/__init__.py
+++ b/src/librarian/builders/__init__.py
@@ -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
index 0000000..91405c3
--- /dev/null
+++ b/src/librarian/builders/epub.py
@@ -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('''WolneLektury.pl''')
+
+ @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: -
+
+ # 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('
' + FUNDRAISING[f % len(FUNDRAISING)] + '
')
+ )
+ 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
+
+#
+# else:
+#
+#
+
+ 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)
+
+ #[Kopia robocza]
+
+ p = etree.XML("""
+ Ta lektura, podobnie jak tysiÄ
ce innych, jest dostÄpna on-line na stronie
+ wolnelektury.pl.
+
""")
+ 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("""
+
+ Utwór opracowany zostaÅ w ramach projektu Wolne Lektury przez fundacjÄ Nowoczesna Polska.
+
+ """))
+
+ if self.document.meta.isbn_epub:
+ etree.SubElement(tp, 'p', **{"class": "info"}).text = self.document.meta.isbn_epub
+
+ tp.append(etree.XML(""""""))
+
+ 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=''
+ )
+
+ 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 = ('''
+
+
+
+ OkÅadka
+
+
+
+
+
+''' % 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
index 0000000..6b2501e
--- /dev/null
+++ b/src/librarian/builders/pdf.py
@@ -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'')
+
+
+
diff --git a/src/librarian/builders/txt.py b/src/librarian/builders/txt.py
index 3f19346..8dba4ae 100644
--- a/src/librarian/builders/txt.py
+++ b/src/librarian/builders/txt.py
@@ -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):
diff --git a/src/librarian/cover.py b/src/librarian/cover.py
index 1873be9..7d7964d 100644
--- a/src/librarian/cover.py
+++ b/src/librarian/cover.py
@@ -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,
}
diff --git a/src/librarian/document.py b/src/librarian/document.py
index 6e94ff2..0dbb14f 100644
--- a/src/librarian/document.py
+++ b/src/librarian/document.py
@@ -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
+
diff --git a/src/librarian/elements/__init__.py b/src/librarian/elements/__init__.py
index f3a8521..c3a55fb 100644
--- a/src/librarian/elements/__init__.py
+++ b/src/librarian/elements/__init__.py
@@ -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,
diff --git a/src/librarian/elements/base.py b/src/librarian/elements/base.py
index b9df185..5317268 100644
--- a/src/librarian/elements/base.py
+++ b/src/librarian/elements/base.py
@@ -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:
diff --git a/src/librarian/elements/blocks/dedykacja.py b/src/librarian/elements/blocks/dedykacja.py
index 5436271..7ac809d 100644
--- a/src/librarian/elements/blocks/dedykacja.py
+++ b/src/librarian/elements/blocks/dedykacja.py
@@ -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"
diff --git a/src/librarian/elements/blocks/dlugi_cytat.py b/src/librarian/elements/blocks/dlugi_cytat.py
index c660583..22e98dd 100644
--- a/src/librarian/elements/blocks/dlugi_cytat.py
+++ b/src/librarian/elements/blocks/dlugi_cytat.py
@@ -10,3 +10,6 @@ class DlugiCytat(WLElement):
TXT_LEGACY_BOTTOM_MARGIN = 0
HTML_TAG = 'blockquote'
+
+ EPUB_TAG = 'div'
+ EPUB_CLASS = 'block'
diff --git a/src/librarian/elements/blocks/nota.py b/src/librarian/elements/blocks/nota.py
index a01bf29..0e3f61f 100644
--- a/src/librarian/elements/blocks/nota.py
+++ b/src/librarian/elements/blocks/nota.py
@@ -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"
diff --git a/src/librarian/elements/blocks/poezja_cyt.py b/src/librarian/elements/blocks/poezja_cyt.py
index 0c103b1..14cd539 100644
--- a/src/librarian/elements/blocks/poezja_cyt.py
+++ b/src/librarian/elements/blocks/poezja_cyt.py
@@ -10,3 +10,6 @@ class PoezjaCyt(WLElement):
TXT_LEGACY_BOTTOM_MARGIN = 0
HTML_TAG = 'blockquote'
+
+ EPUB_TAG = 'div'
+ EPUB_CLASS = 'block'
diff --git a/src/librarian/elements/blocks/ramka.py b/src/librarian/elements/blocks/ramka.py
index d8dd5f0..b85f83f 100644
--- a/src/librarian/elements/blocks/ramka.py
+++ b/src/librarian/elements/blocks/ramka.py
@@ -5,3 +5,5 @@ class Ramka(WLElement):
HTML_TAG = "div"
HTML_CLASS = "ramka"
+ EPUB_TAG = "div"
+ EPUB_CLASS = "frame"
diff --git a/src/librarian/elements/comments/abstrakt.py b/src/librarian/elements/comments/abstrakt.py
index 9b43dc3..887712b 100644
--- a/src/librarian/elements/comments/abstrakt.py
+++ b/src/librarian/elements/comments/abstrakt.py
@@ -7,3 +7,6 @@ class Abstrakt(WLElement):
def html_build(self, builder):
pass
+
+ def epub_build(self, builder):
+ pass
diff --git a/src/librarian/elements/comments/nota_red.py b/src/librarian/elements/comments/nota_red.py
index faa5dd1..0ce970e 100644
--- a/src/librarian/elements/comments/nota_red.py
+++ b/src/librarian/elements/comments/nota_red.py
@@ -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
diff --git a/src/librarian/elements/comments/uwaga.py b/src/librarian/elements/comments/uwaga.py
index adf908b..1bdeb39 100644
--- a/src/librarian/elements/comments/uwaga.py
+++ b/src/librarian/elements/comments/uwaga.py
@@ -7,3 +7,6 @@ class Uwaga(WLElement):
def html_build(self, builder):
pass
+
+ def epub_build(self, builder):
+ pass
diff --git a/src/librarian/elements/drama/didask_tekst.py b/src/librarian/elements/drama/didask_tekst.py
index 7227c17..088e373 100644
--- a/src/librarian/elements/drama/didask_tekst.py
+++ b/src/librarian/elements/drama/didask_tekst.py
@@ -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"
diff --git a/src/librarian/elements/drama/didaskalia.py b/src/librarian/elements/drama/didaskalia.py
index af0520f..bf81b69 100644
--- a/src/librarian/elements/drama/didaskalia.py
+++ b/src/librarian/elements/drama/didaskalia.py
@@ -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"
diff --git a/src/librarian/elements/drama/kwestia.py b/src/librarian/elements/drama/kwestia.py
index 27dca30..56463d6 100644
--- a/src/librarian/elements/drama/kwestia.py
+++ b/src/librarian/elements/drama/kwestia.py
@@ -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"
diff --git a/src/librarian/elements/drama/lista_osob.py b/src/librarian/elements/drama/lista_osob.py
index 5beca64..269b05c 100644
--- a/src/librarian/elements/drama/lista_osob.py
+++ b/src/librarian/elements/drama/lista_osob.py
@@ -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')
diff --git a/src/librarian/elements/drama/lista_osoba.py b/src/librarian/elements/drama/lista_osoba.py
index fe55838..c5770ee 100644
--- a/src/librarian/elements/drama/lista_osoba.py
+++ b/src/librarian/elements/drama/lista_osoba.py
@@ -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()
diff --git a/src/librarian/elements/drama/miejsce_czas.py b/src/librarian/elements/drama/miejsce_czas.py
index a4e9453..cf47ed2 100644
--- a/src/librarian/elements/drama/miejsce_czas.py
+++ b/src/librarian/elements/drama/miejsce_czas.py
@@ -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"
+
diff --git a/src/librarian/elements/drama/naglowek_listy.py b/src/librarian/elements/drama/naglowek_listy.py
index 4db9111..1f164a4 100644
--- a/src/librarian/elements/drama/naglowek_listy.py
+++ b/src/librarian/elements/drama/naglowek_listy.py
@@ -3,3 +3,6 @@ from ..base import WLElement
class NaglowekListy(WLElement):
HTML_TAG = "h3"
+
+ EPUB_TAG = "div"
+ EPUB_CLASS = "h3"
diff --git a/src/librarian/elements/drama/naglowek_osoba.py b/src/librarian/elements/drama/naglowek_osoba.py
index 5ab78fd..afa16ce 100644
--- a/src/librarian/elements/drama/naglowek_osoba.py
+++ b/src/librarian/elements/drama/naglowek_osoba.py
@@ -8,3 +8,6 @@ class NaglowekOsoba(WLElement):
TXT_LEGACY_BOTTOM_MARGIN = 0
HTML_TAG = "h4"
+
+ EPUB_TAG = "h2"
+ EPUB_CLASS = "h4"
diff --git a/src/librarian/elements/drama/osoba.py b/src/librarian/elements/drama/osoba.py
index b0fb793..ffd9494 100644
--- a/src/librarian/elements/drama/osoba.py
+++ b/src/librarian/elements/drama/osoba.py
@@ -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"
diff --git a/src/librarian/elements/figures/ilustr.py b/src/librarian/elements/figures/ilustr.py
index 3c3026c..ab7c2b7 100644
--- a/src/librarian/elements/figures/ilustr.py
+++ b/src/librarian/elements/figures/ilustr.py
@@ -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
diff --git a/src/librarian/elements/figures/kol.py b/src/librarian/elements/figures/kol.py
index e0dae02..c94f20d 100644
--- a/src/librarian/elements/figures/kol.py
+++ b/src/librarian/elements/figures/kol.py
@@ -2,4 +2,4 @@ from ..base import WLElement
class Kol(WLElement):
- HTML_TAG = 'td'
+ EPUB_TAG = HTML_TAG = 'td'
diff --git a/src/librarian/elements/figures/tabela.py b/src/librarian/elements/figures/tabela.py
index af4a436..7da7877 100644
--- a/src/librarian/elements/figures/tabela.py
+++ b/src/librarian/elements/figures/tabela.py
@@ -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
+
+
diff --git a/src/librarian/elements/figures/wiersz.py b/src/librarian/elements/figures/wiersz.py
index bc61f9d..2c7d91a 100644
--- a/src/librarian/elements/figures/wiersz.py
+++ b/src/librarian/elements/figures/wiersz.py
@@ -2,4 +2,4 @@ from ..base import WLElement
class Wiersz(WLElement):
- HTML_TAG = 'tr'
+ EPUB_TAG = HTML_TAG = 'tr'
diff --git a/src/librarian/elements/footnotes/__init__.py b/src/librarian/elements/footnotes/__init__.py
index 0f30747..433e881 100644
--- a/src/librarian/elements/footnotes/__init__.py
+++ b/src/librarian/elements/footnotes/__init__.py
@@ -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."""
diff --git a/src/librarian/elements/front/autor_utworu.py b/src/librarian/elements/front/autor_utworu.py
index fd6b2e8..736a24c 100644
--- a/src/librarian/elements/front/autor_utworu.py
+++ b/src/librarian/elements/front/autor_utworu.py
@@ -6,3 +6,6 @@ class AutorUtworu(HeaderElement):
TXT_LEGACY_BOTTOM_MARGIN = 2
HTML_CLASS = 'author'
+
+ def epub_build(self, builder):
+ return
diff --git a/src/librarian/elements/front/dzielo_nadrzedne.py b/src/librarian/elements/front/dzielo_nadrzedne.py
index a034ae7..5f114bc 100644
--- a/src/librarian/elements/front/dzielo_nadrzedne.py
+++ b/src/librarian/elements/front/dzielo_nadrzedne.py
@@ -6,3 +6,6 @@ class DzieloNadrzedne(HeaderElement):
TXT_LEGACY_BOTTOM_MARGIN = 1
HTML_CLASS = "collection"
+
+ def epub_build(self, builder):
+ return
diff --git a/src/librarian/elements/front/motto.py b/src/librarian/elements/front/motto.py
index 98c7334..7f23ea6 100644
--- a/src/librarian/elements/front/motto.py
+++ b/src/librarian/elements/front/motto.py
@@ -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"
diff --git a/src/librarian/elements/front/motto_podpis.py b/src/librarian/elements/front/motto_podpis.py
index 58fc9db..8fee127 100644
--- a/src/librarian/elements/front/motto_podpis.py
+++ b/src/librarian/elements/front/motto_podpis.py
@@ -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"
+
diff --git a/src/librarian/elements/front/nazwa_utworu.py b/src/librarian/elements/front/nazwa_utworu.py
index aa68082..4d06b47 100644
--- a/src/librarian/elements/front/nazwa_utworu.py
+++ b/src/librarian/elements/front/nazwa_utworu.py
@@ -6,3 +6,6 @@ class NazwaUtworu(HeaderElement):
TXT_LEGACY_BOTTOM_MARGIN = 1
HTML_CLASS = 'title'
+
+ EPUB_TAG = 'h2'
+ EPUB_CLASS = 'intitle'
diff --git a/src/librarian/elements/front/podtytul.py b/src/librarian/elements/front/podtytul.py
index 4431bc2..71e28a8 100644
--- a/src/librarian/elements/front/podtytul.py
+++ b/src/librarian/elements/front/podtytul.py
@@ -6,3 +6,7 @@ class Podtytul(HeaderElement):
TXT_LEGACY_BOTTOM_MARGIN = 1
HTML_CLASS = 'subtitle'
+
+ EPUB_TAG = 'h2'
+ EPUB_CLASS = 'insubtitle'
+
diff --git a/src/librarian/elements/headers/__init__.py b/src/librarian/elements/headers/__init__.py
index 3389eec..f3bda2b 100644
--- a/src/librarian/elements/headers/__init__.py
+++ b/src/librarian/elements/headers/__init__.py
@@ -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
+
+
diff --git a/src/librarian/elements/headers/naglowek_czesc.py b/src/librarian/elements/headers/naglowek_czesc.py
index c7b2d9e..829e4f4 100644
--- a/src/librarian/elements/headers/naglowek_czesc.py
+++ b/src/librarian/elements/headers/naglowek_czesc.py
@@ -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
diff --git a/src/librarian/elements/headers/naglowek_rozdzial.py b/src/librarian/elements/headers/naglowek_rozdzial.py
index ded615f..33ff355 100644
--- a/src/librarian/elements/headers/naglowek_rozdzial.py
+++ b/src/librarian/elements/headers/naglowek_rozdzial.py
@@ -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
index 0000000..8a52ca2
--- /dev/null
+++ b/src/librarian/elements/headers/naglowek_scena.py
@@ -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
+
diff --git a/src/librarian/elements/headers/podtytul_czesc.py b/src/librarian/elements/headers/podtytul_czesc.py
index 9825211..df8fd5c 100644
--- a/src/librarian/elements/headers/podtytul_czesc.py
+++ b/src/librarian/elements/headers/podtytul_czesc.py
@@ -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()
diff --git a/src/librarian/elements/headers/podtytul_podrozdzial.py b/src/librarian/elements/headers/podtytul_podrozdzial.py
index 74aef13..cc00207 100644
--- a/src/librarian/elements/headers/podtytul_podrozdzial.py
+++ b/src/librarian/elements/headers/podtytul_podrozdzial.py
@@ -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()
diff --git a/src/librarian/elements/headers/podtytul_rozdzial.py b/src/librarian/elements/headers/podtytul_rozdzial.py
index 675e19b..f8db548 100644
--- a/src/librarian/elements/headers/podtytul_rozdzial.py
+++ b/src/librarian/elements/headers/podtytul_rozdzial.py
@@ -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()
diff --git a/src/librarian/elements/paragraphs/akap.py b/src/librarian/elements/paragraphs/akap.py
index 0a76c52..b0c0329 100644
--- a/src/librarian/elements/paragraphs/akap.py
+++ b/src/librarian/elements/paragraphs/akap.py
@@ -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'
diff --git a/src/librarian/elements/poetry/strofa.py b/src/librarian/elements/poetry/strofa.py
index 7df549f..a843d20 100644
--- a/src/librarian/elements/poetry/strofa.py
+++ b/src/librarian/elements/poetry/strofa.py
@@ -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)
diff --git a/src/librarian/elements/poetry/wers.py b/src/librarian/elements/poetry/wers.py
index 5c28058..55cf537 100644
--- a/src/librarian/elements/poetry/wers.py
+++ b/src/librarian/elements/poetry/wers.py
@@ -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''')
+
diff --git a/src/librarian/elements/poetry/wers_akap.py b/src/librarian/elements/poetry/wers_akap.py
index 03b8187..c0d70f4 100644
--- a/src/librarian/elements/poetry/wers_akap.py
+++ b/src/librarian/elements/poetry/wers_akap.py
@@ -7,3 +7,7 @@ class WersAkap(Wers):
HTML_ATTR = {
"style": "padding-left: 1em"
}
+
+ EPUB_ATTR = {
+ "style": "margin-left: 1em"
+ }
diff --git a/src/librarian/elements/poetry/wers_cd.py b/src/librarian/elements/poetry/wers_cd.py
index a61d5bc..df1f563 100644
--- a/src/librarian/elements/poetry/wers_cd.py
+++ b/src/librarian/elements/poetry/wers_cd.py
@@ -8,3 +8,7 @@ class WersCd(Wers):
HTML_ATTR = {
"style": "padding-left: 12em",
}
+
+ EPUB_ATTR = {
+ "style": "margin-left: 12em",
+ }
diff --git a/src/librarian/elements/poetry/wers_do_prawej.py b/src/librarian/elements/poetry/wers_do_prawej.py
index 9ab5ff0..81af0d6 100644
--- a/src/librarian/elements/poetry/wers_do_prawej.py
+++ b/src/librarian/elements/poetry/wers_do_prawej.py
@@ -4,6 +4,6 @@ from .wers import Wers
class WersDoPrawej(Wers):
TXT_PREFIX = ' '
- HTML_ATTR = {
+ EPUB_ATTR = HTML_ATTR = {
"style": "text-align: right",
}
diff --git a/src/librarian/elements/poetry/wers_wciety.py b/src/librarian/elements/poetry/wers_wciety.py
index 8ac2bb3..662e57a 100644
--- a/src/librarian/elements/poetry/wers_wciety.py
+++ b/src/librarian/elements/poetry/wers_wciety.py
@@ -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
diff --git a/src/librarian/elements/separators/sekcja_asterysk.py b/src/librarian/elements/separators/sekcja_asterysk.py
index e68430d..46be85b 100644
--- a/src/librarian/elements/separators/sekcja_asterysk.py
+++ b/src/librarian/elements/separators/sekcja_asterysk.py
@@ -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
+
diff --git a/src/librarian/elements/separators/sekcja_swiatlo.py b/src/librarian/elements/separators/sekcja_swiatlo.py
index 7d950da..28ba6e5 100644
--- a/src/librarian/elements/separators/sekcja_swiatlo.py
+++ b/src/librarian/elements/separators/sekcja_swiatlo.py
@@ -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")
diff --git a/src/librarian/elements/separators/separator_linia.py b/src/librarian/elements/separators/separator_linia.py
index 5249691..49ea17f 100644
--- a/src/librarian/elements/separators/separator_linia.py
+++ b/src/librarian/elements/separators/separator_linia.py
@@ -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)
diff --git a/src/librarian/elements/styles/indeks_dolny.py b/src/librarian/elements/styles/indeks_dolny.py
index 5d19a44..bac93c0 100644
--- a/src/librarian/elements/styles/indeks_dolny.py
+++ b/src/librarian/elements/styles/indeks_dolny.py
@@ -4,4 +4,4 @@ from ..base import WLElement
class IndeksDolny(WLElement):
TXT_PREFIX = "_"
- HTML_TAG = "sub"
+ EPUB_TAG = HTML_TAG = "sub"
diff --git a/src/librarian/elements/styles/mat.py b/src/librarian/elements/styles/mat.py
index f284695..fa353f9 100644
--- a/src/librarian/elements/styles/mat.py
+++ b/src/librarian/elements/styles/mat.py
@@ -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()
diff --git a/src/librarian/elements/styles/slowo_obce.py b/src/librarian/elements/styles/slowo_obce.py
index 3592a1e..5848aa6 100644
--- a/src/librarian/elements/styles/slowo_obce.py
+++ b/src/librarian/elements/styles/slowo_obce.py
@@ -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'
diff --git a/src/librarian/elements/styles/tytul_dziela.py b/src/librarian/elements/styles/tytul_dziela.py
index ef3618c..906d98c 100644
--- a/src/librarian/elements/styles/tytul_dziela.py
+++ b/src/librarian/elements/styles/tytul_dziela.py
@@ -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)
diff --git a/src/librarian/elements/styles/wieksze_odstepy.py b/src/librarian/elements/styles/wieksze_odstepy.py
index 3229402..953e797 100644
--- a/src/librarian/elements/styles/wieksze_odstepy.py
+++ b/src/librarian/elements/styles/wieksze_odstepy.py
@@ -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"
diff --git a/src/librarian/elements/styles/wyroznienie.py b/src/librarian/elements/styles/wyroznienie.py
index c76b4cf..2a71a29 100644
--- a/src/librarian/elements/styles/wyroznienie.py
+++ b/src/librarian/elements/styles/wyroznienie.py
@@ -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"
diff --git a/src/librarian/elements/themes/motyw.py b/src/librarian/elements/themes/motyw.py
index f9ab197..25369a7 100644
--- a/src/librarian/elements/themes/motyw.py
+++ b/src/librarian/elements/themes/motyw.py
@@ -18,3 +18,6 @@ class Motyw(WLElement):
"fid": fid,
"name": "m" + fid,
}
+
+ def epub_build(self, builder):
+ pass
diff --git a/src/librarian/epub.py b/src/librarian/epub.py
index e2cdae7..0fb91e5 100644
--- a/src/librarian/epub.py
+++ b/src/librarian/epub.py
@@ -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('xyz')))
- xyz
- """
-
- 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('bxx')
- >>> set_inner_xml(e, 'xyz')
- >>> print(etree.tostring(e, encoding='unicode'))
- xyz
- """
-
- p = etree.fromstring('%s' % 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)
diff --git a/src/librarian/epub/style.css b/src/librarian/epub/style.css
index 82ad4b9..a740f85 100644
--- a/src/librarian/epub/style.css
+++ b/src/librarian/epub/style.css
@@ -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;
+}
diff --git a/src/librarian/epub/xsltAnnotations.xsl b/src/librarian/epub/xsltAnnotations.xsl
index cd22462..90f038e 100644
--- a/src/librarian/epub/xsltAnnotations.xsl
+++ b/src/librarian/epub/xsltAnnotations.xsl
@@ -15,7 +15,7 @@
@@ -29,7 +29,7 @@
- . [przypis autorski] [przypis tÅumacza] [przypis redakcyjny] [przypis edytorski]
+ . [przypis autorski] [przypis tÅumacza] [przypis redakcyjny] [przypis edytorski]
diff --git a/src/librarian/epub/xsltScheme.xsl b/src/librarian/epub/xsltScheme.xsl
index 93767cf..0fae871 100644
--- a/src/librarian/epub/xsltScheme.xsl
+++ b/src/librarian/epub/xsltScheme.xsl
@@ -7,9 +7,7 @@
-
- WolneLektury.pl
-
+ WolneLektury.pl
@@ -79,12 +77,12 @@
-
-
-
+
+
+
@@ -376,7 +374,7 @@
-
diff --git a/src/librarian/fonts.py b/src/librarian/fonts.py
new file mode 100644
index 0000000..adaeae4
--- /dev/null
+++ b/src/librarian/fonts.py
@@ -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
index 0000000..18d7774
--- /dev/null
+++ b/src/librarian/fundraising.py
@@ -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. Kliknij, by przejÅÄ do strony pÅatnoÅci. 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! Zadeklaruj staÅÄ
wpÅatÄ i doÅÄ
cz do Towarzystwa PrzyjacióŠWolnych Lektur.',
+ 'Informacje o nowoÅciach w naszej bibliotece w Twojej skrzynce mailowej? Nic prostszego, zapisz siÄ do newslettera.
Kliknij, by pozostawiÄ swój adres e-mail.',
+ '''Przekaż 1% podatku na Wolne Lektury.
+KRS: 0000070056
+Nazwa organizacji: Fundacja Nowoczesna Polska
+
+Możesz to zrobiÄ w swoim formularzu PIT dostÄpnym od 15 lutego na stronie: www.podatki.gov.pl/pit.
+
+Każda wpÅacona kwota zostanie przeznaczona na rozwój Wolnych Lektur.
+DziÄkujemy, że jesteÅcie z nami!''',
+]
diff --git a/src/librarian/html.py b/src/librarian/html.py
index 363286c..f0f11db 100644
--- a/src/librarian/html.py
+++ b/src/librarian/html.py
@@ -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.
diff --git a/src/librarian/pdf.py b/src/librarian/pdf.py
index cad66a4..a025b9b 100644
--- a/src/librarian/pdf.py
+++ b/src/librarian/pdf.py
@@ -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)
diff --git a/src/librarian/pdf/wl.cls b/src/librarian/pdf/wl.cls
index f8b7731..c46a0ec 100644
--- a/src/librarian/pdf/wl.cls
+++ b/src/librarian/pdf/wl.cls
@@ -145,6 +145,8 @@
\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}
diff --git a/src/librarian/pdf/wl2tex.xslt b/src/librarian/pdf/wl2tex.xslt
index 43a3274..820033d 100644
--- a/src/librarian/pdf/wl2tex.xslt
+++ b/src/librarian/pdf/wl2tex.xslt
@@ -290,10 +290,25 @@
-
+
+
+
+
+ R
+ 5cm
+
+ width=
+
+
+
+
+
+
-
+
+
+
diff --git a/src/librarian/res/BN.png b/src/librarian/res/BN.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e6df234064db629ab14ff0ed75e5909e5d07a1e
GIT binary patch
literal 10125
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