1 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
 
   2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
 
   4 from collections import defaultdict
 
   6 from urllib.request import urlopen
 
   8 from librarian.html import add_table_of_contents, add_table_of_themes, add_image_sizes
 
   9 from librarian import OutputFile
 
  13     file_extension = "html"
 
  20     no_externalities = False
 
  24     root_attrib = {'id': 'book-text'}
 
  26     def __init__(self, gallery_path=None, gallery_url=None, base_url=None):
 
  27         self._base_url = base_url
 
  28         self.gallery_path = gallery_path
 
  29         self.gallery_url = gallery_url
 
  31         self.tree = text = etree.Element(self.root_tag, **self.root_attrib)
 
  32         self.header = etree.Element('h1')
 
  34         self.footnotes = etree.Element('div', id='footnotes')
 
  35         self.counters = defaultdict(lambda: 1)
 
  37         self.nota_red = etree.Element('div', id='nota_red')
 
  41             'header': self.header,
 
  42             'footnotes': self.footnotes,
 
  43             'nota_red': self.nota_red,
 
  45         self.current_cursors = [text]
 
  49         if self._base_url is not None:
 
  52             return 'https://wolnelektury.pl/media/book/pictures/{}/'.format(self.document.meta.url.slug)
 
  56         return self.current_cursors[-1]
 
  58     def enter_fragment(self, fragment):
 
  59         cursor = self.cursors.get(fragment, self.cursor)
 
  60         self.current_cursors.append(cursor)
 
  62     def exit_fragment(self):
 
  63         self.current_cursors.pop()
 
  65     def create_fragment(self, name, element):
 
  66         assert name not in self.cursors
 
  67         self.cursors[name] = element
 
  69     def forget_fragment(self, name):
 
  70         del self.cursors[name]
 
  72     def build(self, document, element=None, **kwargs):
 
  73         self.document = document
 
  75         self.assign_ids(self.document.tree)
 
  79             element = document.tree.getroot()
 
  81         element.html_build(self)
 
  82         self.postprocess(document)
 
  85     def assign_ids(self, tree):
 
  86         # Assign IDs depth-first, to account for any <numeracja> inside.
 
  87         for _e, elem in etree.iterwalk(tree, events=('end',)):
 
  88             if getattr(elem, 'NUMBERING', None):
 
  91     def prepare_images(self):
 
  92         # Temporarily use the legacy method, before transitioning to external generators.
 
  93         if self.gallery_path is None:
 
  96             os.makedirs(self.gallery_path)
 
  99         add_image_sizes(self.document.tree, self.gallery_path, self.gallery_url, self.base_url)
 
 102         if not len(self.tree):
 
 104         return OutputFile.from_bytes(
 
 113     def postprocess(self, document):
 
 114         _ = document.tree.getroot().gettext
 
 116         if document.meta.translators:
 
 117             self.enter_fragment('header')
 
 118             self.start_element('span', {'class': 'translator'})
 
 119             self.push_text(_("translated by") + " ")
 
 122                     translator.readable()
 
 123                     for translator in document.meta.translators
 
 129             self.tree.insert(0, self.header)
 
 131         if self.with_nota_red and len(self.nota_red):
 
 132             self.tree.append(self.nota_red)
 
 134             add_table_of_themes(self.tree)
 
 136             add_table_of_contents(self.tree)
 
 138         if self.counters['fn'] > 1:
 
 139             fnheader = etree.Element("h3")
 
 140             fnheader.text = _("Footnotes")
 
 141             self.footnotes.insert(0, fnheader)
 
 142             self.tree.append(self.footnotes)
 
 144     def start_element(self, tag, attrib=None):
 
 145         self.current_cursors.append(etree.SubElement(
 
 151     def end_element(self):
 
 152         self.current_cursors.pop()
 
 154     def push_text(self, text):
 
 157             cursor[-1].tail = (cursor[-1].tail or '') + text
 
 159             cursor.text = (cursor.text or '') + text
 
 161     def add_visible_number(self, element):
 
 162         assert '_id' in element.attrib, etree.tostring(element)
 
 163         self.start_element('a', {
 
 164             'href': f'#{element.attrib["_id"]}',
 
 167         self.push_text(element.attrib['_visible_numbering'])
 
 171 class StandaloneHtmlBuilder(HtmlBuilder):
 
 172     css_url = "https://static.wolnelektury.pl/css/compressed/book_text.css"
 
 174     def postprocess(self, document):
 
 175         super(StandaloneHtmlBuilder, self).postprocess(document)
 
 177         tree = etree.Element('html')
 
 178         body = etree.SubElement(tree, 'body')
 
 179         body.append(self.tree)
 
 182         head = etree.Element('head')
 
 185         etree.SubElement(head, 'meta', charset='utf-8')
 
 186         etree.SubElement(head, 'title').text = document.meta.title
 
 192             content="width=device-width, initial-scale=1, maximum-scale=1"
 
 195         if self.no_externalities:
 
 198             ).text = urlopen(self.css_url).read().decode('utf-8')
 
 210                 src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"
 
 216                 src="http://malsup.github.io/min/jquery.cycle2.min.js"
 
 220 class SnippetHtmlBuilder(HtmlBuilder):
 
 223     with_footnotes = False
 
 224     with_nota_red = False
 
 226     with_numbering = False
 
 229 class AbstraktHtmlBuilder(HtmlBuilder):
 
 232     with_footnotes = False
 
 233     with_nota_red = False
 
 235     with_numbering = False
 
 237     root_tag = 'blockquote'
 
 240     def build(self, document, element=None, **kwargs):
 
 242             element = document.tree.find('//abstrakt')
 
 244             return OutputFile.from_bytes(b'')
 
 245         element.attrib['_force'] = '1'
 
 246         return super().build(document, element, **kwargs)
 
 249 class DaisyHtmlBuilder(StandaloneHtmlBuilder):
 
 250     file_extension = 'xhtml'
 
 253     with_footnotes = False
 
 254     with_nota_red = False
 
 255     with_deep_identifiers = False
 
 256     no_externalities = True
 
 257     with_numbering = False
 
 260         tree = etree.ElementTree(self.tree)
 
 261         tree.docinfo.public_id = '-//W3C//DTD XHTML 1.0 Transitional//EN'
 
 262         tree.docinfo.system_url = 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
 
 263         return OutputFile.from_bytes(