From c1000fe83b652dcd85b8fc57b60567e3d93aa5c9 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Mon, 25 Feb 2013 12:01:34 +0100 Subject: [PATCH 01/16] Scalable metrics in Cover --- librarian/cover.py | 150 ++++++++++++++++++++++++++++----------------- scripts/book2cover | 10 ++- 2 files changed, 101 insertions(+), 59 deletions(-) diff --git a/librarian/cover.py b/librarian/cover.py index be34e26..9fb01b7 100644 --- a/librarian/cover.py +++ b/librarian/cover.py @@ -9,6 +9,19 @@ from StringIO import StringIO from librarian import get_resource, OutputFile, URLOpener +class Metric(object): + """Gets metrics from an object, scaling it by a factor.""" + def __init__(self, obj, scale): + self._obj = obj + self._scale = float(scale) + + def __getattr__(self, name): + src = getattr(self._obj, name) + if src and self._scale: + src = type(src)(self._scale * src) + return src + + class TextBox(object): """Creates an Image with a series of centered strings.""" @@ -94,7 +107,8 @@ class Cover(object): author_lineskip = 40 author_color = '#000' author_shadow = None - author_font = None + author_font_ttf = get_resource('fonts/DejaVuSerif.ttf') + author_font_size = 30 title_top = 100 title_margin_left = 20 @@ -102,13 +116,15 @@ class Cover(object): title_lineskip = 54 title_color = '#000' title_shadow = None - title_font = None + title_font_ttf = get_resource('fonts/DejaVuSerif.ttf') + title_font_size = 40 logo_bottom = None logo_width = None uses_dc_cover = False format = 'JPEG' + scale = 1 exts = { 'JPEG': 'jpg', @@ -120,11 +136,14 @@ class Cover(object): 'PNG': 'image/png', } - def __init__(self, book_info, format=None): + def __init__(self, book_info, format=None, width=None, height=None): self.author = ", ".join(auth.readable() for auth in book_info.authors) self.title = book_info.title if format is not None: self.format = format + scale = max(float(width or 0) / self.width, float(height or 0) / self.height) + if scale: + self.scale = scale def pretty_author(self): """Allows for decorating author's name.""" @@ -135,7 +154,8 @@ class Cover(object): return self.title def image(self): - img = Image.new('RGB', (self.width, self.height), self.background_color) + metr = Metric(self, self.scale) + img = Image.new('RGB', (metr.width, metr.height), self.background_color) if self.background_img: background = Image.open(self.background_img) @@ -143,34 +163,35 @@ class Cover(object): del background # WL logo - if self.logo_width: + if metr.logo_width: logo = Image.open(get_resource('res/wl-logo.png')) - logo = logo.resize((self.logo_width, logo.size[1] * self.logo_width / logo.size[0])) - img.paste(logo, ((self.width - self.logo_width) / 2, img.size[1] - logo.size[1] - self.logo_bottom)) + logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0])) + img.paste(logo, ((metr.width - metr.logo_width) / 2, img.size[1] - logo.size[1] - metr.logo_bottom)) - top = self.author_top + top = metr.author_top tbox = TextBox( - self.width - self.author_margin_left - self.author_margin_right, - self.height - top, + metr.width - metr.author_margin_left - metr.author_margin_right, + metr.height - top, ) - author_font = self.author_font or ImageFont.truetype( - get_resource('fonts/DejaVuSerif.ttf'), 30) + + author_font = ImageFont.truetype( + self.author_font_ttf, metr.author_font_size) tbox.text(self.pretty_author(), self.author_color, author_font, - self.author_lineskip, self.author_shadow) + metr.author_lineskip, self.author_shadow) text_img = tbox.image() - img.paste(text_img, (self.author_margin_left, top), text_img) + img.paste(text_img, (metr.author_margin_left, top), text_img) - top += text_img.size[1] + self.title_top + top += text_img.size[1] + metr.title_top tbox = TextBox( - self.width - self.title_margin_left - self.title_margin_right, - self.height - top, + metr.width - metr.title_margin_left - metr.title_margin_right, + metr.height - top, ) - title_font = self.author_font or ImageFont.truetype( - get_resource('fonts/DejaVuSerif.ttf'), 40) + title_font = ImageFont.truetype( + self.title_font_ttf, metr.title_font_size) tbox.text(self.pretty_title(), self.title_color, title_font, - self.title_lineskip, self.title_shadow) + metr.title_lineskip, self.title_shadow) text_img = tbox.image() - img.paste(text_img, (self.title_margin_left, top), text_img) + img.paste(text_img, (metr.title_margin_left, top), text_img) return img @@ -181,7 +202,7 @@ class Cover(object): return self.exts[self.format] def save(self, *args, **kwargs): - return self.image().save(format=self.format, *args, **kwargs) + return self.image().save(format=self.format, quality=95, *args, **kwargs) def output_file(self, *args, **kwargs): imgstr = StringIO() @@ -194,13 +215,23 @@ class WLCover(Cover): width = 600 height = 833 uses_dc_cover = True - author_font = ImageFont.truetype( - get_resource('fonts/JunicodeWL-Regular.ttf'), 20) + author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + author_font_size = 20 author_lineskip = 30 - title_font = ImageFont.truetype( - get_resource('fonts/DejaVuSerif-Bold.ttf'), 30) + title_font_ttf = get_resource('fonts/DejaVuSerif-Bold.ttf') + title_font_size = 30 title_lineskip = 40 title_box_width = 350 + + box_top_margin = 100 + box_bottom_margin = 100 + box_padding_y = 20 + box_above_line = 10 + box_below_line = 15 + box_line_left = 75 + box_line_right = 275 + box_line_width = 2 + bar_width = 35 background_color = '#444' author_color = '#444' @@ -220,19 +251,13 @@ class WLCover(Cover): u'Współczesność': '#06393d', } - def __init__(self, book_info, format=None, image_cache=None): - super(WLCover, self).__init__(book_info, format=format) + def __init__(self, book_info, format=None, width=None, height=None): + super(WLCover, self).__init__(book_info, format=format, width=width, height=height) self.kind = book_info.kind self.epoch = book_info.epoch if book_info.cover_url: url = book_info.cover_url bg_src = None - if image_cache: - from urllib import quote - try: - bg_src = URLOpener().open(image_cache + quote(url, safe="")) - except: - pass if bg_src is None: bg_src = URLOpener().open(url) self.background_img = StringIO(bg_src.read()) @@ -244,18 +269,19 @@ class WLCover(Cover): return self.author.upper() def image(self): - img = Image.new('RGB', (self.width, self.height), self.background_color) + metr = Metric(self, self.scale) + img = Image.new('RGB', (metr.width, metr.height), self.background_color) draw = ImageDraw.Draw(img) if self.epoch in self.epoch_colors: epoch_color = self.epoch_colors[self.epoch] else: epoch_color = '#000' - draw.rectangle((0, 0, self.bar_width, self.height), fill=epoch_color) + draw.rectangle((0, 0, metr.bar_width, metr.height), fill=epoch_color) if self.background_img: src = Image.open(self.background_img) - trg_size = (self.width - self.bar_width, self.height) + trg_size = (metr.width - metr.bar_width, metr.height) if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]: resized = ( trg_size[0], @@ -273,25 +299,29 @@ class WLCover(Cover): src = src.resize(resized) src = src.crop((cut, 0, src.size[0] - cut, src.size[1])) - img.paste(src, (self.bar_width, 0)) + img.paste(src, (metr.bar_width, 0)) del src - box = TextBox(self.title_box_width, self.height, padding_y=20) + box = TextBox(metr.title_box_width, metr.height, padding_y=metr.box_padding_y) + author_font = ImageFont.truetype( + self.author_font_ttf, metr.author_font_size) box.text(self.pretty_author(), - font=self.author_font, - line_height=self.author_lineskip, + font=author_font, + line_height=metr.author_lineskip, color=self.author_color, shadow_color=self.author_shadow, ) - box.skip(10) - box.draw.line((75, box.height, 275, box.height), - fill=self.author_color, width=2) - box.skip(15) + box.skip(metr.box_above_line) + box.draw.line((metr.box_line_left, box.height, metr.box_line_right, box.height), + fill=self.author_color, width=metr.box_line_width) + box.skip(metr.box_below_line) + title_font = ImageFont.truetype( + self.title_font_ttf, metr.title_font_size) box.text(self.pretty_title(), - line_height=self.title_lineskip, - font=self.title_font, + line_height=metr.title_lineskip, + font=title_font, color=epoch_color, shadow_color=self.title_shadow, ) @@ -299,15 +329,15 @@ class WLCover(Cover): if self.kind == 'Liryka': # top - box_top = 100 + box_top = metr.box_top_margin elif self.kind == 'Epika': # bottom - box_top = self.height - 100 - box_img.size[1] + box_top = metr.height - metr.box_bottom_margin - box_img.size[1] else: # center - box_top = (self.height - box_img.size[1]) / 2 + box_top = (metr.height - box_img.size[1]) / 2 - box_left = self.bar_width + (self.width - self.bar_width - + box_left = metr.bar_width + (metr.width - metr.bar_width - box_img.size[0]) / 2 draw.rectangle((box_left, box_top, box_left + box_img.size[0], box_top + box_img.size[1]), @@ -338,7 +368,8 @@ class PrestigioCover(Cover): author_lineskip = 60 author_color = '#fff' author_shadow = '#000' - author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Italic.ttf'), 50) + author_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf') + author_font_size = 50 title_top = 0 title_margin_left = 118 @@ -346,7 +377,8 @@ class PrestigioCover(Cover): title_lineskip = 60 title_color = '#fff' title_shadow = '#000' - title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Italic.ttf'), 50) + title_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf') + title_font_size = 50 def pretty_title(self): return u"„%s”" % self.title @@ -362,14 +394,16 @@ class BookotekaCover(Cover): author_margin_right = 233 author_lineskip = 156 author_color = '#d9d919' - author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 130) + author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + author_font_size = 130 title_top = 400 title_margin_left = 307 title_margin_right = 233 title_lineskip = 168 title_color = '#d9d919' - title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 140) + title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + title_font_size = 140 format = 'PNG' @@ -378,8 +412,10 @@ class GandalfCover(Cover): width = 600 height = 730 background_img = get_resource('res/cover-gandalf.png') - author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 30) - title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 40) + author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + author_font_size = 30 + title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + title_font_size = 40 logo_bottom = 25 logo_width = 250 format = 'PNG' diff --git a/scripts/book2cover b/scripts/book2cover index 3cc0ed7..6cda7b3 100755 --- a/scripts/book2cover +++ b/scripts/book2cover @@ -14,10 +14,16 @@ class Book2Cover(Book2Anything): ext = "jpg" uses_cover = True cover_optional = False + transform_options = [ + Option('-W', '--width', action='store', type='int', dest='width', default=None, + help='Set width.'), + Option('-H', '--height', action='store', type='int', dest='height', default=None, + help='Set height.'), + ] @staticmethod - def transform(wldoc, cover): - return wldoc.as_cover(cover_class=cover) + def transform(wldoc, cover, *args, **kwargs): + return wldoc.as_cover(cover_class=cover, *args, **kwargs) if __name__ == '__main__': -- 2.20.1 From 0cefa871f6f1253be544a39c51e1f66f536805ce Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Mon, 25 Feb 2013 13:33:17 +0100 Subject: [PATCH 02/16] Add WL logo on cover --- librarian/cover.py | 29 +++++++++++++++++++++++------ scripts/book2cover | 4 ++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/librarian/cover.py b/librarian/cover.py index 9fb01b7..a37b911 100644 --- a/librarian/cover.py +++ b/librarian/cover.py @@ -4,7 +4,7 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # import re -import Image, ImageFont, ImageDraw, ImageFilter +import Image, ImageFont, ImageDraw, ImageFilter, ImageEnhance from StringIO import StringIO from librarian import get_resource, OutputFile, URLOpener @@ -231,7 +231,10 @@ class WLCover(Cover): box_line_left = 75 box_line_right = 275 box_line_width = 2 - + + logo_top = 15 + logo_width = 140 + bar_width = 35 background_color = '#444' author_color = '#444' @@ -251,10 +254,11 @@ class WLCover(Cover): u'Współczesność': '#06393d', } - def __init__(self, book_info, format=None, width=None, height=None): + def __init__(self, book_info, format=None, width=None, height=None, with_logo=False): super(WLCover, self).__init__(book_info, format=format, width=width, height=height) self.kind = book_info.kind self.epoch = book_info.epoch + self.with_logo = with_logo if book_info.cover_url: url = book_info.cover_url bg_src = None @@ -288,7 +292,7 @@ class WLCover(Cover): src.size[1] * trg_size[0] / src.size[0] ) cut = (resized[1] - trg_size[1]) / 2 - src = src.resize(resized) + src = src.resize(resized, Image.ANTIALIAS) src = src.crop((0, cut, src.size[0], src.size[1] - cut)) else: resized = ( @@ -296,7 +300,7 @@ class WLCover(Cover): trg_size[1], ) cut = (resized[0] - trg_size[0]) / 2 - src = src.resize(resized) + src = src.resize(resized, Image.ANTIALIAS) src = src.crop((cut, 0, src.size[0] - cut, src.size[1])) img.paste(src, (metr.bar_width, 0)) @@ -325,6 +329,15 @@ class WLCover(Cover): color=epoch_color, shadow_color=self.title_shadow, ) + + if self.with_logo: + logo = Image.open(get_resource('res/wl-logo-mono.png')) + logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0]), Image.ANTIALIAS) + alpha = logo.split()[3] + alpha = ImageEnhance.Brightness(alpha).enhance(.75) + logo.putalpha(alpha) + box.skip(metr.logo_top + logo.size[1]) + box_img = box.image() if self.kind == 'Liryka': @@ -344,8 +357,12 @@ class WLCover(Cover): fill='#fff') img.paste(box_img, (box_left, box_top), box_img) - return img + if self.with_logo: + img.paste(logo, + (box_left + (box_img.size[0] - logo.size[0]) / 2, + box_top + box_img.size[1] - metr.box_padding_y - logo.size[1]), mask=logo) + return img class VirtualoCover(Cover): diff --git a/scripts/book2cover b/scripts/book2cover index 6cda7b3..758ab0e 100755 --- a/scripts/book2cover +++ b/scripts/book2cover @@ -14,11 +14,15 @@ class Book2Cover(Book2Anything): ext = "jpg" uses_cover = True cover_optional = False + transform_options = [ Option('-W', '--width', action='store', type='int', dest='width', default=None, help='Set width.'), Option('-H', '--height', action='store', type='int', dest='height', default=None, help='Set height.'), + Option('-l', '--with-logo', dest='with_logo', + action='store_true', default=False, + help='Add WL logo in white box.'), ] @staticmethod -- 2.20.1 From 13480b3da2d3da87f1d99c6d340c1553ca9d89c1 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 2 May 2013 12:17:09 +0200 Subject: [PATCH 03/16] Some experiments with the language: html, epub, covers. --- librarian/__init__.py | 193 +------- librarian/book2anything.py | 98 ++-- librarian/core.py | 37 ++ librarian/cover.py | 438 ------------------ librarian/document.py | 59 +++ librarian/formats/__init__.py | 12 + librarian/formats/cover/__init__.py | 219 +++++++++ librarian/formats/cover/partners/__init__.py | 88 ++++ .../formats/cover/wolnelektury/__init__.py | 166 +++++++ librarian/formats/epub/__init__.py | 279 +++++++++++ librarian/formats/epub/res/chapter.html | 12 + librarian/formats/epub/res/content.opf | 24 + .../{epub => formats/epub/res}/cover.html | 0 librarian/formats/epub/res/footnotes.html | 17 + librarian/formats/html/__init__.py | 167 +++++++ librarian/formats/html/res/html.html | 8 + .../formats/html/res/html_standalone.html | 15 + librarian/functions.py | 106 ----- librarian/html.py | 279 ----------- librarian/meta.py | 96 ++++ librarian/output.py | 78 ++++ librarian/parser.py | 246 +--------- librarian/picture.py | 173 ------- librarian/renderers.py | 107 +++++ librarian/utils.py | 88 ++++ scripts/book2cover | 14 +- scripts/book2epub | 12 +- scripts/book2html | 16 +- setup.py | 20 +- 29 files changed, 1552 insertions(+), 1515 deletions(-) create mode 100755 librarian/core.py delete mode 100644 librarian/cover.py create mode 100755 librarian/document.py create mode 100644 librarian/formats/__init__.py create mode 100644 librarian/formats/cover/__init__.py create mode 100644 librarian/formats/cover/partners/__init__.py create mode 100644 librarian/formats/cover/wolnelektury/__init__.py create mode 100644 librarian/formats/epub/__init__.py create mode 100644 librarian/formats/epub/res/chapter.html create mode 100644 librarian/formats/epub/res/content.opf rename librarian/{epub => formats/epub/res}/cover.html (100%) create mode 100644 librarian/formats/epub/res/footnotes.html create mode 100644 librarian/formats/html/__init__.py create mode 100644 librarian/formats/html/res/html.html create mode 100644 librarian/formats/html/res/html_standalone.html delete mode 100644 librarian/functions.py delete mode 100644 librarian/html.py create mode 100755 librarian/meta.py create mode 100755 librarian/output.py delete mode 100644 librarian/picture.py create mode 100755 librarian/renderers.py create mode 100755 librarian/utils.py diff --git a/librarian/__init__.py b/librarian/__init__.py index c46d5d1..0616f23 100644 --- a/librarian/__init__.py +++ b/librarian/__init__.py @@ -3,12 +3,10 @@ # This file is part of Librarian, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # -from __future__ import with_statement - import os import re -import shutil import urllib +from .utils import XMLNamespace class UnicodeException(Exception): @@ -31,31 +29,6 @@ class ParseError(UnicodeException): class ValidationError(UnicodeException): pass -class NoDublinCore(ValidationError): - """There's no DublinCore section, and it's required.""" - pass - -class NoProvider(UnicodeException): - """There's no DocProvider specified, and it's needed.""" - pass - -class XMLNamespace(object): - '''A handy structure to repsent names in an XML namespace.''' - - def __init__(self, uri): - self.uri = uri - - def __call__(self, tag): - return '{%s}%s' % (self.uri, tag) - - def __contains__(self, tag): - return tag.startswith('{' + str(self) + '}') - - def __repr__(self): - return 'XMLNamespace(%r)' % self.uri - - def __str__(self): - return '%s' % self.uri class EmptyNamespace(XMLNamespace): def __init__(self): @@ -72,7 +45,7 @@ XHTMLNS = XMLNamespace("http://www.w3.org/1999/xhtml") NCXNS = XMLNamespace("http://www.daisy.org/z3986/2005/ncx/") OPFNS = XMLNamespace("http://www.idpf.org/2007/opf") -WLNS = EmptyNamespace() +SSTNS = XMLNamespace('http://nowoczesnapolska.org.pl/sst#') class WLURI(object): @@ -117,165 +90,7 @@ class WLURI(object): return self.slug == other.slug -class DocProvider(object): - """Base class for a repository of XML files. - - Used for generating joined files, like EPUBs. - """ - - def by_slug(self, slug): - """Should return a file-like object with a WL document XML.""" - raise NotImplementedError - - def by_uri(self, uri, wluri=WLURI): - """Should return a file-like object with a WL document XML.""" - wluri = wluri(uri) - return self.by_slug(wluri.slug) - - -class DirDocProvider(DocProvider): - """ Serve docs from a directory of files in form .xml """ - - def __init__(self, dir_): - self.dir = dir_ - self.files = {} - - def by_slug(self, slug): - fname = slug + '.xml' - return open(os.path.join(self.dir, fname)) - - -import lxml.etree as etree -import dcparser - -DEFAULT_BOOKINFO = dcparser.BookInfo( - { RDFNS('about'): u'http://wiki.wolnepodreczniki.pl/Lektury:Template'}, - { DCNS('creator'): [u'Some, Author'], - DCNS('title'): [u'Some Title'], - DCNS('subject.period'): [u'Unknown'], - DCNS('subject.type'): [u'Unknown'], - DCNS('subject.genre'): [u'Unknown'], - DCNS('date'): ['1970-01-01'], - DCNS('language'): [u'pol'], - # DCNS('date'): [creation_date], - DCNS('publisher'): [u"Fundacja Nowoczesna Polska"], - DCNS('description'): - [u"""Publikacja zrealizowana w ramach projektu - Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa - wykonana przez Bibliotekę Narodową z egzemplarza - pochodzącego ze zbiorów BN."""], - DCNS('identifier.url'): [WLURI.example], - DCNS('rights'): - [u"Domena publiczna - zm. [OPIS STANU PRAWNEGO TEKSTU]"] }) - -def xinclude_forURI(uri): - e = etree.Element(XINS("include")) - e.set("href", uri) - return etree.tostring(e, encoding=unicode) - -def wrap_text(ocrtext, creation_date, bookinfo=DEFAULT_BOOKINFO): - """Wrap the text within the minimal XML structure with a DC template.""" - bookinfo.created_at = creation_date - - dcstring = etree.tostring(bookinfo.to_etree(), \ - method='xml', encoding=unicode, pretty_print=True) - - return u'\n' + dcstring + u'\n\n' + ocrtext + \ - u'\n\n' - - -def serialize_raw(element): - b = u'' + (element.text or '') - - for child in element.iterchildren(): - e = etree.tostring(child, method='xml', encoding=unicode, - pretty_print=True) - b += e - - return b - -SERIALIZERS = { - 'raw': serialize_raw, -} - -def serialize_children(element, format='raw'): - return SERIALIZERS[format](element) - -def get_resource(path): - return os.path.join(os.path.dirname(__file__), path) - - -class OutputFile(object): - """Represents a file returned by one of the converters.""" - - _string = None - _filename = None - - def __del__(self): - if self._filename: - os.unlink(self._filename) - - def __nonzero__(self): - return self._string is not None or self._filename is not None - - @classmethod - def from_string(cls, string): - """Converter returns contents of a file as a string.""" - - instance = cls() - instance._string = string - return instance - - @classmethod - def from_filename(cls, filename): - """Converter returns contents of a file as a named file.""" - - instance = cls() - instance._filename = filename - return instance - - def get_string(self): - """Get file's contents as a string.""" - - if self._filename is not None: - with open(self._filename) as f: - return f.read() - else: - return self._string - - def get_file(self): - """Get file as a file-like object.""" - - if self._string is not None: - from StringIO import StringIO - return StringIO(self._string) - elif self._filename is not None: - return open(self._filename) - - def get_filename(self): - """Get file as a fs path.""" - - if self._filename is not None: - return self._filename - elif self._string is not None: - from tempfile import NamedTemporaryFile - temp = NamedTemporaryFile(prefix='librarian-', delete=False) - temp.write(self._string) - temp.close() - self._filename = temp.name - return self._filename - else: - return None - - def save_as(self, path): - """Save file to a path. Create directories, if necessary.""" - - dirname = os.path.dirname(os.path.abspath(path)) - if not os.path.isdir(dirname): - os.makedirs(dirname) - shutil.copy(self.get_filename(), path) - - class URLOpener(urllib.FancyURLopener): - version = 'FNP Librarian (http://github.com/fnp/librarian)' + version = 'FNP Librarian (http://git.nowoczesnapolska.org.pl/?p=librarian.git)' urllib._urlopener = URLOpener() + diff --git a/librarian/book2anything.py b/librarian/book2anything.py index b8b8d27..b50cb1c 100755 --- a/librarian/book2anything.py +++ b/librarian/book2anything.py @@ -8,9 +8,8 @@ from collections import namedtuple import os.path import optparse -from librarian import DirDocProvider, ParseError -from librarian.parser import WLDocument -from librarian.cover import WLCover +from librarian import ParseError +from librarian.document import Document class Option(object): @@ -34,47 +33,26 @@ class Book2Anything(object): Subclass it for any format you want to convert to. """ - format_name = None # Set format name, like "PDF". - ext = None # Set file extension, like "pdf". - uses_cover = False # Can it add a cover? - cover_optional = True # Only relevant if uses_cover - uses_provider = False # Does it need a DocProvider? - transform = None # Transform method. Uses WLDocument.as_{ext} by default. - parser_options = [] # List of Option objects for additional parser args. - transform_options = [] # List of Option objects for additional transform args. - transform_flags = [] # List of Option objects for supported transform flags. - + format_cls = None # A formats.Format subclass + document_options = [] # List of Option objects for document options. + format_options = [] # List of Option objects for format customization. + build_options = [] # List of Option objects for build options. @classmethod def run(cls): # Parse commandline arguments usage = """Usage: %%prog [options] SOURCE [SOURCE...] - Convert SOURCE files to %s format.""" % cls.format_name + Convert SOURCE files to %s.""" % cls.format_cls.format_name parser = optparse.OptionParser(usage=usage) parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='print status messages to stdout') - parser.add_option('-d', '--make-dir', - action='store_true', dest='make_dir', default=False, - help='create a directory for author and put the output file in it') parser.add_option('-o', '--output-file', dest='output_file', metavar='FILE', help='specifies the output file') - parser.add_option('-O', '--output-dir', - dest='output_dir', metavar='DIR', - help='specifies the directory for output') - if cls.uses_cover: - if cls.cover_optional: - parser.add_option('-c', '--with-cover', - action='store_true', dest='with_cover', default=False, - help='create default cover') - parser.add_option('-C', '--image-cache', - dest='image_cache', metavar='URL', - help='prefix for image download cache' + - (' (implies --with-cover)' if cls.cover_optional else '')) - for option in cls.parser_options + cls.transform_options + cls.transform_flags: + for option in cls.document_options + cls.format_options + cls.build_options: option.add(parser) options, input_filenames = parser.parse_args() @@ -83,28 +61,18 @@ class Book2Anything(object): parser.print_help() return(1) - # Prepare additional args for parser. - parser_args = {} - for option in cls.parser_options: - parser_args[option.name()] = option.value(options) - # Prepare additional args for transform method. - transform_args = {} - for option in cls.transform_options: - transform_args[option.name()] = option.value(options) - # Add flags to transform_args, if any. - transform_flags = [flag.name() for flag in cls.transform_flags - if flag.value(options)] - if transform_flags: - transform_args['flags'] = transform_flags - # Add cover support, if any. - if cls.uses_cover: - if options.image_cache: - def cover_class(*args, **kwargs): - return WLCover(image_cache=options.image_cache, *args, **kwargs) - transform_args['cover'] = cover_class - elif not cls.cover_optional or options.with_cover: - transform_args['cover'] = WLCover - + # Prepare additional args for document. + document_args = {} + for option in cls.document_options: + document_args[option.name()] = option.value(options) + # Prepare additional args for format. + format_args = {} + for option in cls.format_options: + format_args[option.name()] = option.value(options) + # Prepare additional args for build. + build_args = {} + for option in cls.build_options: + build_args[option.name()] = option.value(options) # Do some real work try: @@ -112,28 +80,18 @@ class Book2Anything(object): if options.verbose: print main_input - # Where to find input? - if cls.uses_provider: - path, fname = os.path.realpath(main_input).rsplit('/', 1) - provider = DirDocProvider(path) - else: - provider = None + # Do the transformation. + doc = Document.from_file(main_input, **document_args) + format_ = cls.format_cls(doc, **format_args) # Where to write output? - if not (options.output_file or options.output_dir): - output_file = os.path.splitext(main_input)[0] + '.' + cls.ext + if not options.output_file: + output_file = os.path.splitext(main_input)[0] + '.' + format_.format_ext else: output_file = None - - # Do the transformation. - doc = WLDocument.from_file(main_input, provider=provider, **parser_args) - transform = cls.transform - if transform is None: - transform = getattr(WLDocument, 'as_%s' % cls.ext) - output = transform(doc, **transform_args) - - doc.save_output_file(output, - output_file, options.output_dir, options.make_dir, cls.ext) + + output = format_.build(**build_args) + output.save_as(output_file) except ParseError, e: print '%(file)s:%(name)s:%(message)s' % { diff --git a/librarian/core.py b/librarian/core.py new file mode 100755 index 0000000..0b90a2e --- /dev/null +++ b/librarian/core.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from lxml import etree +from librarian import SSTNS +from .meta import Metadata + + +class TextElement(etree.ElementBase): + @property + def meta(self): + m = self.find(SSTNS('metadata')) + if m is None: + return Metadata.about(self) + return m + + +class Span(TextElement): + pass + + +class Div(TextElement): + pass + + +class Section(TextElement): + pass + + +class Header(TextElement): + pass + + +class Aside(TextElement): + pass diff --git a/librarian/cover.py b/librarian/cover.py deleted file mode 100644 index a37b911..0000000 --- a/librarian/cover.py +++ /dev/null @@ -1,438 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import re -import Image, ImageFont, ImageDraw, ImageFilter, ImageEnhance -from StringIO import StringIO -from librarian import get_resource, OutputFile, URLOpener - - -class Metric(object): - """Gets metrics from an object, scaling it by a factor.""" - def __init__(self, obj, scale): - self._obj = obj - self._scale = float(scale) - - def __getattr__(self, name): - src = getattr(self._obj, name) - if src and self._scale: - src = type(src)(self._scale * src) - return src - - -class TextBox(object): - """Creates an Image with a series of centered strings.""" - - SHADOW_X = 3 - SHADOW_Y = 3 - SHADOW_BLUR = 3 - - def __init__(self, max_width, max_height, padding_x=None, padding_y=None): - if padding_x is None: - padding_x = self.SHADOW_X + self.SHADOW_BLUR - if padding_y is None: - padding_y = self.SHADOW_Y + self.SHADOW_BLUR - - self.max_width = max_width - self.max_text_width = max_width - 2 * padding_x - self.padding_y = padding_y - self.height = padding_y - self.img = Image.new('RGBA', (max_width, max_height)) - self.draw = ImageDraw.Draw(self.img) - self.shadow_img = None - self.shadow_draw = None - - def skip(self, height): - """Skips some vertical space.""" - self.height += height - - def text(self, text, color='#000', font=None, line_height=20, - shadow_color=None): - """Writes some centered text.""" - text = re.sub(r'\s+', ' ', text) - if shadow_color: - if not self.shadow_img: - self.shadow_img = Image.new('RGBA', self.img.size) - self.shadow_draw = ImageDraw.Draw(self.shadow_img) - while text: - line = text - line_width = self.draw.textsize(line, font=font)[0] - while line_width > self.max_text_width: - parts = line.rsplit(' ', 1) - if len(parts) == 1: - line_width = self.max_text_width - break - line = parts[0] - line_width = self.draw.textsize(line, font=font)[0] - line = line.strip() + ' ' - - pos_x = (self.max_width - line_width) / 2 - - if shadow_color: - self.shadow_draw.text( - (pos_x + self.SHADOW_X, self.height + self.SHADOW_Y), - line, font=font, fill=shadow_color - ) - - self.draw.text((pos_x, self.height), line, font=font, fill=color) - self.height += line_height - # go to next line - text = text[len(line):] - - def image(self): - """Creates the actual Image object.""" - image = Image.new('RGBA', (self.max_width, - self.height + self.padding_y)) - if self.shadow_img: - shadow = self.shadow_img.filter(ImageFilter.BLUR) - image.paste(shadow, (0, 0), shadow) - image.paste(self.img, (0, 0), self.img) - else: - image.paste(self.img, (0, 0)) - return image - - -class Cover(object): - """Abstract base class for cover images generator.""" - width = 600 - height = 800 - background_color = '#fff' - background_img = None - - author_top = 100 - author_margin_left = 20 - author_margin_right = 20 - author_lineskip = 40 - author_color = '#000' - author_shadow = None - author_font_ttf = get_resource('fonts/DejaVuSerif.ttf') - author_font_size = 30 - - title_top = 100 - title_margin_left = 20 - title_margin_right = 20 - title_lineskip = 54 - title_color = '#000' - title_shadow = None - title_font_ttf = get_resource('fonts/DejaVuSerif.ttf') - title_font_size = 40 - - logo_bottom = None - logo_width = None - uses_dc_cover = False - - format = 'JPEG' - scale = 1 - - exts = { - 'JPEG': 'jpg', - 'PNG': 'png', - } - - mime_types = { - 'JPEG': 'image/jpeg', - 'PNG': 'image/png', - } - - def __init__(self, book_info, format=None, width=None, height=None): - self.author = ", ".join(auth.readable() for auth in book_info.authors) - self.title = book_info.title - if format is not None: - self.format = format - scale = max(float(width or 0) / self.width, float(height or 0) / self.height) - if scale: - self.scale = scale - - def pretty_author(self): - """Allows for decorating author's name.""" - return self.author - - def pretty_title(self): - """Allows for decorating title.""" - return self.title - - def image(self): - metr = Metric(self, self.scale) - img = Image.new('RGB', (metr.width, metr.height), self.background_color) - - if self.background_img: - background = Image.open(self.background_img) - img.paste(background, None, background) - del background - - # WL logo - if metr.logo_width: - logo = Image.open(get_resource('res/wl-logo.png')) - logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0])) - img.paste(logo, ((metr.width - metr.logo_width) / 2, img.size[1] - logo.size[1] - metr.logo_bottom)) - - top = metr.author_top - tbox = TextBox( - metr.width - metr.author_margin_left - metr.author_margin_right, - metr.height - top, - ) - - author_font = ImageFont.truetype( - self.author_font_ttf, metr.author_font_size) - tbox.text(self.pretty_author(), self.author_color, author_font, - metr.author_lineskip, self.author_shadow) - text_img = tbox.image() - img.paste(text_img, (metr.author_margin_left, top), text_img) - - top += text_img.size[1] + metr.title_top - tbox = TextBox( - metr.width - metr.title_margin_left - metr.title_margin_right, - metr.height - top, - ) - title_font = ImageFont.truetype( - self.title_font_ttf, metr.title_font_size) - tbox.text(self.pretty_title(), self.title_color, title_font, - metr.title_lineskip, self.title_shadow) - text_img = tbox.image() - img.paste(text_img, (metr.title_margin_left, top), text_img) - - return img - - def mime_type(self): - return self.mime_types[self.format] - - def ext(self): - return self.exts[self.format] - - def save(self, *args, **kwargs): - return self.image().save(format=self.format, quality=95, *args, **kwargs) - - def output_file(self, *args, **kwargs): - imgstr = StringIO() - self.save(imgstr, *args, **kwargs) - return OutputFile.from_string(imgstr.getvalue()) - - -class WLCover(Cover): - """Default Wolne Lektury cover generator.""" - width = 600 - height = 833 - uses_dc_cover = True - author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') - author_font_size = 20 - author_lineskip = 30 - title_font_ttf = get_resource('fonts/DejaVuSerif-Bold.ttf') - title_font_size = 30 - title_lineskip = 40 - title_box_width = 350 - - box_top_margin = 100 - box_bottom_margin = 100 - box_padding_y = 20 - box_above_line = 10 - box_below_line = 15 - box_line_left = 75 - box_line_right = 275 - box_line_width = 2 - - logo_top = 15 - logo_width = 140 - - bar_width = 35 - background_color = '#444' - author_color = '#444' - default_background = get_resource('res/cover.png') - format = 'JPEG' - - epoch_colors = { - u'Starożytność': '#9e3610', - u'Średniowiecze': '#564c09', - u'Renesans': '#8ca629', - u'Barok': '#a6820a', - u'Oświecenie': '#f2802e', - u'Romantyzm': '#db4b16', - u'Pozytywizm': '#961060', - u'Modernizm': '#7784e0', - u'Dwudziestolecie międzywojenne': '#3044cf', - u'Współczesność': '#06393d', - } - - def __init__(self, book_info, format=None, width=None, height=None, with_logo=False): - super(WLCover, self).__init__(book_info, format=format, width=width, height=height) - self.kind = book_info.kind - self.epoch = book_info.epoch - self.with_logo = with_logo - if book_info.cover_url: - url = book_info.cover_url - bg_src = None - if bg_src is None: - bg_src = URLOpener().open(url) - self.background_img = StringIO(bg_src.read()) - bg_src.close() - else: - self.background_img = self.default_background - - def pretty_author(self): - return self.author.upper() - - def image(self): - metr = Metric(self, self.scale) - img = Image.new('RGB', (metr.width, metr.height), self.background_color) - draw = ImageDraw.Draw(img) - - if self.epoch in self.epoch_colors: - epoch_color = self.epoch_colors[self.epoch] - else: - epoch_color = '#000' - draw.rectangle((0, 0, metr.bar_width, metr.height), fill=epoch_color) - - if self.background_img: - src = Image.open(self.background_img) - trg_size = (metr.width - metr.bar_width, metr.height) - if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]: - resized = ( - trg_size[0], - src.size[1] * trg_size[0] / src.size[0] - ) - cut = (resized[1] - trg_size[1]) / 2 - src = src.resize(resized, Image.ANTIALIAS) - src = src.crop((0, cut, src.size[0], src.size[1] - cut)) - else: - resized = ( - src.size[0] * trg_size[1] / src.size[1], - trg_size[1], - ) - cut = (resized[0] - trg_size[0]) / 2 - src = src.resize(resized, Image.ANTIALIAS) - src = src.crop((cut, 0, src.size[0] - cut, src.size[1])) - - img.paste(src, (metr.bar_width, 0)) - del src - - box = TextBox(metr.title_box_width, metr.height, padding_y=metr.box_padding_y) - author_font = ImageFont.truetype( - self.author_font_ttf, metr.author_font_size) - box.text(self.pretty_author(), - font=author_font, - line_height=metr.author_lineskip, - color=self.author_color, - shadow_color=self.author_shadow, - ) - - box.skip(metr.box_above_line) - box.draw.line((metr.box_line_left, box.height, metr.box_line_right, box.height), - fill=self.author_color, width=metr.box_line_width) - box.skip(metr.box_below_line) - - title_font = ImageFont.truetype( - self.title_font_ttf, metr.title_font_size) - box.text(self.pretty_title(), - line_height=metr.title_lineskip, - font=title_font, - color=epoch_color, - shadow_color=self.title_shadow, - ) - - if self.with_logo: - logo = Image.open(get_resource('res/wl-logo-mono.png')) - logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0]), Image.ANTIALIAS) - alpha = logo.split()[3] - alpha = ImageEnhance.Brightness(alpha).enhance(.75) - logo.putalpha(alpha) - box.skip(metr.logo_top + logo.size[1]) - - box_img = box.image() - - if self.kind == 'Liryka': - # top - box_top = metr.box_top_margin - elif self.kind == 'Epika': - # bottom - box_top = metr.height - metr.box_bottom_margin - box_img.size[1] - else: - # center - box_top = (metr.height - box_img.size[1]) / 2 - - box_left = metr.bar_width + (metr.width - metr.bar_width - - box_img.size[0]) / 2 - draw.rectangle((box_left, box_top, - box_left + box_img.size[0], box_top + box_img.size[1]), - fill='#fff') - img.paste(box_img, (box_left, box_top), box_img) - - if self.with_logo: - img.paste(logo, - (box_left + (box_img.size[0] - logo.size[0]) / 2, - box_top + box_img.size[1] - metr.box_padding_y - logo.size[1]), mask=logo) - - return img - - -class VirtualoCover(Cover): - width = 600 - height = 730 - author_top = 73 - title_top = 73 - logo_bottom = 25 - logo_width = 250 - - -class PrestigioCover(Cover): - width = 580 - height = 783 - background_img = get_resource('res/cover-prestigio.png') - - author_top = 446 - author_margin_left = 118 - author_margin_right = 62 - author_lineskip = 60 - author_color = '#fff' - author_shadow = '#000' - author_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf') - author_font_size = 50 - - title_top = 0 - title_margin_left = 118 - title_margin_right = 62 - title_lineskip = 60 - title_color = '#fff' - title_shadow = '#000' - title_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf') - title_font_size = 50 - - def pretty_title(self): - return u"„%s”" % self.title - - -class BookotekaCover(Cover): - width = 2140 - height = 2733 - background_img = get_resource('res/cover-bookoteka.png') - - author_top = 480 - author_margin_left = 307 - author_margin_right = 233 - author_lineskip = 156 - author_color = '#d9d919' - author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') - author_font_size = 130 - - title_top = 400 - title_margin_left = 307 - title_margin_right = 233 - title_lineskip = 168 - title_color = '#d9d919' - title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') - title_font_size = 140 - - format = 'PNG' - - -class GandalfCover(Cover): - width = 600 - height = 730 - background_img = get_resource('res/cover-gandalf.png') - author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') - author_font_size = 30 - title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') - title_font_size = 40 - logo_bottom = 25 - logo_width = 250 - format = 'PNG' diff --git a/librarian/document.py b/librarian/document.py new file mode 100755 index 0000000..32148e3 --- /dev/null +++ b/librarian/document.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from StringIO import StringIO +from lxml import etree +from . import SSTNS +from .core import Section +from .parser import SSTParser + + +class Document(object): + # Do I use meta_context? + def __init__(self, edoc, meta_context=None): + self.edoc = edoc + + root_elem = edoc.getroot() + if meta_context is not None: + root_elem.meta_context = meta_context + + if not isinstance(root_elem, Section): + if root_elem.tag != SSTNS('section'): + raise ValidationError("Invalid root element. Found '%s', should be '%s'" % ( + root_elem.tag, SSTNS('section'))) + else: + raise ValidationError("Invalid class of root element. " + "Use librarian.parser.SSTParser.") + + @classmethod + def from_string(cls, xml, *args, **kwargs): + return cls.from_file(StringIO(xml), *args, **kwargs) + + @classmethod + def from_file(cls, xmlfile, *args, **kwargs): + # first, prepare for parsing + if isinstance(xmlfile, basestring): + file = open(xmlfile, 'rb') + try: + data = file.read() + finally: + file.close() + else: + data = xmlfile.read() + + if not isinstance(data, unicode): + data = data.decode('utf-8') + + data = data.replace(u'\ufeff', '') + + parser = SSTParser() + tree = etree.parse(StringIO(data.encode('utf-8')), parser) + tree.xinclude() + return cls(tree, *args, **kwargs) + + @property + def meta(self): + """ Document's metadata is root's metadata. """ + return self.edoc.getroot().meta diff --git a/librarian/formats/__init__.py b/librarian/formats/__init__.py new file mode 100644 index 0000000..cfe4fc2 --- /dev/null +++ b/librarian/formats/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +class Format(object): + """ Generic format class. """ + def __init__(self, doc): + self.doc = doc + + def build(self): + raise NotImplementedError diff --git a/librarian/formats/cover/__init__.py b/librarian/formats/cover/__init__.py new file mode 100644 index 0000000..7a787e8 --- /dev/null +++ b/librarian/formats/cover/__init__.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +import re +from PIL import Image, ImageFont, ImageDraw, ImageFilter, ImageEnhance +from StringIO import StringIO +from librarian import DCNS, URLOpener +from librarian.output import OutputFile +from librarian.utils import get_resource +from librarian.formats import Format + + +class Metric(object): + """Gets metrics from an object, scaling it by a factor.""" + def __init__(self, obj, scale): + self._obj = obj + self._scale = float(scale) + + def __getattr__(self, name): + src = getattr(self._obj, name) + if src and self._scale: + src = type(src)(self._scale * src) + return src + + +class TextBox(object): + """Creates an Image with a series of centered strings.""" + + SHADOW_X = 3 + SHADOW_Y = 3 + SHADOW_BLUR = 3 + + def __init__(self, max_width, max_height, padding_x=None, padding_y=None): + if padding_x is None: + padding_x = self.SHADOW_X + self.SHADOW_BLUR + if padding_y is None: + padding_y = self.SHADOW_Y + self.SHADOW_BLUR + + self.max_width = max_width + self.max_text_width = max_width - 2 * padding_x + self.padding_y = padding_y + self.height = padding_y + self.img = Image.new('RGBA', (max_width, max_height)) + self.draw = ImageDraw.Draw(self.img) + self.shadow_img = None + self.shadow_draw = None + + def skip(self, height): + """Skips some vertical space.""" + self.height += height + + def text(self, text, color='#000', font=None, line_height=20, + shadow_color=None): + """Writes some centered text.""" + text = re.sub(r'\s+', ' ', text) + if shadow_color: + if not self.shadow_img: + self.shadow_img = Image.new('RGBA', self.img.size) + self.shadow_draw = ImageDraw.Draw(self.shadow_img) + while text: + line = text + line_width = self.draw.textsize(line, font=font)[0] + while line_width > self.max_text_width: + parts = line.rsplit(' ', 1) + if len(parts) == 1: + line_width = self.max_text_width + break + line = parts[0] + line_width = self.draw.textsize(line, font=font)[0] + line = line.strip() + ' ' + + pos_x = (self.max_width - line_width) / 2 + + if shadow_color: + self.shadow_draw.text( + (pos_x + self.SHADOW_X, self.height + self.SHADOW_Y), + line, font=font, fill=shadow_color + ) + + self.draw.text((pos_x, self.height), line, font=font, fill=color) + self.height += line_height + # go to next line + text = text[len(line):] + + def image(self): + """Creates the actual Image object.""" + image = Image.new('RGBA', (self.max_width, + self.height + self.padding_y)) + if self.shadow_img: + shadow = self.shadow_img.filter(ImageFilter.BLUR) + image.paste(shadow, (0, 0), shadow) + image.paste(self.img, (0, 0), self.img) + else: + image.paste(self.img, (0, 0)) + return image + + +class Cover(Format): + """Base class for cover images generator.""" + format_name = u"cover image" + + width = 600 + height = 800 + background_color = '#fff' + background_img = None + + author_top = 100 + author_margin_left = 20 + author_margin_right = 20 + author_lineskip = 40 + author_color = '#000' + author_shadow = None + author_font_ttf = get_resource('fonts/DejaVuSerif.ttf') + author_font_size = 30 + + title_top = 100 + title_margin_left = 20 + title_margin_right = 20 + title_lineskip = 54 + title_color = '#000' + title_shadow = None + title_font_ttf = get_resource('fonts/DejaVuSerif.ttf') + title_font_size = 40 + + logo_bottom = None + logo_width = None + uses_dc_cover = False + + format = 'JPEG' + scale = 1 + + exts = { + 'JPEG': 'jpg', + 'PNG': 'png', + } + + mime_types = { + 'JPEG': 'image/jpeg', + 'PNG': 'image/png', + } + + def __init__(self, doc, format=None, width=None, height=None): + self.author = ", ".join(auth for auth in doc.meta.get(DCNS('creator'))) + self.title = doc.meta.title() + if format is not None: + self.format = format + scale = max(float(width or 0) / self.width, float(height or 0) / self.height) + if scale: + self.scale = scale + + def pretty_author(self): + """Allows for decorating author's name.""" + return self.author + + def pretty_title(self): + """Allows for decorating title.""" + return self.title + + def image(self): + metr = Metric(self, self.scale) + img = Image.new('RGB', (metr.width, metr.height), self.background_color) + + if self.background_img: + background = Image.open(self.background_img) + img.paste(background, None, background) + del background + + # WL logo + if metr.logo_width: + logo = Image.open(get_resource('res/wl-logo.png')) + logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0])) + img.paste(logo, ((metr.width - metr.logo_width) / 2, img.size[1] - logo.size[1] - metr.logo_bottom)) + + top = metr.author_top + tbox = TextBox( + metr.width - metr.author_margin_left - metr.author_margin_right, + metr.height - top, + ) + + author_font = ImageFont.truetype( + self.author_font_ttf, metr.author_font_size) + tbox.text(self.pretty_author(), self.author_color, author_font, + metr.author_lineskip, self.author_shadow) + text_img = tbox.image() + img.paste(text_img, (metr.author_margin_left, top), text_img) + + top += text_img.size[1] + metr.title_top + tbox = TextBox( + metr.width - metr.title_margin_left - metr.title_margin_right, + metr.height - top, + ) + title_font = ImageFont.truetype( + self.title_font_ttf, metr.title_font_size) + tbox.text(self.pretty_title(), self.title_color, title_font, + metr.title_lineskip, self.title_shadow) + text_img = tbox.image() + img.paste(text_img, (metr.title_margin_left, top), text_img) + + return img + imgstr = StringIO() + img.save(imgstr, format=self.format, quality=95) + OutputFile.from_string(imgstr.getvalue()) + + def mime_type(self): + return self.mime_types[self.format] + + @property + def format_ext(self): + return self.exts[self.format] + + def save(self, *args, **kwargs): + return self.image().save(format=self.format, quality=95, *args, **kwargs) + + def build(self, *args, **kwargs): + imgstr = StringIO() + self.save(imgstr, *args, **kwargs) + return OutputFile.from_string(imgstr.getvalue()) diff --git a/librarian/formats/cover/partners/__init__.py b/librarian/formats/cover/partners/__init__.py new file mode 100644 index 0000000..2d8a663 --- /dev/null +++ b/librarian/formats/cover/partners/__init__.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from librarian.utils import get_resource +from .. import Cover + + +class VirtualoCover(Cover): + format_name = u"Virtualo cover image" + + width = 600 + height = 730 + author_top = 73 + title_top = 73 + logo_bottom = 25 + logo_width = 250 + + +class PrestigioCover(Cover): + format_name = u"Prestigio cover image" + + width = 580 + height = 783 + background_img = get_resource('res/cover-prestigio.png') + + author_top = 446 + author_margin_left = 118 + author_margin_right = 62 + author_lineskip = 60 + author_color = '#fff' + author_shadow = '#000' + author_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf') + author_font_size = 50 + + title_top = 0 + title_margin_left = 118 + title_margin_right = 62 + title_lineskip = 60 + title_color = '#fff' + title_shadow = '#000' + title_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf') + title_font_size = 50 + + def pretty_title(self): + return u"„%s”" % self.title + + +class BookotekaCover(Cover): + format_name = u"Bookoteka cover image" + + width = 2140 + height = 2733 + background_img = get_resource('res/cover-bookoteka.png') + + author_top = 480 + author_margin_left = 307 + author_margin_right = 233 + author_lineskip = 156 + author_color = '#d9d919' + author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + author_font_size = 130 + + title_top = 400 + title_margin_left = 307 + title_margin_right = 233 + title_lineskip = 168 + title_color = '#d9d919' + title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + title_font_size = 140 + + format = 'PNG' + + +class GandalfCover(Cover): + format_name = u"Gandalf cover image" + + width = 600 + height = 730 + background_img = get_resource('res/cover-gandalf.png') + author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + author_font_size = 30 + title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + title_font_size = 40 + logo_bottom = 25 + logo_width = 250 + format = 'PNG' diff --git a/librarian/formats/cover/wolnelektury/__init__.py b/librarian/formats/cover/wolnelektury/__init__.py new file mode 100644 index 0000000..4218770 --- /dev/null +++ b/librarian/formats/cover/wolnelektury/__init__.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from PIL import Image, ImageFont, ImageDraw +from librarian.utils import get_resource +from .. import Cover, Metric, TextBox + + +class WLCover(Cover): + """Default Wolne Lektury cover generator.""" + format_name = u"WL-style cover image" + + width = 600 + height = 833 + uses_dc_cover = True + author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') + author_font_size = 20 + author_lineskip = 30 + title_font_ttf = get_resource('fonts/DejaVuSerif-Bold.ttf') + title_font_size = 30 + title_lineskip = 40 + title_box_width = 350 + + box_top_margin = 100 + box_bottom_margin = 100 + box_padding_y = 20 + box_above_line = 10 + box_below_line = 15 + box_line_left = 75 + box_line_right = 275 + box_line_width = 2 + + logo_top = 15 + logo_width = 140 + + bar_width = 35 + background_color = '#444' + author_color = '#444' + default_background = get_resource('res/cover.png') + format = 'JPEG' + + epoch_colors = { + u'Starożytność': '#9e3610', + u'Średniowiecze': '#564c09', + u'Renesans': '#8ca629', + u'Barok': '#a6820a', + u'Oświecenie': '#f2802e', + u'Romantyzm': '#db4b16', + u'Pozytywizm': '#961060', + u'Modernizm': '#7784e0', + u'Dwudziestolecie międzywojenne': '#3044cf', + u'Współczesność': '#06393d', + } + + def __init__(self, doc, format=None, width=None, height=None, with_logo=False): + super(WLCover, self).__init__(doc, format=format, width=width, height=height) + self.kind = doc.meta.get_one('kind') + self.epoch = doc.meta.get_one('epoch') + self.with_logo = with_logo + # TODO + if doc.meta.get('cover_url'): + url = doc.meta.get('cover_url')[0] + bg_src = None + if bg_src is None: + bg_src = URLOpener().open(url) + self.background_img = StringIO(bg_src.read()) + bg_src.close() + else: + self.background_img = self.default_background + + def pretty_author(self): + return self.author.upper() + + def image(self): + metr = Metric(self, self.scale) + img = Image.new('RGB', (metr.width, metr.height), self.background_color) + draw = ImageDraw.Draw(img) + + if self.epoch in self.epoch_colors: + epoch_color = self.epoch_colors[self.epoch] + else: + epoch_color = '#000' + draw.rectangle((0, 0, metr.bar_width, metr.height), fill=epoch_color) + + if self.background_img: + src = Image.open(self.background_img) + trg_size = (metr.width - metr.bar_width, metr.height) + if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]: + resized = ( + trg_size[0], + src.size[1] * trg_size[0] / src.size[0] + ) + cut = (resized[1] - trg_size[1]) / 2 + src = src.resize(resized, Image.ANTIALIAS) + src = src.crop((0, cut, src.size[0], src.size[1] - cut)) + else: + resized = ( + src.size[0] * trg_size[1] / src.size[1], + trg_size[1], + ) + cut = (resized[0] - trg_size[0]) / 2 + src = src.resize(resized, Image.ANTIALIAS) + src = src.crop((cut, 0, src.size[0] - cut, src.size[1])) + + img.paste(src, (metr.bar_width, 0)) + del src + + box = TextBox(metr.title_box_width, metr.height, padding_y=metr.box_padding_y) + author_font = ImageFont.truetype( + self.author_font_ttf, metr.author_font_size) + box.text(self.pretty_author(), + font=author_font, + line_height=metr.author_lineskip, + color=self.author_color, + shadow_color=self.author_shadow, + ) + + box.skip(metr.box_above_line) + box.draw.line((metr.box_line_left, box.height, metr.box_line_right, box.height), + fill=self.author_color, width=metr.box_line_width) + box.skip(metr.box_below_line) + + title_font = ImageFont.truetype( + self.title_font_ttf, metr.title_font_size) + box.text(self.pretty_title(), + line_height=metr.title_lineskip, + font=title_font, + color=epoch_color, + shadow_color=self.title_shadow, + ) + + if self.with_logo: + logo = Image.open(get_resource('res/wl-logo-mono.png')) + logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0]), Image.ANTIALIAS) + alpha = logo.split()[3] + alpha = ImageEnhance.Brightness(alpha).enhance(.75) + logo.putalpha(alpha) + box.skip(metr.logo_top + logo.size[1]) + + box_img = box.image() + + if self.kind == 'Liryka': + # top + box_top = metr.box_top_margin + elif self.kind == 'Epika': + # bottom + box_top = metr.height - metr.box_bottom_margin - box_img.size[1] + else: + # center + box_top = (metr.height - box_img.size[1]) / 2 + + box_left = metr.bar_width + (metr.width - metr.bar_width - + box_img.size[0]) / 2 + draw.rectangle((box_left, box_top, + box_left + box_img.size[0], box_top + box_img.size[1]), + fill='#fff') + img.paste(box_img, (box_left, box_top), box_img) + + if self.with_logo: + img.paste(logo, + (box_left + (box_img.size[0] - logo.size[0]) / 2, + box_top + box_img.size[1] - metr.box_padding_y - logo.size[1]), mask=logo) + + return img diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py new file mode 100644 index 0000000..f9f7565 --- /dev/null +++ b/librarian/formats/epub/__init__.py @@ -0,0 +1,279 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +import os +from copy import deepcopy +from tempfile import NamedTemporaryFile +import zipfile +from lxml import etree +from librarian import OPFNS, NCXNS, XHTMLNS +from librarian import core +from librarian.formats import Format +from librarian.formats.cover.wolnelektury import WLCover +from librarian.output import OutputFile +from librarian.renderers import Register, TreeRenderer, UnknownElement +from librarian.utils import Context, get_resource, extend_element + + +class EpubFormat(Format): + format_name = 'EPUB' + format_ext = 'epub' + + cover = WLCover + renderers = Register() + + def __init__(self, doc, cover=None, with_fonts=True): + super(EpubFormat, self).__init__(doc) + self.with_fonts = with_fonts + if cover is not None: + self.cover = cover + + def build(self): + opf = etree.parse(get_resource('formats/epub/res/content.opf')) + manifest = opf.find(OPFNS('manifest')) + guide = opf.find(OPFNS('guide')) + spine = opf.find(OPFNS('spine')) + + output_file = NamedTemporaryFile(prefix='librarian', suffix='.epub', delete=False) + zip = zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) + + mime = zipfile.ZipInfo() + mime.filename = 'mimetype' + mime.compress_type = zipfile.ZIP_STORED + mime.extra = '' + zip.writestr(mime, 'application/epub+zip') + zip.writestr('META-INF/container.xml', '' \ + '' \ + '') + + toc_file = etree.fromstring('' \ + '' \ + '') + nav_map = toc_file[-1] + + if self.cover is not None: + cover = self.cover(self.doc) + cover_output = cover.build() + cover_name = 'cover.%s' % cover.format_ext + zip.writestr(os.path.join('OPS', cover_name), cover_output.get_string()) + del cover_output + + cover_tree = etree.parse(get_resource('formats/epub/res/cover.html')) + cover_tree.find('//' + XHTMLNS('img')).set('src', cover_name) + zip.writestr('OPS/cover.html', etree.tostring( + cover_tree, method="html", pretty_print=True)) + + if cover.uses_dc_cover: + if self.doc.meta.get_one('cover_by'): + document.edoc.getroot().set('data-cover-by', self.doc.meta.get_one('cover_by')) + if self.doc.meta.get_one('cover_source'): + document.edoc.getroot().set('data-cover-source', self.doc.meta.get_one('cover_source')) + + manifest.append(etree.fromstring( + '')) + manifest.append(etree.fromstring( + '' % (cover_name, cover.mime_type()))) + spine.insert(0, etree.fromstring('')) + opf.getroot()[0].append(etree.fromstring('')) + guide.append(etree.fromstring('')) + + + ctx = Context(format=self) + ctx.toc = TOC() + ctx.toc_level = 0 + ctx.footnotes = Footnotes() + ctx.part_no = 0 + + wrap_tmpl = etree.parse(get_resource('formats/epub/res/chapter.html')) + for e in self.render(self.doc.edoc.getroot(), ctx): + if not len(e) and not e.text.strip(): + continue + wrap = deepcopy(wrap_tmpl) + extend_element(wrap.find('//*[@id="book-text"]'), e) + + partstr = 'part%d' % int(e.get('part_no')) + manifest.append(manifest.makeelement(OPFNS('item'), attrib={ + 'id': partstr, + 'href': partstr + ".html", + 'media-type': 'application/xhtml+xml', + })) + spine.append(spine.makeelement(OPFNS('itemref'), attrib={ + 'idref': partstr, + })) + zip.writestr('OPS/%s.html' % partstr, etree.tostring(wrap, method='html')) + + if len(ctx.footnotes.output): + ctx.toc.add("Przypisy", "footnotes.html") + manifest.append(etree.Element(OPFNS('item'), + id='footnotes', href='footnotes.html', + **{'media-type': "application/xhtml+xml"})) + spine.append(etree.Element('itemref', idref='footnotes')) + wrap = etree.parse(get_resource('formats/epub/res/footnotes.html')) + extend_element(wrap.find('//*[@id="footnotes"]'), ctx.footnotes.output) + + #chars = chars.union(used_chars(html_tree.getroot())) + zip.writestr('OPS/footnotes.html', etree.tostring( + wrap, method="html", pretty_print=True)) + + + zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True)) + ctx.toc.render(toc_file[-1]) + zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True)) + zip.close() + return OutputFile.from_filename(output_file.name) + + def render(self, element, ctx): + return self.renderers.get_for(element).render(element, ctx) + + +# Helpers + +class EpubRenderer(TreeRenderer): + """ Renders insides as XML in a <_/> container. """ + def container(self, ctx): + root, inner = super(EpubRenderer, self).container() + root.set("part_no", str(ctx.part_no)) + return root, inner + + def render(self, element, ctx): + subctx = self.subcontext(element, ctx) + wrapper, inside = self.container(ctx) + if element.text: + extend_element(inside, self.render_text(element.text, ctx)) + for child in element: + try: + child_renderer = ctx.format.renderers.get_for(child) + except UnknownElement: + continue + else: + if getattr(child_renderer, 'epub_separate', False): + yield wrapper + ctx.part_no += 1 + for child_part in child_renderer.render(child, subctx): + yield child_part + wrapper, inside = self.container(ctx) + else: + child_parts = list(child_renderer.render(child, subctx)) + extend_element(inside, child_parts[0]) + if len(child_parts) > 1: + yield wrapper + for child_part in child_parts[1:-1]: + yield child_part + wrapper, inside = self.container(ctx) + extend_element(inside, child_parts[-1]) + finally: + if child.tail: + extend_element(inside, self.render_text(child.tail, ctx)) + yield wrapper + + +class Footnotes(object): + def __init__(self): + self.counter = 0 + self.output = etree.Element("_") + + def append(self, items): + self.counter += 1 + e = etree.Element("a", + href="part%d.html#footnote-anchor-%d" % (int(items[0].get('part_no')), self.counter), + id="footnote-%d" % self.counter, + style="float:left;margin-right:1em") + e.text = "[%d]" % self.counter + e.tail = " " + self.output.append(e) + for item in items: + extend_element(self.output, item) + anchor = etree.Element("a", + id="footnote-anchor-%d" % self.counter, + href="footnotes.html#footnote-%d" % self.counter) + anchor.text = "[%d]" % self.counter + return anchor + + +class TOC(object): + def __init__(self, title=None, href="", root=None): + if root is None: + self.counter = 0 + self.root = self + else: + self.root = root + self.children = [] + self.title = title + self.href = href.format(counter=self.root.counter) + self.number = self.root.counter + self.root.counter += 1 + + def add(self, title, href): + subtoc = type(self)(title, href, root=self.root) + self.children.append(subtoc) + return subtoc + + def render(self, nav_map): + for child in self.children: + nav_point = etree.Element(NCXNS('navPoint')) + nav_point.set('id', 'NavPoint-%d' % child.number) + nav_point.set('playOrder', str(child.number)) + + nav_label = etree.Element(NCXNS('navLabel')) + text = etree.Element(NCXNS('text')) + text.text = child.title + nav_label.append(text) + nav_point.append(nav_label) + + content = etree.Element(NCXNS('content')) + content.set('src', child.href) + nav_point.append(content) + nav_map.append(nav_point) + child.render(nav_map) + + +# Renderers + +class AsideR(EpubRenderer): + def render(self, element, ctx): + outputs = list(super(AsideR, self).render(element, ctx)) + anchor = ctx.footnotes.append(outputs) + wrapper, inside = self.text_container() #etree.Element('_', part_no=str(ctx.part_no)) + inside.append(anchor) + yield wrapper +EpubFormat.renderers.register(core.Aside, None, AsideR('div')) + + +class DivR(EpubRenderer): + def container(self, ctx): + root, inner = super(DivR, self).container(ctx) + if getattr(ctx, 'inline', False): + inner.tag = 'span' + inner.set('style', 'display: block;') + return root, inner +EpubFormat.renderers.register(core.Div, None, DivR('div')) + + +class HeaderR(EpubRenderer): + def subcontext(self, element, ctx): + return Context(ctx, inline=True) +EpubFormat.renderers.register(core.Header, None, HeaderR('h1')) + + +class SectionR(EpubRenderer): + epub_separate = True + + def render(self, element, ctx): + # Add 'poczatek'? + if element.getparent() is not None: + tocitem = ctx.toc.add(element.meta.title(), 'part%d.html' % ctx.part_no) + ctx = Context(ctx, toc=tocitem) + return super(SectionR, self).render(element, ctx) +EpubFormat.renderers.register(core.Section, None, SectionR()) + + +class SpanR(EpubRenderer): + pass +EpubFormat.renderers.register(core.Span, None, SpanR('span')) + diff --git a/librarian/formats/epub/res/chapter.html b/librarian/formats/epub/res/chapter.html new file mode 100644 index 0000000..342d5df --- /dev/null +++ b/librarian/formats/epub/res/chapter.html @@ -0,0 +1,12 @@ + + + + + + WolneLektury.pl + + + +
+ + diff --git a/librarian/formats/epub/res/content.opf b/librarian/formats/epub/res/content.opf new file mode 100644 index 0000000..df95a3a --- /dev/null +++ b/librarian/formats/epub/res/content.opf @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/librarian/epub/cover.html b/librarian/formats/epub/res/cover.html similarity index 100% rename from librarian/epub/cover.html rename to librarian/formats/epub/res/cover.html diff --git a/librarian/formats/epub/res/footnotes.html b/librarian/formats/epub/res/footnotes.html new file mode 100644 index 0000000..b3b868c --- /dev/null +++ b/librarian/formats/epub/res/footnotes.html @@ -0,0 +1,17 @@ + + + + + + Przypisy + + + +
+

+ Przypisy: +

+
+
+ + diff --git a/librarian/formats/html/__init__.py b/librarian/formats/html/__init__.py new file mode 100644 index 0000000..ddf2c78 --- /dev/null +++ b/librarian/formats/html/__init__.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +import re +from lxml import etree +from librarian.formats import Format +from librarian.output import OutputFile +from librarian.renderers import Register, TreeRenderer +from librarian.utils import Context, get_resource +from librarian import core + + +class HtmlFormat(Format): + format_name = 'HTML' + format_ext = 'html' + + renderers = Register() + + def __init__(self, doc, standalone=False): + super(HtmlFormat, self).__init__(doc) + self.standalone = standalone + + def build(self): + if self.standalone: + tmpl = get_resource("formats/html/res/html_standalone.html") + else: + tmpl = get_resource("formats/html/res/html.html") + t = etree.parse(tmpl) + + ctx = Context(format=self) + ctx.toc = TOC() + ctx.toc_level = 0 + ctx.footnotes = Footnotes() + + if self.standalone: + t.find('head/title').text = u"%s (%s)" % (self.doc.meta.title(), self.doc.meta.author()) + + t.find('.//div[@id="content"]').extend( + self.render(self.doc.edoc.getroot(), ctx)) + t.find('.//div[@id="toc"]').append(ctx.toc.render()) + t.find('.//div[@id="footnotes"]').extend(ctx.footnotes.output) + + return OutputFile.from_string(etree.tostring( + t, encoding='utf-8', method="html")) + + def render(self, element, ctx): + return self.renderers.get_for(element).render(element, ctx) + + +# Helpers + +class NaturalText(TreeRenderer): + def render_text(self, text, ctx): + root, inner = self.text_container() + chunks = re.split('(?<=\s\w) ', text) + inner.text = chunks[0] + for chunk in chunks[1:]: + x = etree.Entity("nbsp") + x.tail = chunk + inner.append(x) + return root + + +class LiteralText(TreeRenderer): + pass + + +class Footnotes(object): + def __init__(self): + self.counter = 0 + self.output = etree.Element("_") + + def append(self, item): + self.counter += 1 + e = etree.Element("a", + href="#footnote-anchor-%d" % self.counter, + id="footnote-%d" % self.counter, + style="float:left;margin-right:1em") + e.text = "[%d]" % self.counter + e.tail = " " + self.output.append(e) + self.output.extend(item) + anchor = etree.Element("a", + id="footnote-anchor-%d" % self.counter, + href="#footnote-%d" % self.counter) + anchor.text = "[%d]" % self.counter + return anchor + + +class TOC(object): + def __init__(self): + self.items = [] + self.counter = 0 + + def add(self, title, level=0): + self.counter += 1 + self.items.append((level, title, self.counter)) + return self.counter + + def render(self): + out = etree.Element("ul", id="toc") + curr_level = 0 + cursor = out + for level, title, counter in self.items: + while level > curr_level: + ins = etree.Element("ul") + cursor.append(ins) + cursor = ins + curr_level += 1 + while level < curr_level: + cursor = cursor.getparent() + curr_level -= 1 + ins = etree.Element("li") + ins.append(etree.Element("a", href="#sect%d" % counter)) + ins[0].text = title + cursor.append(ins) + return out + + +# Renderers + +HtmlFormat.renderers.register(core.Aside, None, NaturalText('aside')) + +class AsideFootnote(NaturalText): + def render(self, element, ctx): + output = super(AsideFootnote, self).render(element, ctx) + anchor = ctx.footnotes.append(output) + root, inner = self.container() + inner.append(anchor) + return root +HtmlFormat.renderers.register(core.Aside, 'footnote', AsideFootnote()) + + +HtmlFormat.renderers.register(core.Header, None, NaturalText('h1')) + + +HtmlFormat.renderers.register(core.Div, None, NaturalText('div')) +HtmlFormat.renderers.register(core.Div, 'item', NaturalText('li')) +HtmlFormat.renderers.register(core.Div, 'list', NaturalText('ul')) +HtmlFormat.renderers.register(core.Div, 'p', NaturalText('p')) + + +class Section(NaturalText): + def subcontext(self, element, ctx): + return Context(ctx, toc_level=ctx.toc_level + 1) + + def render(self, element, ctx): + counter = ctx.toc.add(element.meta.title(), ctx.toc_level) + root = super(Section, self).render(element, ctx) + root[0].set("id", "sect%d" % counter) + return root +HtmlFormat.renderers.register(core.Section, None, Section('section')) + + +HtmlFormat.renderers.register(core.Span, None, NaturalText('span')) +HtmlFormat.renderers.register(core.Span, 'cite', NaturalText('cite')) +HtmlFormat.renderers.register(core.Span, 'cite.code', LiteralText('code')) +HtmlFormat.renderers.register(core.Span, 'emph', NaturalText('em')) + +class SpanUri(LiteralText): + def render(self, element, ctx): + root = super(SpanUri, self).render(element, ctx) + root[0].attrib['href'] = element.text + return root +HtmlFormat.renderers.register(core.Span, 'uri', SpanUri('a')) diff --git a/librarian/formats/html/res/html.html b/librarian/formats/html/res/html.html new file mode 100644 index 0000000..a6e6314 --- /dev/null +++ b/librarian/formats/html/res/html.html @@ -0,0 +1,8 @@ +
+
+
+
+
+
+
+
diff --git a/librarian/formats/html/res/html_standalone.html b/librarian/formats/html/res/html_standalone.html new file mode 100644 index 0000000..a6b6213 --- /dev/null +++ b/librarian/formats/html/res/html_standalone.html @@ -0,0 +1,15 @@ + + + + + + + +
+
+
+
+
+
+ + diff --git a/librarian/functions.py b/librarian/functions.py deleted file mode 100644 index 523b3d5..0000000 --- a/librarian/functions.py +++ /dev/null @@ -1,106 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from lxml import etree -import re - -from librarian.dcparser import Person - -def _register_function(f): - """ Register extension function with lxml """ - ns = etree.FunctionNamespace('http://wolnelektury.pl/functions') - ns[f.__name__] = f - - -def reg_substitute_entities(): - ENTITY_SUBSTITUTIONS = [ - (u'---', u'—'), - (u'--', u'–'), - (u'...', u'…'), - (u',,', u'„'), - (u'"', u'”'), - ] - - def substitute_entities(context, text): - """XPath extension function converting all entites in passed text.""" - if isinstance(text, list): - text = ''.join(text) - for entity, substitutution in ENTITY_SUBSTITUTIONS: - text = text.replace(entity, substitutution) - return text - - _register_function(substitute_entities) - - -def reg_strip(): - def strip(context, text): - """Remove unneeded whitespace from beginning and end""" - if isinstance(text, list): - text = ''.join(text) - return re.sub(r'\s+', ' ', text).strip() - _register_function(strip) - - -def reg_starts_white(): - def starts_white(context, text): - if isinstance(text, list): - text = ''.join(text) - if not text: - return False - return text[0].isspace() - _register_function(starts_white) - - -def reg_ends_white(): - def ends_white(context, text): - if isinstance(text, list): - text = ''.join(text) - if not text: - return False - return text[-1].isspace() - _register_function(ends_white) - - -def reg_wrap_words(): - def wrap_words(context, text, wrapping): - """XPath extension function automatically wrapping words in passed text""" - if isinstance(text, list): - text = ''.join(text) - if not wrapping: - return text - - words = re.split(r'\s', text) - - line_length = 0 - lines = [[]] - for word in words: - line_length += len(word) + 1 - if line_length > wrapping: - # Max line length was exceeded. We create new line - lines.append([]) - line_length = len(word) - lines[-1].append(word) - return '\n'.join(' '.join(line) for line in lines) - _register_function(wrap_words) - - -def reg_person_name(): - def person_name(context, text): - """ Converts "Name, Forename" to "Forename Name" """ - if isinstance(text, list): - text = ''.join(text) - return Person.from_text(text).readable() - _register_function(person_name) - - -def reg_texcommand(): - def texcommand(context, text): - """Remove non-letters""" - if isinstance(text, list): - text = ''.join(text) - return re.sub(r'[^a-zA-Z]', '', text).strip() - _register_function(texcommand) - - diff --git a/librarian/html.py b/librarian/html.py deleted file mode 100644 index c1a5e5b..0000000 --- a/librarian/html.py +++ /dev/null @@ -1,279 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import os -import cStringIO -import copy - -from lxml import etree -from librarian import XHTMLNS, ParseError, OutputFile -from librarian import functions - -from lxml.etree import XMLSyntaxError, XSLTApplyError - -functions.reg_substitute_entities() -functions.reg_person_name() - -STYLESHEETS = { - 'legacy': 'xslt/book2html.xslt', - 'full': 'xslt/wl2html_full.xslt', - 'partial': 'xslt/wl2html_partial.xslt' -} - -def get_stylesheet(name): - return os.path.join(os.path.dirname(__file__), STYLESHEETS[name]) - -def html_has_content(text): - return etree.ETXPath('//p|//{%(ns)s}p|//h1|//{%(ns)s}h1' % {'ns': str(XHTMLNS)})(text) - -def transform(wldoc, stylesheet='legacy', options=None, flags=None): - """Transforms the WL document to XHTML. - - If output_filename is None, returns an XML, - otherwise returns True if file has been written,False if it hasn't. - File won't be written if it has no content. - """ - # Parse XSLT - try: - style_filename = get_stylesheet(stylesheet) - style = etree.parse(style_filename) - - document = copy.deepcopy(wldoc) - del wldoc - document.swap_endlines() - - if flags: - for flag in flags: - document.edoc.getroot().set(flag, 'yes') - - document.clean_ed_note() - - if not options: - options = {} - result = document.transform(style, **options) - del document # no longer needed large object :) - - if html_has_content(result): - add_anchors(result.getroot()) - add_table_of_contents(result.getroot()) - - return OutputFile.from_string(etree.tostring(result, method='html', - xml_declaration=False, pretty_print=True, encoding='utf-8')) - else: - return None - except KeyError: - raise ValueError("'%s' is not a valid stylesheet.") - except (XMLSyntaxError, XSLTApplyError), e: - raise ParseError(e) - -class Fragment(object): - def __init__(self, id, themes): - super(Fragment, self).__init__() - self.id = id - self.themes = themes - self.events = [] - - def append(self, event, element): - self.events.append((event, element)) - - def closed_events(self): - stack = [] - for event, element in self.events: - if event == 'start': - stack.append(('end', element)) - elif event == 'end': - try: - stack.pop() - except IndexError: - print 'CLOSED NON-OPEN TAG:', element - - stack.reverse() - return self.events + stack - - def to_string(self): - result = [] - for event, element in self.closed_events(): - if event == 'start': - result.append(u'<%s %s>' % (element.tag, ' '.join('%s="%s"' % (k, v) for k, v in element.attrib.items()))) - if element.text: - result.append(element.text) - elif event == 'end': - result.append(u'' % element.tag) - if element.tail: - result.append(element.tail) - else: - result.append(element) - - return ''.join(result) - - def __unicode__(self): - return self.to_string() - - -def extract_fragments(input_filename): - """Extracts theme fragments from input_filename.""" - open_fragments = {} - closed_fragments = {} - - # iterparse would die on a HTML document - parser = etree.HTMLParser(encoding='utf-8') - buf = cStringIO.StringIO() - buf.write(etree.tostring(etree.parse(input_filename, parser).getroot()[0][0], encoding='utf-8')) - buf.seek(0) - - for event, element in etree.iterparse(buf, events=('start', 'end')): - # Process begin and end elements - if element.get('class', '') in ('theme-begin', 'theme-end'): - if not event == 'end': continue # Process elements only once, on end event - - # Open new fragment - if element.get('class', '') == 'theme-begin': - fragment = Fragment(id=element.get('fid'), themes=element.text) - - # Append parents - if element.getparent().get('id', None) != 'book-text': - parents = [element.getparent()] - while parents[-1].getparent().get('id', None) != 'book-text': - parents.append(parents[-1].getparent()) - - parents.reverse() - for parent in parents: - fragment.append('start', parent) - - open_fragments[fragment.id] = fragment - - # Close existing fragment - else: - try: - fragment = open_fragments[element.get('fid')] - except KeyError: - print '%s:closed not open fragment #%s' % (input_filename, element.get('fid')) - else: - closed_fragments[fragment.id] = fragment - del open_fragments[fragment.id] - - # Append element tail to lost_text (we don't want to lose any text) - if element.tail: - for fragment_id in open_fragments: - open_fragments[fragment_id].append('text', element.tail) - - - # Process all elements except begin and end - else: - # Omit annotation tags - if (len(element.get('name', '')) or - element.get('class', '') in ('annotation', 'anchor')): - if event == 'end' and element.tail: - for fragment_id in open_fragments: - open_fragments[fragment_id].append('text', element.tail) - else: - for fragment_id in open_fragments: - open_fragments[fragment_id].append(event, copy.copy(element)) - - return closed_fragments, open_fragments - - -def add_anchor(element, prefix, with_link=True, with_target=True, link_text=None): - if with_link: - if link_text is None: - link_text = prefix - anchor = etree.Element('a', href='#%s' % prefix) - anchor.set('class', 'anchor') - anchor.text = unicode(link_text) - if element.text: - anchor.tail = element.text - element.text = u'' - element.insert(0, anchor) - - if with_target: - anchor_target = etree.Element('a', name='%s' % prefix) - anchor_target.set('class', 'target') - anchor_target.text = u' ' - if element.text: - anchor_target.tail = element.text - element.text = u'' - element.insert(0, anchor_target) - - -def any_ancestor(element, test): - for ancestor in element.iterancestors(): - if test(ancestor): - return True - return False - - -def add_anchors(root): - counter = 1 - for element in root.iterdescendants(): - if any_ancestor(element, lambda e: e.get('class') in ('note', 'motto', 'motto_podpis', 'dedication') - or e.get('id') == 'nota_red' - or e.tag == 'blockquote'): - continue - - if element.tag == 'p' and 'verse' in element.get('class', ''): - if counter == 1 or counter % 5 == 0: - add_anchor(element, "f%d" % counter, link_text=counter) - counter += 1 - elif 'paragraph' in element.get('class', ''): - add_anchor(element, "f%d" % counter, link_text=counter) - counter += 1 - - -def raw_printable_text(element): - working = copy.deepcopy(element) - for e in working.findall('a'): - if e.get('class') == 'annotation': - e.text = '' - return etree.tostring(working, method='text', encoding=unicode).strip() - - -def add_table_of_contents(root): - sections = [] - counter = 1 - for element in root.iterdescendants(): - if element.tag in ('h2', 'h3'): - if any_ancestor(element, lambda e: e.get('id') in ('footnotes', 'nota_red') or e.get('class') in ('person-list',)): - continue - - element_text = raw_printable_text(element) - if element.tag == 'h3' and len(sections) and sections[-1][1] == 'h2': - sections[-1][3].append((counter, element.tag, element_text, [])) - else: - sections.append((counter, element.tag, element_text, [])) - add_anchor(element, "s%d" % counter, with_link=False) - counter += 1 - - toc = etree.Element('div') - toc.set('id', 'toc') - toc_header = etree.SubElement(toc, 'h2') - toc_header.text = u'Spis treści' - toc_list = etree.SubElement(toc, 'ol') - - for n, section, text, subsections in sections: - section_element = etree.SubElement(toc_list, 'li') - add_anchor(section_element, "s%d" % n, with_target=False, link_text=text) - - if len(subsections): - subsection_list = etree.SubElement(section_element, 'ol') - for n, subsection, text, _ in subsections: - subsection_element = etree.SubElement(subsection_list, 'li') - add_anchor(subsection_element, "s%d" % n, with_target=False, link_text=text) - - root.insert(0, toc) - - -def extract_annotations(html_path): - """For each annotation, yields a tuple: anchor, text, html.""" - parser = etree.HTMLParser(encoding='utf-8') - tree = etree.parse(html_path, parser) - footnotes = tree.find('//*[@id="footnotes"]') - if footnotes is not None: - for footnote in footnotes.findall('div'): - anchor = footnote.find('a[@name]').get('name') - del footnote[:2] - text_str = etree.tostring(footnote, method='text', encoding='utf-8').strip() - html_str = etree.tostring(footnote, method='html', encoding='utf-8') - yield anchor, text_str, html_str - diff --git a/librarian/meta.py b/librarian/meta.py new file mode 100755 index 0000000..5b50d92 --- /dev/null +++ b/librarian/meta.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from lxml import etree +from librarian import DCNS, SSTNS + + +def text_value(meta): + """ Finds out the text value of metadata element. + + >>> p = Person() + >>> p.text = u"Czajka, Radek" + >>> print text_value(p) + Radek Czajka + + """ + if hasattr(meta, 'text_value'): + return meta.text_value() + else: + return meta.text + + +class Metadata(etree.ElementBase): + @classmethod + def about(cls, element): + meta = cls() + meta._about = element + return meta + + def get_about(self): + if hasattr(self, '_about'): + return self._about + else: + return self.getparent() + + def get(self, key, inherit=True): + """ Finds metadata by its element name. """ + values = self.findall(key) + if values: + return [text_value(v) for v in values] + elif inherit and self.get_about().getparent() is not None: + return self.get_about().getparent().meta.get(key) + elif inherit and hasattr(self.get_about(), 'meta_context'): + return self.get_about().meta_context.get(key) + else: + return [] + + def get_one(self, *args, **kwargs): + values = self.get(*args, **kwargs) + if values: + return values[0] + else: + return None + + + # Specials. + + def author(self): + try: + return unicode(self.get(DCNS('creator'))[0]) + except IndexError: + return u"" + + def slug(self): + try: + return self.get(DCNS('identifier'))[0].slug() + except IndexError: + return None + + def title(self): + dc_title = self.get(DCNS('title'), inherit=False) + if dc_title: + return unicode(dc_title[0]) + else: + header = self.get_about().find(SSTNS('header')) + if header is not None: + # FIXME: This should be a simple text representation + return header.text + else: + return u"" + + +class MetaItem(etree.ElementBase): + pass + + +class Person(MetaItem): + def text_value(self): + return u" ".join(p.strip() for p in reversed(self.text.rsplit(u',', 1))) + + +class Identifier(MetaItem): + def slug(self): + return self.text.rstrip('/').rsplit('/', 1)[-1] diff --git a/librarian/output.py b/librarian/output.py new file mode 100755 index 0000000..a11f697 --- /dev/null +++ b/librarian/output.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +import os +import shutil + + +class OutputFile(object): + """Represents a file returned by one of the converters.""" + + _string = None + _filename = None + + def __del__(self): + if self._filename: + os.unlink(self._filename) + + def __nonzero__(self): + return self._string is not None or self._filename is not None + + @classmethod + def from_string(cls, string): + """Converter returns contents of a file as a string.""" + + instance = cls() + instance._string = string + return instance + + @classmethod + def from_filename(cls, filename): + """Converter returns contents of a file as a named file.""" + + instance = cls() + instance._filename = filename + return instance + + def get_string(self): + """Get file's contents as a string.""" + + if self._filename is not None: + with open(self._filename) as f: + return f.read() + else: + return self._string + + def get_file(self): + """Get file as a file-like object.""" + + if self._string is not None: + from StringIO import StringIO + return StringIO(self._string) + elif self._filename is not None: + return open(self._filename) + + def get_filename(self): + """Get file as a fs path.""" + + if self._filename is not None: + return self._filename + elif self._string is not None: + from tempfile import NamedTemporaryFile + temp = NamedTemporaryFile(prefix='librarian-', delete=False) + temp.write(self._string) + temp.close() + self._filename = temp.name + return self._filename + else: + return None + + def save_as(self, path): + """Save file to a path. Create directories, if necessary.""" + + dirname = os.path.dirname(os.path.abspath(path)) + if not os.path.isdir(dirname): + os.makedirs(dirname) + shutil.copy(self.get_filename(), path) diff --git a/librarian/parser.py b/librarian/parser.py index a9e8c65..a0b8a7f 100644 --- a/librarian/parser.py +++ b/librarian/parser.py @@ -3,226 +3,28 @@ # This file is part of Librarian, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # -from librarian import ValidationError, NoDublinCore, ParseError, NoProvider -from librarian import RDFNS -from librarian.cover import WLCover -from librarian import dcparser - -from xml.parsers.expat import ExpatError from lxml import etree -from lxml.etree import XMLSyntaxError, XSLTApplyError - -import os -import re -from StringIO import StringIO - -class WLDocument(object): - LINE_SWAP_EXPR = re.compile(r'/\s', re.MULTILINE | re.UNICODE) - provider = None - - def __init__(self, edoc, parse_dublincore=True, provider=None, - strict=False, meta_fallbacks=None): - self.edoc = edoc - self.provider = provider - - root_elem = edoc.getroot() - - dc_path = './/' + RDFNS('RDF') - - if root_elem.tag != 'utwor': - raise ValidationError("Invalid root element. Found '%s', should be 'utwor'" % root_elem.tag) - - if parse_dublincore: - self.rdf_elem = root_elem.find(dc_path) - - if self.rdf_elem is None: - raise NoDublinCore('Document has no DublinCore - which is required.') - - self.book_info = dcparser.BookInfo.from_element( - self.rdf_elem, fallbacks=meta_fallbacks, strict=strict) - else: - self.book_info = None - - @classmethod - def from_string(cls, xml, *args, **kwargs): - return cls.from_file(StringIO(xml), *args, **kwargs) - - @classmethod - def from_file(cls, xmlfile, *args, **kwargs): - - # first, prepare for parsing - if isinstance(xmlfile, basestring): - file = open(xmlfile, 'rb') - try: - data = file.read() - finally: - file.close() - else: - data = xmlfile.read() - - if not isinstance(data, unicode): - data = data.decode('utf-8') - - data = data.replace(u'\ufeff', '') - - try: - parser = etree.XMLParser(remove_blank_text=False) - tree = etree.parse(StringIO(data.encode('utf-8')), parser) - - return cls(tree, *args, **kwargs) - except (ExpatError, XMLSyntaxError, XSLTApplyError), e: - raise ParseError(e) - - def swap_endlines(self): - """Converts line breaks in stanzas into
tags.""" - # only swap inside stanzas - for elem in self.edoc.iter('strofa'): - for child in list(elem): - if child.tail: - chunks = self.LINE_SWAP_EXPR.split(child.tail) - ins_index = elem.index(child) + 1 - while len(chunks) > 1: - ins = etree.Element('br') - ins.tail = chunks.pop() - elem.insert(ins_index, ins) - child.tail = chunks.pop(0) - if elem.text: - chunks = self.LINE_SWAP_EXPR.split(elem.text) - while len(chunks) > 1: - ins = etree.Element('br') - ins.tail = chunks.pop() - elem.insert(0, ins) - elem.text = chunks.pop(0) - - def parts(self): - if self.provider is None: - raise NoProvider('No document provider supplied.') - if self.book_info is None: - raise NoDublinCore('No Dublin Core in document.') - for part_uri in self.book_info.parts: - yield self.from_file(self.provider.by_uri(part_uri), - provider=self.provider) - - def chunk(self, path): - # convert the path to XPath - expr = self.path_to_xpath(path) - elems = self.edoc.xpath(expr) - - if len(elems) == 0: - return None - else: - return elems[0] - - def path_to_xpath(self, path): - parts = [] - - for part in path.split('/'): - match = re.match(r'([^\[]+)\[(\d+)\]', part) - if not match: - parts.append(part) - else: - tag, n = match.groups() - parts.append("*[%d][name() = '%s']" % (int(n)+1, tag) ) - - if parts[0] == '.': - parts[0] = '' - - return '/'.join(parts) - - def transform(self, stylesheet, **options): - return self.edoc.xslt(stylesheet, **options) - - def update_dc(self): - if self.book_info: - parent = self.rdf_elem.getparent() - parent.replace( self.rdf_elem, self.book_info.to_etree(parent) ) - - def serialize(self): - self.update_dc() - return etree.tostring(self.edoc, encoding=unicode, pretty_print=True) - - def merge_chunks(self, chunk_dict): - unmerged = [] - - for key, data in chunk_dict.iteritems(): - try: - xpath = self.path_to_xpath(key) - node = self.edoc.xpath(xpath)[0] - repl = etree.fromstring(u"<%s>%s" %(node.tag, data, node.tag) ) - node.getparent().replace(node, repl) - except Exception, e: - unmerged.append( repr( (key, xpath, e) ) ) - - return unmerged - - def clean_ed_note(self): - """ deletes forbidden tags from nota_red """ - - for node in self.edoc.xpath('|'.join('//nota_red//%s' % tag for tag in - ('pa', 'pe', 'pr', 'pt', 'begin', 'end', 'motyw'))): - tail = node.tail - node.clear() - node.tag = 'span' - node.tail = tail - - def editors(self): - """Returns a set of all editors for book and its children. - - :returns: set of dcparser.Person objects - """ - if self.book_info is None: - raise NoDublinCore('No Dublin Core in document.') - persons = set(self.book_info.editors + - self.book_info.technical_editors) - for child in self.parts(): - persons.update(child.editors()) - if None in persons: - persons.remove(None) - return persons - - # Converters - - def as_html(self, *args, **kwargs): - from librarian import html - return html.transform(self, *args, **kwargs) - - def as_text(self, *args, **kwargs): - from librarian import text - return text.transform(self, *args, **kwargs) - - def as_epub(self, *args, **kwargs): - from librarian import epub - return epub.transform(self, *args, **kwargs) - - def as_pdf(self, *args, **kwargs): - from librarian import pdf - return pdf.transform(self, *args, **kwargs) - - def as_mobi(self, *args, **kwargs): - from librarian import mobi - return mobi.transform(self, *args, **kwargs) - - def as_fb2(self, *args, **kwargs): - from librarian import fb2 - return fb2.transform(self, *args, **kwargs) - - def as_cover(self, cover_class=None, *args, **kwargs): - if cover_class is None: - cover_class = WLCover - return cover_class(self.book_info, *args, **kwargs).output_file() - - def save_output_file(self, output_file, output_path=None, - output_dir_path=None, make_author_dir=False, ext=None): - if output_dir_path: - save_path = output_dir_path - if make_author_dir: - save_path = os.path.join(save_path, - unicode(self.book_info.author).encode('utf-8')) - save_path = os.path.join(save_path, - self.book_info.uri.slug) - if ext: - save_path += '.%s' % ext - else: - save_path = output_path - - output_file.save_as(save_path) +from . import DCNS, SSTNS +from . import core, meta + + +class SSTParser(etree.XMLParser): + """ XML parser using relevant element classes. """ + def __init__(self): + super(SSTParser, self).__init__(remove_blank_text=False) + lookup = etree.ElementNamespaceClassLookup() + self.set_element_class_lookup(lookup) + + # Define core language tags. + sst_ns = lookup.get_namespace(SSTNS.uri) + sst_ns['aside'] = core.Aside + sst_ns['div'] = core.Div + sst_ns['header'] = core.Header + sst_ns['section'] = core.Section + sst_ns['span'] = core.Span + sst_ns['metadata'] = meta.Metadata + + # Define any special metadata. + dc_ns = lookup.get_namespace(DCNS.uri) + dc_ns['creator'] = meta.Person + dc_ns['identifier'] = meta.Identifier diff --git a/librarian/picture.py b/librarian/picture.py deleted file mode 100644 index ee3c61d..0000000 --- a/librarian/picture.py +++ /dev/null @@ -1,173 +0,0 @@ - -from dcparser import (as_person, as_date, Field, WorkInfo, DCNS) -from librarian import (RDFNS, ValidationError, NoDublinCore, ParseError, WLURI) -from xml.parsers.expat import ExpatError -from os import path -from StringIO import StringIO -from lxml import etree -from lxml.etree import (XMLSyntaxError, XSLTApplyError) -import re - - -class WLPictureURI(WLURI): - _re_wl_uri = re.compile('http://wolnelektury.pl/katalog/obraz/' - '(?P[-a-z0-9]+)/?$') - - @classmethod - def from_slug(cls, slug): - uri = 'http://wolnelektury.pl/katalog/obraz/%s/' % slug - return cls(uri) - -def as_wlpictureuri_strict(text): - return WLPictureURI.strict(text) - - -class PictureInfo(WorkInfo): - """ - Dublin core metadata for a picture - """ - FIELDS = ( - Field(DCNS('language'), 'language', required=False), - Field(DCNS('subject.period'), 'epochs', salias='epoch', multiple=True), - Field(DCNS('subject.type'), 'kinds', salias='kind', multiple=True), - - Field(DCNS('format.dimensions'), 'dimensions', required=False), - Field(DCNS('format.checksum.sha1'), 'sha1', required=True), - Field(DCNS('description.medium'), 'medium', required=False), - Field(DCNS('description.dimensions'), 'original_dimensions', required=False), - Field(DCNS('format'), 'mime_type', required=False), - Field(DCNS('identifier.url'), 'url', WLPictureURI, - strict=as_wlpictureuri_strict), - ) - - -class ImageStore(object): - EXT = ['gif', 'jpeg', 'png', 'swf', 'psd', 'bmp' - 'tiff', 'tiff', 'jpc', 'jp2', 'jpf', 'jb2', 'swc', - 'aiff', 'wbmp', 'xbm'] - MIME = ['image/gif', 'image/jpeg', 'image/png', - 'application/x-shockwave-flash', 'image/psd', 'image/bmp', - 'image/tiff', 'image/tiff', 'application/octet-stream', - 'image/jp2', 'application/octet-stream', 'application/octet-stream', - 'application/x-shockwave-flash', 'image/iff', 'image/vnd.wap.wbmp', 'image/xbm'] - - def __init__(self, dir_): - self.dir = dir_ - return super(ImageStore, self).__init__() - - def path(self, slug, mime_type): - """ - Finds file by slug and mime type in our iamge store. - Returns a file objects (perhaps should return a filename?) - """ - try: - i = self.MIME.index(mime_type) - except ValueError: - err = ValueError("Picture %s has unknown mime type: %s" % (slug, mime_type)) - err.slug = slug - err.mime_type = mime_type - raise err - ext = self.EXT[i] - # add some common extensions tiff->tif, jpeg->jpg - return path.join(self.dir, slug + '.' + ext) - - -class WLPicture(object): - def __init__(self, edoc, parse_dublincore=True, image_store=None): - self.edoc = edoc - self.image_store = image_store - - root_elem = edoc.getroot() - - dc_path = './/' + RDFNS('RDF') - - if root_elem.tag != 'picture': - raise ValidationError("Invalid root element. Found '%s', should be 'picture'" % root_elem.tag) - - if parse_dublincore: - self.rdf_elem = root_elem.find(dc_path) - - if self.rdf_elem is None: - raise NoDublinCore('Document has no DublinCore - which is required.') - - self.picture_info = PictureInfo.from_element(self.rdf_elem) - else: - self.picture_info = None - - @classmethod - def from_string(cls, xml, *args, **kwargs): - return cls.from_file(StringIO(xml), *args, **kwargs) - - @classmethod - def from_file(cls, xmlfile, parse_dublincore=True, image_store=None): - - # first, prepare for parsing - if isinstance(xmlfile, basestring): - file = open(xmlfile, 'rb') - try: - data = file.read() - finally: - file.close() - else: - data = xmlfile.read() - - if not isinstance(data, unicode): - data = data.decode('utf-8') - - data = data.replace(u'\ufeff', '') - - # assume images are in the same directory - if image_store is None and xmlfile.name is not None: - image_store = ImageStore(path.dirname(xmlfile.name)) - - try: - parser = etree.XMLParser(remove_blank_text=False) - tree = etree.parse(StringIO(data.encode('utf-8')), parser) - - return cls(tree, parse_dublincore=parse_dublincore, image_store=image_store) - except (ExpatError, XMLSyntaxError, XSLTApplyError), e: - raise ParseError(e) - - @property - def mime_type(self): - if self.picture_info is None: - raise ValueError('DC is not loaded, hence we don\'t know the image type') - return self.picture_info.mime_type - - @property - def slug(self): - return self.picture_info.url.slug - - @property - def image_path(self): - if self.image_store is None: - raise ValueError("No image store associated with whis WLPicture.") - return self.image_store.path(self.slug, self.mime_type) - - def image_file(self, *args, **kwargs): - return open(self.image_path, *args, **kwargs) - - def partiter(self): - """ - Iterates the parts of this picture and returns them and their metadata - """ - for part in self.edoc.iter("div"): - pd = {} - pd['type'] = part.get('type') - if pd['type'] == 'area': - pd['coords'] = ((int(part.get('x1')), int(part.get('y1'))), - (int(part.get('x2')), int(part.get('y2')))) - - pd['themes'] = [] - pd['object'] = None - parent = part - while True: - parent = parent.getparent() - if parent is None: - break - if parent.tag == 'sem': - if parent.get('type') == 'theme': - pd['themes'] += map(unicode.strip, unicode(parent.get('theme')).split(',')) - elif parent.get('type') == 'object' and pd['object'] is None: - pd['object'] = parent.get('object') - yield pd diff --git a/librarian/renderers.py b/librarian/renderers.py new file mode 100755 index 0000000..59ed8a4 --- /dev/null +++ b/librarian/renderers.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from lxml import etree +from . import UnicodeException +from .utils import extend_element + + +class UnknownElement(UnicodeException): + pass + + +class Renderer(object): + """ Renders an element in a context to some kind of container. """ + def render(self, element, ctx): + """ Renders the element in the context. """ + raise NotImplemented + + def render_text(self, text, ctx): + """ Renders the text in the context. """ + raise NotImplemented + + +class TreeRenderer(Renderer): + """ Renders insides as XML in a <_/> container. """ + root_name = "_" + + def __init__(self, tag_name=None, attrib=None): + self.tag_name = tag_name + self.attrib = attrib or {} + + def container(self): + root = etree.Element(self.root_name) + if self.tag_name: + inner = etree.Element(self.tag_name, **self.attrib) + root.append(inner) + return root, inner + else: + return root, root + + def text_container(self): + root = etree.Element(self.root_name) + return root, root + + def subcontext(self, element, ctx): + return ctx + + def get_insides(self, element, ctx): + subctx = self.subcontext(element, ctx) + if element.text: + yield self.render_text(element.text, ctx) + for child in element: + try: + yield ctx.format.render(child, subctx) + except UnknownElement: + pass + if child.tail: + yield self.render_text(child.tail, ctx) + + def render(self, element, ctx): + root, inner = self.container() + for inside in self.get_insides(element, ctx): + extend_element(inner, inside) + return root + + def render_text(self, text, ctx): + root, inner = self.text_container() + inner.text = text + return root + + + +class Register(object): + """ Class-renderer register. + + >>> from librarian.core import Div + >>> renderer = Renderer() + >>> reg = Register() + >>> reg.register(Div, 'a.b', renderer) + >>> reg.get(Div, 'a.b.c') is renderer + True + + """ + def __init__(self): + self.classes = {} + + def register(self, tag, klass, renderer): + self.classes[tag, klass] = renderer + + def get(self, tag, klass=None): + while klass: + try: + return self.classes[tag, klass] + except KeyError: + try: + klass = klass.rsplit('.', 1)[-2] + except IndexError: + klass = None + try: + return self.classes[tag, None] + except KeyError: + raise UnknownElement(tag) + + def get_for(self, element): + return self.get(type(element), element.get('class')) diff --git a/librarian/utils.py b/librarian/utils.py new file mode 100755 index 0000000..25936bf --- /dev/null +++ b/librarian/utils.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +import os + + +class Context(object): + """ Processing context. + + >>> ctx = Context(a=1) + >>> subctx = Context(ctx, a=2) + >>> ctx.b = 3 + >>> print subctx.a, subctx.b + 2 3 + + """ + def __init__(self, _upctx=None, **initial): + object.__setattr__(self, '_upctx', _upctx) + object.__setattr__(self, '_data', initial or {}) + + def __getattr__(self, name): + if name in self._data: + return self._data[name] + elif self._upctx is not None: + return getattr(self._upctx, name) + else: + raise AttributeError + + def __setattr__(self, name, value): + try: + self.try_setattr(name, value) + except ValueError: + self._data[name] = value + + def try_setattr(self, name, value): + if name in self._data: + self._data[name] = value + elif self._upctx is not None: + self._upctx.try_setattr(name, value) + else: + raise ValueError + + +class XMLNamespace(object): + '''A handy structure to repsent names in an XML namespace.''' + def __init__(self, uri): + self.uri = uri + + def __call__(self, tag): + return '{%s}%s' % (self.uri, tag) + + def __contains__(self, tag): + return tag.startswith('{' + str(self) + '}') + + def __repr__(self): + return 'XMLNamespace(%r)' % self.uri + + def __str__(self): + return '%s' % self.uri + + +def extend_element(container, element=None, text=None): + """ Extends XML element with another one's contents. + + Differs from etree.Element.extend by taking the text into account. + + >>> from lxml import etree + >>> container = etree.fromstring("") + >>> element = etree.fromstring("<_>ac") + >>> extend_element(container, element) + >>> print etree.tostring(container) + ac + + """ + add_text = (text or "") + (element.text or "" if element is not None else "") + if add_text: + if len(container): + container[-1].tail = (container[-1].tail or "") + add_text + else: + container.text = (container.text or "") + add_text + if element is not None: + container.extend(element) + + +def get_resource(path): + return os.path.join(os.path.dirname(__file__), path) diff --git a/scripts/book2cover b/scripts/book2cover index 758ab0e..977096e 100755 --- a/scripts/book2cover +++ b/scripts/book2cover @@ -4,18 +4,14 @@ # This file is part of Librarian, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # -from StringIO import StringIO -from librarian import OutputFile from librarian.book2anything import Book2Anything, Option +from librarian.formats.cover.wolnelektury import WLCover class Book2Cover(Book2Anything): - format_name = "JPEG" - ext = "jpg" - uses_cover = True - cover_optional = False + format_cls = WLCover - transform_options = [ + format_options = [ Option('-W', '--width', action='store', type='int', dest='width', default=None, help='Set width.'), Option('-H', '--height', action='store', type='int', dest='height', default=None, @@ -25,10 +21,6 @@ class Book2Cover(Book2Anything): help='Add WL logo in white box.'), ] - @staticmethod - def transform(wldoc, cover, *args, **kwargs): - return wldoc.as_cover(cover_class=cover, *args, **kwargs) - if __name__ == '__main__': Book2Cover.run() diff --git a/scripts/book2epub b/scripts/book2epub index 01ca79a..4d061f0 100755 --- a/scripts/book2epub +++ b/scripts/book2epub @@ -5,19 +5,11 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # from librarian.book2anything import Book2Anything, Option +from librarian.formats.epub import EpubFormat class Book2Epub(Book2Anything): - format_name = "EPUB" - ext = "epub" - uses_cover = True - uses_provider = True - transform_flags = [ - Option('-w', '--working-copy', dest='working-copy', - action='store_true', default=False, - help='mark the output as a working copy') - ] - + format_cls = EpubFormat if __name__ == '__main__': Book2Epub.run() diff --git a/scripts/book2html b/scripts/book2html index 5d48eec..6c1e1c6 100755 --- a/scripts/book2html +++ b/scripts/book2html @@ -5,23 +5,17 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # from librarian.book2anything import Book2Anything, Option +from librarian.formats.html import HtmlFormat class Book2Html(Book2Anything): - format_name = "HTML" - ext = "html" - uses_cover = False - uses_provider = False - transform_flags = [ - Option('-r', '--raw', dest='full-page', + format_cls = HtmlFormat + + format_options = [ + Option('-r', '--raw', dest='standalone', action='store_false', default=True, help='output raw text for use in templates') ] - parser_args = [ - Option('-i', '--ignore-dublin-core', dest='parse_dublincore', - action='store_false', default=True, - help='don\'t try to parse dublin core metadata') - ] if __name__ == '__main__': diff --git a/setup.py b/setup.py index 51003ef..a0e4e53 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ # import os import os.path -from distutils.core import setup +from setuptools import setup, find_packages def whole_tree(prefix, path): files = [] @@ -21,18 +21,26 @@ def whole_tree(prefix, path): setup( name='librarian', - version='1.5.1', + version='2.0a', description='Converter from WolneLektury.pl XML-based language to XHTML, TXT and other formats', author="Marek Stępniowski", author_email='marek@stepniowski.com', maintainer='Radek Czajka', maintainer_email='radoslaw.czajka@nowoczesnapolska.org.pl', url='http://github.com/fnp/librarian', - packages=['librarian'], - package_data={'librarian': ['xslt/*.xslt', 'epub/*', 'mobi/*', 'pdf/*', 'fb2/*', 'fonts/*', 'res/*'] + - whole_tree(os.path.join(os.path.dirname(__file__), 'librarian'), 'font-optimizer')}, + packages=find_packages(), + package_data={ + 'librarian': ['xslt/*.xslt', 'epub/*', 'html/*', 'mobi/*', 'pdf/*', 'fb2/*', 'fonts/*', 'res/*'] + + whole_tree(os.path.join(os.path.dirname(__file__), 'librarian'), 'font-optimizer'), + 'librarian.formats.html': ['res/*'], + 'librarian.formats.epub': ['res/*'], + }, include_package_data=True, - install_requires=['lxml>=2.2'], + install_requires=[ + 'lxml>=2.2', + 'pillow', + 'Texml', + ], scripts=['scripts/book2html', 'scripts/book2txt', 'scripts/book2epub', -- 2.20.1 From 85b5bd4d4b6fdaff30df0dce136db850fe610830 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 2 May 2013 14:23:51 +0200 Subject: [PATCH 04/16] obvious epub toc fix --- librarian/book2anything.py | 1 - librarian/formats/epub/__init__.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/librarian/book2anything.py b/librarian/book2anything.py index b50cb1c..e46a4b4 100755 --- a/librarian/book2anything.py +++ b/librarian/book2anything.py @@ -4,7 +4,6 @@ # This file is part of Librarian, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # -from collections import namedtuple import os.path import optparse diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py index f9f7565..38778ac 100644 --- a/librarian/formats/epub/__init__.py +++ b/librarian/formats/epub/__init__.py @@ -230,7 +230,7 @@ class TOC(object): content.set('src', child.href) nav_point.append(content) nav_map.append(nav_point) - child.render(nav_map) + child.render(nav_point) # Renderers -- 2.20.1 From 3f64488684dbf14b7e0005209d2dca878e1852d9 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Mon, 7 Mar 2016 13:41:58 +0100 Subject: [PATCH 05/16] local changes from server --- a.py | 13 + librarian/document.py | 19 +- librarian/formats/html/__init__.py | 73 +++- librarian/formats/pdf/__init__.py | 381 +++++++++++++++++++ librarian/formats/pdf/res/TonyJericho.png | Bin 0 -> 331099 bytes librarian/formats/pdf/res/coverimage.sty | 23 ++ librarian/formats/pdf/res/default.sty | 33 ++ librarian/formats/pdf/res/insertimage.sty | 5 + librarian/formats/pdf/res/template.texml | 12 + librarian/formats/pdf/res/wl.cls | 426 +++++++++++++++++++++ librarian/formats/pdf/res/wl2tex.xslt | 441 ++++++++++++++++++++++ milpdf/a.xml | 4 + milpdf/m.xml | 151 ++++++++ milpdf/p.xml | 56 +++ 14 files changed, 1631 insertions(+), 6 deletions(-) create mode 100644 a.py create mode 100644 librarian/formats/pdf/__init__.py create mode 100644 librarian/formats/pdf/res/TonyJericho.png create mode 100755 librarian/formats/pdf/res/coverimage.sty create mode 100755 librarian/formats/pdf/res/default.sty create mode 100644 librarian/formats/pdf/res/insertimage.sty create mode 100644 librarian/formats/pdf/res/template.texml create mode 100755 librarian/formats/pdf/res/wl.cls create mode 100755 librarian/formats/pdf/res/wl2tex.xslt create mode 100644 milpdf/a.xml create mode 100644 milpdf/m.xml create mode 100644 milpdf/p.xml diff --git a/a.py b/a.py new file mode 100644 index 0000000..d5f4886 --- /dev/null +++ b/a.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +from librarian.document import Document as SST +from librarian.formats.pdf import PdfFormat +from librarian.utils import Context +import shutil + +sst = SST.from_file('milpdf/a.xml') +of = PdfFormat(sst).build(Context(), verbose=True) +shutil.copy(of.get_filename(), 'a.pdf') + + + diff --git a/librarian/document.py b/librarian/document.py index 32148e3..acc80ae 100755 --- a/librarian/document.py +++ b/librarian/document.py @@ -21,11 +21,22 @@ class Document(object): if not isinstance(root_elem, Section): if root_elem.tag != SSTNS('section'): - raise ValidationError("Invalid root element. Found '%s', should be '%s'" % ( - root_elem.tag, SSTNS('section'))) + if root_elem.tag == 'section': + for element in root_elem.iter(): + if element.tag in ('section', 'header', 'div', 'span', 'aside', 'metadata'): + element.tag = str(SSTNS(element.tag)) + + parser = SSTParser() + tree = etree.parse(StringIO(etree.tostring(root_elem)), parser) + tree.xinclude() + self.edoc = tree + else: + raise ValueError("Invalid root element. Found '%s', should be '%s'" % ( + root_elem.tag, SSTNS('section'))) else: - raise ValidationError("Invalid class of root element. " + raise ValueError("Invalid class of root element. " "Use librarian.parser.SSTParser.") + #print etree.tostring(self.edoc.getroot()) @classmethod def from_string(cls, xml, *args, **kwargs): @@ -47,6 +58,8 @@ class Document(object): data = data.decode('utf-8') data = data.replace(u'\ufeff', '') + # This is bad. The editor shouldn't spew unknown HTML entities. + data = data.replace(u' ', u'\u00a0') parser = SSTParser() tree = etree.parse(StringIO(data.encode('utf-8')), parser) diff --git a/librarian/formats/html/__init__.py b/librarian/formats/html/__init__.py index ddf2c78..2cf2601 100644 --- a/librarian/formats/html/__init__.py +++ b/librarian/formats/html/__init__.py @@ -22,7 +22,7 @@ class HtmlFormat(Format): super(HtmlFormat, self).__init__(doc) self.standalone = standalone - def build(self): + def build(self, files_path=None): if self.standalone: tmpl = get_resource("formats/html/res/html_standalone.html") else: @@ -30,6 +30,7 @@ class HtmlFormat(Format): t = etree.parse(tmpl) ctx = Context(format=self) + ctx.files_path = files_path ctx.toc = TOC() ctx.toc_level = 0 ctx.footnotes = Footnotes() @@ -39,7 +40,7 @@ class HtmlFormat(Format): t.find('.//div[@id="content"]').extend( self.render(self.doc.edoc.getroot(), ctx)) - t.find('.//div[@id="toc"]').append(ctx.toc.render()) + #t.find('.//div[@id="toc"]').append(ctx.toc.render()) t.find('.//div[@id="footnotes"]').extend(ctx.footnotes.output) return OutputFile.from_string(etree.tostring( @@ -67,6 +68,12 @@ class LiteralText(TreeRenderer): pass +class Silent(TreeRenderer): + def render_text(self, text, ctx): + root, inner = self.text_container() + return root + + class Footnotes(object): def __init__(self): self.counter = 0 @@ -122,6 +129,7 @@ class TOC(object): # Renderers HtmlFormat.renderers.register(core.Aside, None, NaturalText('aside')) +HtmlFormat.renderers.register(core.Aside, 'comment', Silent()) class AsideFootnote(NaturalText): def render(self, element, ctx): @@ -132,13 +140,60 @@ class AsideFootnote(NaturalText): return root HtmlFormat.renderers.register(core.Aside, 'footnote', AsideFootnote()) + +class Header(NaturalText): + def render(self, element, ctx): + root = super(Header, self).render(element, ctx) + if ctx.toc_level == 1: + d = etree.SubElement(root, 'div', {'class': "page-header"}) + d.insert(0, root[0]) + else: + root[0].tag = 'h2' + if root[0].text: + d = etree.SubElement(root[0], 'a', {'id': root[0].text, 'style': 'pointer: hand; color:#ddd; font-size:.8em'}) + #d.text = "per" + return root + -HtmlFormat.renderers.register(core.Header, None, NaturalText('h1')) +HtmlFormat.renderers.register(core.Header, None, Header('h1')) HtmlFormat.renderers.register(core.Div, None, NaturalText('div')) + +class DivDefined(NaturalText): + def render(self, element, ctx): + output = super(DivDefined, self).render(element, ctx) + output[0].text = (output[0].text or '') + ':' + output[0].attrib['id'] = output[0].text # not so cool? + return output + +HtmlFormat.renderers.register(core.Div, 'defined', DivDefined('dt', {'style': 'display: inline-block'})) + + +class DivImage(NaturalText): + def render(self, element, ctx): + output = super(DivImage, self).render(element, ctx) + src = element.attrib.get('src', '') + if src.startswith('file://'): + src = ctx.files_path + src[7:] + output[0].attrib['src'] = src + output[0].attrib['style'] = 'display: block; width: 60%; margin: 3em auto' + return output + +HtmlFormat.renderers.register(core.Div, 'img', DivImage('img')) + HtmlFormat.renderers.register(core.Div, 'item', NaturalText('li')) HtmlFormat.renderers.register(core.Div, 'list', NaturalText('ul')) +HtmlFormat.renderers.register(core.Div, 'list.enum', NaturalText('ol')) + +class DivListDefinitions(NaturalText): + def render(self, element, ctx): + output = super(DivListDefinitions, self).render(element, ctx) + #if ctx.toc_level > 2: + # output[0].attrib['style'] = 'float: right' + return output + +HtmlFormat.renderers.register(core.Div, 'list.definitions', DivListDefinitions('ul')) HtmlFormat.renderers.register(core.Div, 'p', NaturalText('p')) @@ -158,6 +213,7 @@ HtmlFormat.renderers.register(core.Span, None, NaturalText('span')) HtmlFormat.renderers.register(core.Span, 'cite', NaturalText('cite')) HtmlFormat.renderers.register(core.Span, 'cite.code', LiteralText('code')) HtmlFormat.renderers.register(core.Span, 'emph', NaturalText('em')) +HtmlFormat.renderers.register(core.Span, 'emp', NaturalText('strong')) class SpanUri(LiteralText): def render(self, element, ctx): @@ -165,3 +221,14 @@ class SpanUri(LiteralText): root[0].attrib['href'] = element.text return root HtmlFormat.renderers.register(core.Span, 'uri', SpanUri('a')) + +class SpanLink(LiteralText): + def render(self, element, ctx): + root = super(SpanLink, self).render(element, ctx) + src = element.attrib.get('href', '') + if src.startswith('file://'): + src = ctx.files_path + src[7:] + root[0].attrib['href'] = src + return root +HtmlFormat.renderers.register(core.Span, 'link', SpanLink('a')) + diff --git a/librarian/formats/pdf/__init__.py b/librarian/formats/pdf/__init__.py new file mode 100644 index 0000000..298db09 --- /dev/null +++ b/librarian/formats/pdf/__init__.py @@ -0,0 +1,381 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +import os +import re +import shutil +from subprocess import call, PIPE +from tempfile import NamedTemporaryFile, mkdtemp +from lxml import etree +from urllib import urlretrieve +from StringIO import StringIO +from Texml.processor import process +from librarian import DCNS, XMLNamespace +from librarian.formats import Format +from librarian.output import OutputFile +from librarian.renderers import Register, TreeRenderer +from librarian.utils import Context, get_resource, extend_element +from librarian import core +from PIL import Image +from ..html import Silent + + +TexmlNS = XMLNamespace('http://getfo.sourceforge.net/texml/ns1') + + +def texml_cmd(name, *parms, **kwargs): + cmd = etree.Element(TexmlNS('cmd'), name=name) + for opt in kwargs.get('opts', []): + etree.SubElement(cmd, TexmlNS('opt')).text = opt + for parm in parms: + etree.SubElement(cmd, TexmlNS('parm')).text = parm + return cmd + + +class PdfFormat(Format): + format_name = 'PDF' + format_ext = 'pdf' + tex_passes = 1 + style = get_resource('formats/pdf/res/default.sty') + + local_packages = [ + get_resource('formats/pdf/res/coverimage.sty'), + get_resource('formats/pdf/res/insertimage.sty'), + ] + + renderers = Register() + + def retrieve_file(self, url, save_as): + # TODO: local sheme + return False + + def add_file(self, ctx, filename, url=None, path=None, image=False): + from subprocess import call + assert url or path + save_as = os.path.join(ctx.workdir, filename) + if path is not None: + ext = path.rsplit('.', 1)[-1] + if image: + if ext == 'gif': + call(['convert', path, save_as]) + else: + # JPEGs with bad density will break LaTeX with 'Dimension too large'. + call(['convert', '-units', 'PixelsPerInch', path, '-density', '300', save_as + '_.' + ext]) + shutil.move(save_as + '_.' + ext, save_as) + else: + shutil.copy(path, save_as) + elif not self.retrieve_file(url, save_as): + if url.startswith('file://'): + url = ctx.files_path + url[7:] + + if url.startswith('/'): + url = 'http://milpeer.eu' + url + + ext = url.rsplit('.', 1)[-1] + if image: + urlretrieve(url, save_as + '_.' + ext) + if ext == 'gif': + call(['convert', save_as + '_.' + ext, save_as]) + else: + # JPEGs with bad density will break LaTeX with 'Dimension too large'. + r = call(['convert', '-units', 'PixelsPerInch', save_as + '_.' + ext, '-density', '300', save_as + '_2.' + ext]) + if r: + shutil.move(save_as + '_.' + ext, save_as) + else: + shutil.move(save_as + '_2.' + ext, save_as) + else: + urlretrieve(url, save_as) + + def get_file(self, ctx, filename): + return os.path.join(ctx.workdir, filename) + + def get_texml(self, build_ctx): + t = etree.Element(TexmlNS('TeXML')) + + self.add_file(build_ctx, 'wl.cls', path=get_resource('formats/pdf/res/wl.cls')) + t.append(texml_cmd("documentclass", "wl")) + + # global packages + self.add_file(build_ctx, 'style.sty', path=self.style) + t.append(texml_cmd("usepackage", "style")) + t.append(texml_cmd("usepackage", "hyphenat")) + + # local packages + for i, package in enumerate(self.local_packages): + self.add_file(build_ctx, "librarianlocalpackage%s.sty" % i, path=package) + t.append(texml_cmd("usepackage", "librarianlocalpackage%s" % i)) + + author = ", ". join(self.doc.meta.get(DCNS('creator')) or '') + title = self.doc.meta.title() + t.append(texml_cmd("author", author)) + t.append(texml_cmd("title", title)) + + doc = etree.SubElement(t, TexmlNS('env'), name="document") + doc.append(texml_cmd("thispagestyle", "empty")) + + # title page + height_left = 297 + cover_url = self.doc.meta.get_one(DCNS('relation.coverimage.url')) + if cover_url: + self.add_file(build_ctx, 'cover.png', cover_url, image=True) + + img = Image.open(self.get_file(build_ctx, 'cover.png')) + size = img.size + + if (size[1] > size[0]): + img = img.crop((0, 0, size[0], size[0])) + img.save(self.get_file(build_ctx, 'cover.png'), format=img.format, quality=90) + size = img.size + + # TODO: hardcoded paper size here + height = 210.0 * size[1] / size[0] + doc.append(texml_cmd("makecover", "%fmm" % height)) + else: + doc.append(texml_cmd("vfill*")) + + # Wielkości! + grp = etree.SubElement(doc, 'group') + grp.append(texml_cmd("raggedright")) + grp.append(texml_cmd("vfill")) + if author: + p = texml_cmd("par", "") + grp.append(p) + p[0].append(texml_cmd("Large")) + p[0].append(texml_cmd("noindent")) + p[0].append(texml_cmd("nohyphens", author)) + p[0].append(texml_cmd("vspace", "1em")) + #p[0][-1].tail = author + if title: + p = texml_cmd("par", "") + grp.append(p) + p[0].append(texml_cmd("Huge")) + p[0].append(texml_cmd("noindent")) + p[0].append(texml_cmd("nohyphens", title)) + #p[0][-1].tail = title + doc.append(texml_cmd("vfill")) + doc.append(texml_cmd("vfill")) + + # IOFile probably would be better + cover_logo_url = getattr(build_ctx, 'cover_logo', None) + # TEST + # TODO: convert + #cover_logo_url = 'http://milpeer.mdrn.pl/media/dynamic/people/logo/nowoczesnapolska.org.pl.png' + if cover_logo_url: + self.add_file(build_ctx, 'coverlogo.png', cover_logo_url, image=True) + size = Image.open(self.get_file(build_ctx, 'coverlogo.png')).size + p = texml_cmd("par", "") + doc.append(p) + p[0].append(texml_cmd("noindent")) + p[0].append(texml_cmd("insertimage", 'coverlogo.png', "%fcm" % (1.0 * size[0] / size[1]), "1cm")) + + # logo organizacji! + doc.append(texml_cmd("clearpage")) + + ctx = Context(build_ctx, format=self, img=1) + doc.extend(self.render(self.doc.edoc.getroot(), ctx)) + + # Redakcyjna na końcu. + doc.append(texml_cmd("clearpage")) + + doc.append(texml_cmd("section*", "Information about the resource")) + doc.append(texml_cmd("vspace", "1em")) + + for m, f in ( + ('Publisher: ', DCNS('publisher')), + ('Rights: ', DCNS('rights')), + ('Intended audience: ', DCNS('audience')), + ('', DCNS('description')), + ): + v = self.doc.meta.get_one(f) + if v: + e = texml_cmd("par", "") + e[0].append(texml_cmd("noindent")) + e[0][0].tail = "%s%s" % (m, v) + doc.append(e) + doc.append(texml_cmd("vspace", "1em")) + + + e = texml_cmd("par", "") + e[0].append(texml_cmd("noindent")) + e[0][0].tail = "Resource prepared using " + e[0].append(texml_cmd("href", "http://milpeer.eu", "MIL/PEER")) + e[0][-1].tail = " editing platform. " + doc.append(e) + + source_url = getattr(build_ctx, 'source_url', None) + #source_url = 'http://milpeer.mdrn.pl/documents/27/' + if source_url: + e = texml_cmd("par", "") + doc.append(e) + e[0].append(texml_cmd("noindent")) + e[0][0].tail = "Source available at " + e[0].append(texml_cmd("href", source_url, source_url)) + + return t + + def get_tex_dir(self, ctx): + ctx.workdir = mkdtemp('-wl2pdf') + texml = self.get_texml(ctx) + tex_path = os.path.join(ctx.workdir, 'doc.tex') + with open(tex_path, 'w') as fout: + #print etree.tostring(texml) + process(StringIO(etree.tostring(texml)), fout, 'utf-8') + + #~ if self.save_tex: + #~ shutil.copy(tex_path, self.save_tex) + + + + #for sfile in ['wasysym.sty', 'uwasyvar.fd', 'uwasy.fd']: + # shutil.copy(get_resource(os.path.join('res/wasysym', sfile)), temp) + return ctx.workdir + + def build(self, ctx=None, verbose=False): + temp = self.get_tex_dir(ctx) + tex_path = os.path.join(temp, 'doc.tex') + try: + cwd = os.getcwd() + except OSError: + cwd = None + os.chdir(temp) + + if verbose: + for i in range(self.tex_passes): + p = call(['xelatex', tex_path]) + else: + for i in range(self.tex_passes): + p = call(['xelatex', '-interaction=batchmode', tex_path], + stdout=PIPE, stderr=PIPE) + if p: + #raise ParseError("Error parsing .tex file: %s" % tex_path) + raise RuntimeError("Error parsing .tex file: %s" % tex_path) + + if cwd is not None: + os.chdir(cwd) + + output_file = NamedTemporaryFile(prefix='librarian', suffix='.pdf', delete=False) + pdf_path = os.path.join(temp, 'doc.pdf') + shutil.move(pdf_path, output_file.name) + shutil.rmtree(temp) + os.system("ls -l " + output_file.name) + return OutputFile.from_filename(output_file.name) + + def render(self, element, ctx): + return self.renderers.get_for(element).render(element, ctx) + + + + +class CmdRenderer(TreeRenderer): + def parms(self): + return [] + def container(self): + root = etree.Element(self.root_name) + root.append(texml_cmd(self.tag_name, *(self.parms() + [""]))) + inner = root[0][-1] + return root, inner + +class EnvRenderer(TreeRenderer): + def container(self): + root = etree.Element(self.root_name) + inner = etree.SubElement(root, 'env', name=self.tag_name) + return root, inner + +class GroupRenderer(CmdRenderer): + def container(self): + root = etree.Element(self.root_name) + inner = etree.SubElement(root, 'group') + if self.tag_name: + inner.append(texml_cmd(self.tag_name, *(self.parms() + [""]))) + return root, inner + + +class SectionRenderer(CmdRenderer): + def subcontext(self, element, ctx): + # here? + return Context(ctx, toc_level=getattr(ctx, 'toc_level', 1) + 2) + + def container(self): + root = etree.Element(self.root_name) + root.append(texml_cmd('pagebreak', opts=['1'])) + root.append(texml_cmd(self.tag_name, *(self.parms() + [""]))) + inner = root[1][0] + return root, inner + +PdfFormat.renderers.register(core.Section, None, SectionRenderer('par')) + +# TODO: stopnie +PdfFormat.renderers.register(core.Header, None, CmdRenderer('section*')) + +PdfFormat.renderers.register(core.Div, None, CmdRenderer('par')) + +class ImgRenderer(CmdRenderer): + def parms(self): + return ["", ""] + + def render(self, element, ctx): + root = super(ImgRenderer, self).render(element, ctx) + url = element.get('src') + nr = getattr(ctx, 'img', 0) + ctx.img = nr + 1 + ctx.format.add_file(ctx, 'f%d.png' % nr, url, image=True) + root[0][0].text = 'f%d.png' % nr + try: + size = Image.open(ctx.format.get_file(ctx, 'f%d.png' % nr)).size + except IOError: # not an image + del root[0]; + return root + root[0][1].text = '15cm' + root[0][2].text = '%fcm' % (15.0 * size[1] / size[0]) + return root + +PdfFormat.renderers.register(core.Div, 'img', ImgRenderer('insertimage')) + + +PdfFormat.renderers.register(core.Div, 'defined', CmdRenderer('textbf')) +PdfFormat.renderers.register(core.Div, 'item', CmdRenderer('item')) +PdfFormat.renderers.register(core.Div, 'list', EnvRenderer('itemize')) +PdfFormat.renderers.register(core.Div, 'list.enum', EnvRenderer('enumerate')) + + + +PdfFormat.renderers.register(core.Span, None, TreeRenderer()) +PdfFormat.renderers.register(core.Span, 'cite', CmdRenderer('emph')) +PdfFormat.renderers.register(core.Span, 'cite.code', CmdRenderer('texttt')) +PdfFormat.renderers.register(core.Span, 'emp', CmdRenderer('textbf')) +PdfFormat.renderers.register(core.Span, 'emph', CmdRenderer('emph')) + +class SpanUri(CmdRenderer): + def parms(self): + return [""] + def render(self, element, ctx): + root = super(SpanUri, self).render(element, ctx) + src = element.text + if src.startswith('file://'): + src = ctx.files_path + src[7:] + root[0][0].text = src + return root +PdfFormat.renderers.register(core.Span, 'uri', SpanUri('href')) + + +class SpanLink(CmdRenderer): + def parms(self): + return [""] + def render(self, element, ctx): + root = super(SpanLink, self).render(element, ctx) + src = element.attrib.get('href', '') + if src.startswith('file://'): + src = ctx.files_path + src[7:] + root[0][0].text = src + return root +PdfFormat.renderers.register(core.Span, 'link', SpanLink('href')) + + + + +PdfFormat.renderers.register(core.Aside, None, TreeRenderer()) +PdfFormat.renderers.register(core.Aside, 'editorial', CmdRenderer('editorialpage')) +PdfFormat.renderers.register(core.Aside, 'comment', Silent()) + diff --git a/librarian/formats/pdf/res/TonyJericho.png b/librarian/formats/pdf/res/TonyJericho.png new file mode 100644 index 0000000000000000000000000000000000000000..08089b619b4a3f444c8c8f8bdca80d878b9a7379 GIT binary patch literal 331099 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 z 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 + + + \documentclass{wl} + \usepackage{style} + \usepackage{coverimage} + + + + + + diff --git a/librarian/formats/pdf/res/wl.cls b/librarian/formats/pdf/res/wl.cls new file mode 100755 index 0000000..f336749 --- /dev/null +++ b/librarian/formats/pdf/res/wl.cls @@ -0,0 +1,426 @@ +% -*- coding: utf-8 -*- +\NeedsTeXFormat{LaTeX2e} +\ProvidesClass{wl}[2011/11/28 wolnelektury.pl book style] + +% PDF customizations +% +% nofootnotes - disable generation of footnotes +% nothemes - disable generation of themes +% defaultleading - default leading +% onehalfleading - leading of 1.5 (interlinia) +% doubleleading - double leading (interlinia) +% a4paper,... - paper size as required by LaTeX +% nowlfont - don't use customized WL font + +\RequirePackage{setspace} +\RequirePackage{type1cm} +%\RequirePackage{wasysym} +\DeclareOption{13pt}{% +\AtEndOfClass{% +% font size definitions, similar to ones in /usr/share/texmf-texlive/tex/latex/base/ +\renewcommand\normalsize{% + \@setfontsize\normalsize{13pt}{14.5pt}% + \abovedisplayskip 12\p@ \@plus3\p@ \@minus7\p@ + \abovedisplayshortskip \z@ \@plus3\p@ + \belowdisplayshortskip 6.5\p@ \@plus3.5\p@ \@minus3\p@ + \belowdisplayskip \abovedisplayskip + \let\@listi\@listI}\normalsize% +\renewcommand\footnotesize{% + \@setfontsize\footnotesize\@xpt\@xiipt + \abovedisplayskip 10\p@ \@plus2\p@ \@minus5\p@ + \abovedisplayshortskip \z@ \@plus3\p@ + \belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@ + \def\@listi{\leftmargin\leftmargini + \topsep 6\p@ \@plus2\p@ \@minus2\p@ + \parsep 3\p@ \@plus2\p@ \@minus\p@ + \itemsep \parsep}% + \belowdisplayskip \abovedisplayskip +}% +}% +} + +%% \DeclareOption{14pt}{\renewcommand{\normalsize}{\AtEndOfClass{\fontsize{14}{17}\selectfont}}} + +\DeclareOption{defaultleading}{} +\DeclareOption{doubleleading}{\AtBeginDocument{\doublespacing}}%\setlength{\leading}{1em plus 0.5ex minus 0.2ex}} +\DeclareOption{onehalfleading}{\AtBeginDocument{\onehalfspacing}}%\setlength{\leading}{1em plus 0.5ex minus 0.2ex}} + +%% This does not really work, since dvipdfm(x) will use it's configuration in /etc/texmf/dvipdfm(x) and force a global paper size setting. +\DeclareOption{a5paper}{% + \setlength{\paperheight}{210mm}% + \setlength{\paperwidth}{148mm}} + + +\newif\ifshowfootnotes \showfootnotestrue +\DeclareOption{nofootnotes}{\showfootnotesfalse} + +\newif\ifshowthemes \showthemestrue +\DeclareOption{nothemes}{\showthemesfalse} + +\newif\ifenablewlfont \enablewlfonttrue +\DeclareOption{nowlfont}{\enablewlfontfalse} + +\DeclareOption*{\PassOptionsToClass{\CurrentOption}{book}} +\ProcessOptions\relax +\LoadClass[a4paper,oneside]{book} + + +\usepackage{trace} + +\usepackage[xetex]{graphicx} +\usepackage{fontspec} +\usepackage{xunicode} +\usepackage{xltxtra} + +\usepackage[overload]{textcase} +\usepackage{scalefnt} +% TODO: link color is a style thing +\usepackage[hyphens]{url} +\usepackage[colorlinks=true,linkcolor=black,setpagesize=false,urlcolor=blue,xetex]{hyperref} + +\ifenablewlfont +\setmainfont [ +%ExternalLocation, +%UprightFont = Dosis-Regular, +UprightFont = JunicodeWL-Regular, +%ItalicFont = Dosis-SemiBold, +%BoldFont = Dosis-Bold, +%BoldItalicFont = Dosis-Bold, +%SmallCapsFont = Dosis-Regular, +ItalicFont = JunicodeWL-Italic, +BoldFont = Junicode-Bold, +%BoldFont = Junicode-Italic, +BoldItalicFont = Junicode-BoldItalic, +SmallCapsFont = JunicodeWL-Regular, +SmallCapsFeatures = {Letters={SmallCaps,UppercaseSmallCaps}}, +Numbers=OldStyle, +Scale=1.04, +LetterSpace=-1.0 +] {Junicode} + +%\newfontfamily\alien[ +%\setmainfont[ +%SmallCapsFeatures = {Letters={SmallCaps,UppercaseSmallCaps}}, +%Numbers=OldStyle, +%Scale=0.85, +%LetterSpace=-1.0 +%] {FreeSans} +%\setmainfont{FreeSans} + + +\defaultfontfeatures{ +SizeFeatures={ + {Size={-10.5}, FakeStretch=1.02, LetterSpace=2.0 }, + {Size={10.5-12}, FakeStretch=2.00, LetterSpace=0.0 }, + {Size={12-}, FakeStretch=0.98, LetterSpace=-2.0 } +} +} + +\renewcommand{\textsc}[1]{% +{\addfontfeature{ +SizeFeatures={ + {Size={-10.5}, Scale=1.2, FakeStretch=1.02, LetterSpace=8.0 }, + {Size={10.5-12}, Scale=1.2, FakeStretch=1.02, LetterSpace=8.0 }, + {Size={12-}, FakeStretch=1.0, LetterSpace=8.0 } +}, +Letters={SmallCaps,UppercaseSmallCaps} +} +#1} +} +\fi% enablewlfont + +%{\Itshape JunicodeWL-Italic.ttf } +%{\bfseries Junicode-Bold.ttf } +%{\bfseries\itshape Junicode-BoldItalic.ttf } + +\pagestyle{plain} +\usepackage{fancyhdr} + +\makeatletter + +% bottom figure below footnotes +\usepackage{fnpos} +\makeFNabove + +\usepackage{color} +\definecolor{theme}{gray}{.3} + + +\setlength{\marginparsep}{2em} +%\setlength{\marginparwidth}{8.5em} +\setlength{\marginparwidth}{2.5cm} +\setlength{\oddsidemargin}{0pt} +\setlength{\voffset}{0pt} +\setlength{\topmargin}{0pt} +\setlength{\headheight}{0pt} +\setlength{\headsep}{0pt} +\setlength{\textheight}{24cm} +\setlength{\textwidth}{16cm} + + +\pagestyle{fancy} +\fancyhf{} +\renewcommand{\headrulewidth}{0pt} +\renewcommand{\footrulewidth}{0pt} +\lfoot{{\footnotesize \@author{} \emph{\@title}}} +\cfoot{} +\rfoot{{\footnotesize \thepage}} + +\clubpenalty=100000 +\widowpenalty=100000 + + +% see http://osdir.com/ml/tex.xetex/2005-10/msg00003.html +\newsavebox{\ximagebox}\newlength{\ximageheight} +\newsavebox{\xglyphbox}\newlength{\xglyphheight} +\newcommand{\xbox}[1] +{\savebox{\ximagebox}{#1}\settoheight{\ximageheight}{\usebox {\ximagebox}}% +\savebox{\xglyphbox}{\char32}\settoheight{\xglyphheight}{\usebox {\xglyphbox}}% +\raisebox{\ximageheight}[0pt][0pt]{%\raisebox{-\xglyphheight}[0pt] [0pt]{% +\makebox[0pt][l]{\usebox{\xglyphbox}}}%}% +\usebox{\ximagebox}% +\raisebox{0pt}[0pt][0pt]{\makebox[0pt][r]{\usebox{\xglyphbox}}}} + + + +\newcommand{\typosubsubsection}[1]{% +{#1} +} + +\newcommand{\typosubsection}[1]{% +{\addfontfeature{ +SizeFeatures={ + {Size={-10}, Scale=1.2, FakeStretch=1.00, LetterSpace=8.0 }, + {Size={10.5-12}, Scale=1.2, FakeStretch=1.00, LetterSpace=8.0 }, + {Size={12-}, FakeStretch=1.0, LetterSpace=8.0 } +}, +Letters={Uppercase} +} +\MakeUppercase{#1}} +} + +\newcommand{\typosection}[1]{% +{\addfontfeature{FakeStretch=0.96, LetterSpace=-4.0}\emph{\scalefont{2}#1}} +%{\addfontfeature{Scale=2.0, FakeStretch=0.98, LetterSpace=-2.0}\emph{#1}} +} + + +\newcommand{\tytul}[1]{% +#1% +\vspace{1em}% +} + +\newcommand{\nazwapodutworu}[1]{% +\section*{\typosection{#1}}% +} + +\newcommand{\autorutworu}[1]{% +\subsection*{\typosubsection{#1}}% +} + +\newcommand{\dzielonadrzedne}[1]{% +\subsection*{\typosubsubsection{#1}}% +} + +\newcommand{\nazwautworu}[1]{% +\section*{\raggedright{\typosection{#1}}}% +} + +\newcommand{\podtytul}[1]{% +\subsection*{\typosubsubsection{#1}}% +} + +\newcommand{\translator}[1]{% +% TODO: l10n is a style thing +\subsection*{\typosubsubsection{tłum. #1}}% +} + + +\newcommand{\powiesc}[1]{#1} +\newcommand{\opowiadanie}[1]{#1} +\newcommand{\lirykal}[1]{#1} +\newcommand{\lirykalp}[1]{#1} +\newcommand{\dramatwierszowanyl}[1]{#1} +\newcommand{\dramatwierszowanylp}[1]{#1} +\newcommand{\dramatwspolczesny}[1]{#1} + +\newcommand{\nota}[1]{% +\par{#1}% +} + +\newcommand{\dedykacja}[1]{% +\begin{em}% +\begin{flushright}% +#1% +\end{flushright}% +\end{em}% +} + +\newcommand{\dlugicytat}[1]{% +\begin{quotation}% +#1% +\end{quotation}% +} + +\newcommand{\poezjacyt}[1]{% +\begin{verse}% +#1% +\end{verse}% +} +\newcommand{\motto}[1]{% +\begin{em}% +#1% +\end{em}% +} +\newcommand{\listaosob}[2]{% +\par{#1}% +\begin{itemize}% +#2% +\end{itemize}% +} + +\newcommand{\nagloweklisty}[1]{% +\typosubsubsection{#1}% +} + +\newcommand{\listaosoba}[1]{% +\item{#1}% +} + +\newcommand{\kwestia}[1]{% +\par{#1}% +} + +\newcommand{\naglowekakt}[1]{% +\pagebreak +\subsection*{\typosubsection{#1}}% +} +\newcommand{\naglowekczesc}[1]{% +\pagebreak +\subsection*{\typosubsection{#1}}% +} +\newcommand{\srodtytul}[1]{% +\subsection*{\typosubsection{#1}}% +} + +\newcommand{\naglowekscena}[1]{% +\subsubsection*{\typosubsubsection{#1}}% +} +\newcommand{\naglowekrozdzial}[1]{% +{\subsubsection*{\raggedright{\typosubsubsection{#1}}}}% +} + +\newcommand{\naglowekosoba}[1]{% +\par{\textsc{#1}}\nopagebreak% +} +\newcommand{\naglowekpodrozdzial}[1]{% +\par{#1}\nopagebreak% +} + +\newcommand{\miejsceczas}[1]{% +\par{\emph{#1}}% +} +\newcommand{\didaskalia}[1]{% +\par{\emph{#1}}% +} + +\newcommand{\akap}[1]{% +\par{#1}% +} +\newcommand{\akapdialog}[1]{% +\par{#1}% +} +\newcommand{\akapcd}[1]{% +\par{#1}% +} + +\newcommand{\mottopodpis}[1]{% +\begin{em}% +\begin{flushright}% +#1% +\end{flushright}% +\end{em}% +} + +\newcommand{\strofa}[1]{% +\par{\noindent{\ignorespaces#1\vspace{1em}}}% +} + +\newcommand{\wers}[1]{#1} + +\newcommand{\wersakap}[1]{% +\hspace*{1em}#1% +} +\newcommand{\werscd}[1]{% +\hspace*{8em}#1% +} +\newcommand{\werswciety}[2][1em]{% +\hspace*{#1}#2% +} + +\ifshowfootnotes + \newcommand{\pa}[1]{\NoCaseChange{\footnote{#1}}} + \newcommand{\pe}[1]{\NoCaseChange{\footnote{#1}}} + \newcommand{\pr}[1]{\NoCaseChange{\footnote{#1}}} + \newcommand{\pt}[1]{\NoCaseChange{\footnote{#1}}} +\else + \newcommand{\pa}[1]{} + \newcommand{\pe}[1]{} + \newcommand{\pr}[1]{} + \newcommand{\pt}[1]{} +\fi + +\newcommand{\mat}[1]{$#1$} + +\newcommand{\didasktekst}[1]{% +\emph{#1}% +} +\newcommand{\slowoobce}[1]{% +\emph{#1}% +} +\newcommand{\tytuldziela}[1]{% +\emph{#1}% +} +\newcommand{\wyroznienie}[1]{% +\emph{#1}% +} + +\newcommand{\osoba}[1]{% +#1% +} + +\newcommand{\sekcjaswiatlo}{% +\vspace{30pt}% +} + +\newcommand{\sekcjaasterysk}{% +\vspace{10pt}% +\begin{center}% +\par{*}% +\end{center}% +} + +\newcommand{\separatorlinia}{% +\vspace{10pt}% +\hrule{}% +\vspace{10pt}% +} + +\newcommand{\motyw}[2][0]{% +\ifshowthemes +\mbox{}% +\marginpar{% +\vspace{-8pt}% +\vspace{-#1\baselineskip}% +\raggedright{\hspace{0pt}% +\footnotesize{\color{theme}{#2}}}% +\vspace{\baselineskip}% +}% +\fi +} + + +\newcommand{\editorialpage}[1]{% +#1% +\pagebreak% +} diff --git a/librarian/formats/pdf/res/wl2tex.xslt b/librarian/formats/pdf/res/wl2tex.xslt new file mode 100755 index 0000000..f36172d --- /dev/null +++ b/librarian/formats/pdf/res/wl2tex.xslt @@ -0,0 +1,441 @@ + + + + + + + + + + \documentclass[]{wl} + \usepackage{style} + + \usepackage{makecover} + + + + + + \usepackage[maxfloats=64]{morefloats} + + + + + \usepackage{morefloats} + + + + + + \IfFileExists{morefloats.sty}{ + \usepackage{morefloats} + }{} + + + + + + + + + + + mm + 210mm + + + + + + + + + + + + + + + + + + + + + + + + + + + \def\coverby{ + + + + Okładka na podstawie: + + + + + + + + + + + + + + + } + \def\editors{} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \def\authors{} + \author{\authors} + \title{} + \def\translatorsline{} + + \def\bookurl{} + + \def\rightsinfo{ + + + + + + + } + + + \def\sourceinfo{ + + Tekst opracowany na podstawie: + \vspace{.6em} + + } + \def\description{ + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \\{} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wers + + + + em + + + + + + + + + + + + + + + + + + + + + + + + „” + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + Opracowanie redakcyjne i przypisy: + + . + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/milpdf/a.xml b/milpdf/a.xml new file mode 100644 index 0000000..460f9ff --- /dev/null +++ b/milpdf/a.xml @@ -0,0 +1,4 @@ +
Modern Poland FoundationenglishCreative Commons Attribution – Share Alike18+https://farm6.staticflickr.com/5308/5729009434_0038a5a6fb_o.jpg + +
Relations in the media environment
I
knows how to consciously create her image depending on needs or a recipient; e.g. knows how to use words and body language during lectures.
can react in a crisis situation concerning her image, e.g. demands deleting untrue information about her by sending corrections to website's administrators.
I and others
can convince others through media, adjusting the message to its recipient and to the goal of the communication, e.g. knows how to discuss about an article, without offending the interlocutor.
can influence others thanks to proper language and chosen channels of communication, e.g. can convince friends from social media to social activism on the university.
knows that communication through media has its limitations and can counteract them, e.g. calls or talks directly if chat on instant messenger is not possible/
is aware that some recipients of her messages may be less competent. Can adjust her messages to recipients' capabilities, e.g. knows that some scholars rarely check their e-mails, so when necessary is able to find other effective means of communication.
I and environment
knows the internet and reality permeat each other, and rules of human relationships are very similar, e.g. knows that group work when done remotely requires the same commitment as in person.
knows that part of social life happens through media, e.g. knows that a party may happen on the internet.
can plan and activate a group via different, virtual and real media, e.g. coordinates the preparations to student self-government elections.
+
diff --git a/milpdf/m.xml b/milpdf/m.xml new file mode 100644 index 0000000..b391d62 --- /dev/null +++ b/milpdf/m.xml @@ -0,0 +1,151 @@ +
+ + przedszkole + Zuzanna Zasacka + Izabela Machnowska + + Zuzanna Zasacka + Izabela Machnowska + course + Publikacja zrealizowana w ramach projektu Cyfrowa Przyszłość, dofinansowanego ze środków Ministerstwa Kultury i Dziedzictwa Narodowego. + 2014/0/wychowanie-przedszkolne/c7 + http://milpeer.mdrn.pl/media/dynamic/uploads/13/3512808721_202609103f_b.jpg + + + + +
Animacja - robimy film rysunkowy
+
+
Wiedza w pigułce +
Porównanie sposobu zaprezentowania informacji w wielu różnych źródłach pozwala na celniejszą ocenę, który materiał będzie dla ciebie najprzydatniejszy. Sprzyja również krytycznemu spojrzeniu na dostępne źródła — dzięki temu łatwiej możemy wskazać intencję nadawcy, a także stopień wiarygodności przekazu.
Jednym z najważniejszych czynników kształtujących przekazy medialne jest intencja nadawcy — a więc to, co chciał osiągnąć poprzez rozpowszechnienie utworu czy tekstu. Wpływa na nią nie tylko cel czy funkcja komunikatu (np. zareklamowanie, przekonanie do danego poglądu czy informowanie), lecz także jego grupa docelowa. W inny sposób mówi się do młodzieży, w inny do dorosłych; w inny do znawców dziedziny, inaczej do laików.
Rzadko kiedy zdarzają się bezstronne artykuły, a i one przekazują tylko wybrane informacje na dany temat. Oddzielenie opinii od faktów jest kluczowe w krytycznej ocenie tekstów. Dzięki niej możesz uniknąć manipulacji, a także nieświadomego przyjmowania perspektywy autora. Większość materiałów dostępnych w internecie zawiera opinie ich twórców. Nawet jeśli na pierwszy rzut oka artykuł jest informacyjny, podczas uważnej lektury można dostrzec wyznaczniki subiektywnego punktu widzenia.
Zlewanie się informacji i opinii łatwo zaobserwować w reklamach. Z jednej strony opowiadają one o produkcie, z drugiej zaś przedstawiają jego subiektywną ocenę. Podczas odbioru przekazów medialnych warto zwracać uwagę na pojawianie się takich oceniających określeń.
Na to, w jaki sposób prezentowane są informacje, wpływają nie tylko opinie autorów. Również zastosowany gatunek medialny narzuca określony sposób mówienia o danym temacie: czego innego oczekujemy od felietonu, a czego innego od radiowego wydania wiadomości. Charakter i ograniczenia źródła zależą również od kanału komunikacyjnego, np. informacje zamieszczone w internecie są często aktualniejsze niż książki. Nie mogą one również oddziaływać na zmysł słuchu, w przeciwieństwie do radia czy telewizji.
Warto jednocześnie pamiętać, że do niektórych źródeł można dotrzeć poprzez różne kanały komunikacyjne. Dotyczy to m. in. codziennej prasy, drukowanej i udostępnianej w sieci.
Niezależnie od różnych czynników wpływających na kształt przekazów, najważniejszym kryterium ich oceny jest zawsze ich wiarygodność.
+
+
teacher + +
Pomysł na lekcję +
+
Podczas zajęć dzieci spróbują zrobić własny, krótki film animowany. Na początku narysują po kolei każdą klatkę, po czym "historia" zostanie sfilmowana przez prowadzącego.
+
+
Cele operacyjne +
+
+ Uczestnicy i uczestniczki: +
wiedzą, czym jest film rysunkowy;
wiedzą, za pomocą jakich narzędzi medialnych można przygotować taki film;
wiedzą, że proste treści medialne można przetwarzać.
+
+
+
+
Przebieg zajęć +
+
+
1.
Poproś dzieci, aby spróbowały wymienić tytuły filmów animowanych, które oglądały w domu lub w przedszkolu.
+
+
Czas
+ + 5 +
+
Metoda
+ + rozmowa +
+
Pomoce
+ + +
+
+
+
2.
Zabawa ruchowa ,,Słoneczko się budzi, słoneczko zasypia". W miejscu przeznaczonym do ćwiczeń ruchowych dzieci wraz z nauczycielem siedzą w dużym, wspólnym kole - siad skrzyżny, dłonie na kolanach, głowa opuszczona w dół. Na hasło nauczyciela ,,film" każde dziecko kolejno (zaczynając od pierwszego po prawej stronie nauczyciela) kładzie się na podłogę (,,przykleja" plecy do podłogi) i mocno naprężone i wyciągnięte ręce kładzie równolegle wzdłuż głowy (,,przykleja" ręce do podłogi), imitując tym samym promyk słońca. Kiedy wszystkie dzieci wykonały już swoje ruchy i całe ,,otwarte", duże słońce z promykami leży na podłodze, nauczyciel ponownie wypowiada hasło ,,film", a dzieci kolejno wracają do pozycji wyjściowej (słoneczko zasypia). UWAGA! Ćwiczenie należy wykonać bardzo płynnie, bez przerw, we wspólnym rytmie - chodzi tu o uzyskanie tzw. ,,efektu fali". Żeby doprowadzić do oczekiwanego rezultatu, powtórz ćwiczenie kilka razy. Jeśli masz czas, poproś wybrane dziecko o nakręcenie komórką dokumentującego zabawę filmiku.
+
+
Czas
+ 10 +
+
Metoda
+ zabawa +
+
Pomoce
+ +
+
+
-
+
+
3.
Rozdaj każdemu dziecku kartkę formatu A5 z narysowanym przez ciebie przedtem kółkiem na środku. Podziel dzieci na grupy stolikami. Przy pierwszym stoliku dzieci dorysowują po dwa promyczki na każdej kartce, przy drugim stoliku dzieci rysują po pięć promyków, przy trzecim ,,mnóstwo", ,,dużo" promyków. Jeśli dzieci jest więcej, nauczyciel, w czasie wykonywania przez dzieci prac, zbiera już gotowe kartki z dorysowanymi promykami i na bieżąco zanosi je do czwartego stolika, gdzie rysowane są tylko smutne lub wesołe buźki. Pod koniec zepnij wszystkie kartki spinaczem (typu czarny metalowy klips).
+
+
Czas
+ 10 +
+
Metoda
+ zabawa +
+
Pomoce
+ kartki A5 dla każdej uczestniczki/uczestnika, kredki +
+
+
+
+
4.
Zabawa dźwiękonaśladowcza ,,Ciszej - głośniej". Nauczyciel tworzy z dziećmi muzykę do filmu. W tym celu ćwiczy z dziećmi odgłosy znane dzieciom ze środowiska naturalnego np: wiatr (szszszszszszszsz), konik (tzw. ,,języczki"), pies (hau,hau), kraczące wrony, szczekające psy na wsi, itp. W tym ćwiczeniu ważne jest, aby dzieci nie przekrzykiwały się nawzajem oraz zwracały uwagę na dynamikę (głośniej, ciszej), którą wskazuje nauczyciel, dyrygując rękami.
+
+
Czas
+
5
+
+
Metoda
+ zabawa +
+
Pomoce
+ +
+
+
+
5.
Spróbuj nakręcić film z dziecięcych animacji. Będziesz potrzebował/a urządzenia z funkcją filmowania (aparatu lub komórki). Najlepiej uchwycić moment kartkowania. Każdy filmik będzie miał parę sekund. Nakręćcie różne wersje filmu. Np.: za pierwszym razem Janek kciukiem kartkuje rysunki, nauczyciel kręci film, dziewczynki imitują głosem podkład dźwiękowy ,,wiatr", za drugim razem Piotrek kręci film, Ania kartkuje kciukiem, a chłopcy robią podkład dźwiękowy ,,koniki" itd. Na koniec wspólnie wybierzcie najlepszą wersję.
+
+
Czas
+
15
+
+
Metoda
+ zabawa +
+
Pomoce
+ komórka lub aparat z funkcją nagrywania +
+
+
+
+
+
+
Ewaluacja +
+
Czy po przeprowadzeniu zajęć ich uczestnicy i uczestniczki: +
umieją, przy wsparciu dorosłych, pokazać grupie efekty samodzielnego tworzenia i przetwarzania komunikatów?
+
+
+
+
Opcje dodatkowe +
+
Jeśli masz więcej czasu, w pierwszym zadaniu możesz pokazać starszym przedszkolakom znaleziony w internecie przykładowy filmik o tym, jak powstaje film animowany. Najlepiej, gdy będzie to jakaś popularna wśród dzieci produkcja.
+
+
+
+
Słowniczek +
+
+
+
wiarygodność
+ cecha tego, co wiarygodne, czyli rzetelne i godne zaufania +
+
+
źródło informacji
+ każdy przedmiot z którego czerpie się wiadomości na interesujący nas temat +
+
+
+
+
Czytelnia +
+
+
"Animacja i jej rodzaje" w Samouczku na stronie Muzuem Animacji Semafor [dostęp: 21.01.2015], dostępny w internecie: http://muzeum.se-ma-for.com/samouczek/animacja-i-jej-rodzaje-pl
+
" Jak to działa, czyli przygotuj własną animację" [dostęp: 21.01.2015], dostępny w internecie: http://www.nina.gov.pl/edukacja/pracownia/artyku%C5%82/2012/05/30/jak-to-dziala---czyli-przygotuj-wlasna-animacje_
+
+
+
diff --git a/milpdf/p.xml b/milpdf/p.xml new file mode 100644 index 0000000..190fed2 --- /dev/null +++ b/milpdf/p.xml @@ -0,0 +1,56 @@ +
engChildrenhttp://milpeer.mdrn.pl/media/dynamic/uploads/27/smartie.pngAn Author + + + +
The adventures of Smartie the Penguin
+ +
+
It was late at night and Smartie the Penguin was +WIDE awake... He was supposed to be fast asleep +but tomorrow was his birthday. He was so +excited; all he wanted was a new computer!
+
+
+ +
+
The big day came and Smartie had really enjoyed +his birthday party. He had saved his biggest and +most exciting looking present until the very end, +“Oh please, please, please!” he thought as he +began to open it.
+
+
+ +
+
Smartie tore off the +wrapping paper in a hurry, +it was exactly what he +wanted, a brand new +laptop!
+
+
“Oh! Thank +you, thank you, +thank you!” He +cried.
+
+
+ +
+
Daddy Penguin was +extremely clever and +knew all about +computers and how to +get on to the internet. +He helped Smartie to +find his favourite +website and soon +Smartie was happily +playing a game online.
+
+
+ + +
-- 2.20.1 From 9e4ab00dca15599a016b0621cdaae623bc8a723e Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Thu, 13 Oct 2016 16:48:10 +0200 Subject: [PATCH 06/16] epub download stub --- librarian/formats/epub/__init__.py | 50 ++++++++++++++---------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py index 38778ac..b7cf539 100644 --- a/librarian/formats/epub/__init__.py +++ b/librarian/formats/epub/__init__.py @@ -44,18 +44,18 @@ class EpubFormat(Format): mime.compress_type = zipfile.ZIP_STORED mime.extra = '' zip.writestr(mime, 'application/epub+zip') - zip.writestr('META-INF/container.xml', '' \ - '' \ - '') - - toc_file = etree.fromstring('' \ - '' \ - '') - nav_map = toc_file[-1] + zip.writestr('META-INF/container.xml', '' + '' + '') + + toc_file = etree.fromstring('' + '' + '') + # nav_map = toc_file[-1] if self.cover is not None: cover = self.cover(self.doc) @@ -71,9 +71,9 @@ class EpubFormat(Format): if cover.uses_dc_cover: if self.doc.meta.get_one('cover_by'): - document.edoc.getroot().set('data-cover-by', self.doc.meta.get_one('cover_by')) + self.doc.edoc.getroot().set('data-cover-by', self.doc.meta.get_one('cover_by')) if self.doc.meta.get_one('cover_source'): - document.edoc.getroot().set('data-cover-source', self.doc.meta.get_one('cover_source')) + self.doc.edoc.getroot().set('data-cover-source', self.doc.meta.get_one('cover_source')) manifest.append(etree.fromstring( '')) @@ -83,7 +83,6 @@ class EpubFormat(Format): opf.getroot()[0].append(etree.fromstring('')) guide.append(etree.fromstring('')) - ctx = Context(format=self) ctx.toc = TOC() ctx.toc_level = 0 @@ -110,18 +109,17 @@ class EpubFormat(Format): if len(ctx.footnotes.output): ctx.toc.add("Przypisy", "footnotes.html") - manifest.append(etree.Element(OPFNS('item'), - id='footnotes', href='footnotes.html', - **{'media-type': "application/xhtml+xml"})) + manifest.append(etree.Element( + OPFNS('item'), id='footnotes', href='footnotes.html', + **{'media-type': "application/xhtml+xml"})) spine.append(etree.Element('itemref', idref='footnotes')) wrap = etree.parse(get_resource('formats/epub/res/footnotes.html')) extend_element(wrap.find('//*[@id="footnotes"]'), ctx.footnotes.output) - #chars = chars.union(used_chars(html_tree.getroot())) + # chars = chars.union(used_chars(html_tree.getroot())) zip.writestr('OPS/footnotes.html', etree.tostring( wrap, method="html", pretty_print=True)) - zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True)) ctx.toc.render(toc_file[-1]) zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True)) @@ -180,8 +178,8 @@ class Footnotes(object): def append(self, items): self.counter += 1 - e = etree.Element("a", - href="part%d.html#footnote-anchor-%d" % (int(items[0].get('part_no')), self.counter), + e = etree.Element( + "a", href="part%d.html#footnote-anchor-%d" % (int(items[0].get('part_no')), self.counter), id="footnote-%d" % self.counter, style="float:left;margin-right:1em") e.text = "[%d]" % self.counter @@ -189,9 +187,8 @@ class Footnotes(object): self.output.append(e) for item in items: extend_element(self.output, item) - anchor = etree.Element("a", - id="footnote-anchor-%d" % self.counter, - href="footnotes.html#footnote-%d" % self.counter) + anchor = etree.Element( + "a", href="footnotes.html#footnote-%d" % self.counter, id="footnote-anchor-%d" % self.counter) anchor.text = "[%d]" % self.counter return anchor @@ -239,7 +236,7 @@ class AsideR(EpubRenderer): def render(self, element, ctx): outputs = list(super(AsideR, self).render(element, ctx)) anchor = ctx.footnotes.append(outputs) - wrapper, inside = self.text_container() #etree.Element('_', part_no=str(ctx.part_no)) + wrapper, inside = self.text_container() # etree.Element('_', part_no=str(ctx.part_no)) inside.append(anchor) yield wrapper EpubFormat.renderers.register(core.Aside, None, AsideR('div')) @@ -276,4 +273,3 @@ EpubFormat.renderers.register(core.Section, None, SectionR()) class SpanR(EpubRenderer): pass EpubFormat.renderers.register(core.Span, None, SpanR('span')) - -- 2.20.1 From 9c1ccb775783170750653a1add478802cbc2844f Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Fri, 2 Dec 2016 15:25:27 +0100 Subject: [PATCH 07/16] images in epub --- librarian/formats/epub/__init__.py | 51 ++++++++++++++++++++++++++++-- librarian/utils.py | 2 +- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py index b7cf539..b9d1c7a 100644 --- a/librarian/formats/epub/__init__.py +++ b/librarian/formats/epub/__init__.py @@ -4,11 +4,15 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # import os +import urllib from copy import deepcopy +from mimetypes import guess_type from tempfile import NamedTemporaryFile import zipfile +from urllib2 import urlopen + from lxml import etree -from librarian import OPFNS, NCXNS, XHTMLNS +from librarian import OPFNS, NCXNS, XHTMLNS, DCNS from librarian import core from librarian.formats import Format from librarian.formats.cover.wolnelektury import WLCover @@ -30,12 +34,29 @@ class EpubFormat(Format): if cover is not None: self.cover = cover - def build(self): + def build(self, ctx=None): + + def add_file(url, file_id): + filename = url.rsplit('/', 1)[1] + if url.startswith('file://'): + url = ctx.files_path + urllib.quote(url[7:]) + if url.startswith('/'): + url = 'http://milpeer.eu' + url + file_content = urlopen(url).read() + zip.writestr(os.path.join('OPS', filename), file_content) + manifest.append(etree.fromstring( + '' % (file_id, filename, guess_type(url)[0]))) + opf = etree.parse(get_resource('formats/epub/res/content.opf')) manifest = opf.find(OPFNS('manifest')) guide = opf.find(OPFNS('guide')) spine = opf.find(OPFNS('spine')) + author = ", ". join(self.doc.meta.get(DCNS('creator')) or '') + title = self.doc.meta.title() + opf.find('.//' + DCNS('creator')).text = author + opf.find('.//' + DCNS('title')).text = title + output_file = NamedTemporaryFile(prefix='librarian', suffix='.epub', delete=False) zip = zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) @@ -58,6 +79,7 @@ class EpubFormat(Format): # nav_map = toc_file[-1] if self.cover is not None: + cover_image = self.doc.meta.get(DCNS('relation.coverimage.url'))[0] cover = self.cover(self.doc) cover_output = cover.build() cover_name = 'cover.%s' % cover.format_ext @@ -83,10 +105,14 @@ class EpubFormat(Format): opf.getroot()[0].append(etree.fromstring('')) guide.append(etree.fromstring('')) - ctx = Context(format=self) + if not ctx: + ctx = Context(format=self) + else: + ctx.format = self ctx.toc = TOC() ctx.toc_level = 0 ctx.footnotes = Footnotes() + ctx.images = [] ctx.part_no = 0 wrap_tmpl = etree.parse(get_resource('formats/epub/res/chapter.html')) @@ -107,6 +133,9 @@ class EpubFormat(Format): })) zip.writestr('OPS/%s.html' % partstr, etree.tostring(wrap, method='html')) + for i, url in enumerate(ctx.images): + add_file(url, 'image%s' % i) + if len(ctx.footnotes.output): ctx.toc.add("Przypisy", "footnotes.html") manifest.append(etree.Element( @@ -252,6 +281,22 @@ class DivR(EpubRenderer): EpubFormat.renderers.register(core.Div, None, DivR('div')) +class DivImageR(EpubRenderer): + def render(self, element, ctx): + src = element.attrib.get('src', '') + ctx.images.append(src) + src = src.rsplit('/', 1)[1] + return super(DivImageR, self).render(element, Context(ctx, src=src)) + + def container(self, ctx): + root, inner = super(DivImageR, self).container(ctx) + src = getattr(ctx, 'src', '') + inner.set('src', src) + # inner.set('style', 'display: block; width: 60%; margin: 3em auto') + return root, inner +EpubFormat.renderers.register(core.Div, 'img', DivImageR('img')) + + class HeaderR(EpubRenderer): def subcontext(self, element, ctx): return Context(ctx, inline=True) diff --git a/librarian/utils.py b/librarian/utils.py index 25936bf..a2e3522 100755 --- a/librarian/utils.py +++ b/librarian/utils.py @@ -26,7 +26,7 @@ class Context(object): elif self._upctx is not None: return getattr(self._upctx, name) else: - raise AttributeError + raise AttributeError, "'%s' object has no attribute '%s'" % (type(self), name) def __setattr__(self, name, value): try: -- 2.20.1 From 25af49b6c63e1c005129856e107143864ad5b245 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Fri, 9 Dec 2016 16:05:27 +0100 Subject: [PATCH 08/16] epub covers --- librarian/__init__.py | 11 ++++--- librarian/formats/cover/__init__.py | 16 ++++++---- librarian/formats/cover/evens/__init__.py | 36 +++++++++++++++++++++++ librarian/formats/epub/__init__.py | 9 +++--- librarian/formats/epub/res/cover.html | 4 +-- 5 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 librarian/formats/cover/evens/__init__.py diff --git a/librarian/__init__.py b/librarian/__init__.py index 0616f23..02464ef 100644 --- a/librarian/__init__.py +++ b/librarian/__init__.py @@ -3,7 +3,6 @@ # This file is part of Librarian, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # -import os import re import urllib from .utils import XMLNamespace @@ -23,13 +22,19 @@ class UnicodeException(Exception): message = unicode(args, encoding='utf-8', errors='ignore') return message + class ParseError(UnicodeException): pass + class ValidationError(UnicodeException): pass +class BuildError(Exception): + pass + + class EmptyNamespace(XMLNamespace): def __init__(self): super(EmptyNamespace, self).__init__('') @@ -53,8 +58,7 @@ class WLURI(object): slug = None example = 'http://wolnelektury.pl/katalog/lektura/template/' - _re_wl_uri = re.compile(r'http://(www\.)?wolnelektury.pl/katalog/lektura/' - '(?P[-a-z0-9]+)/?$') + _re_wl_uri = re.compile(r'http://(www\.)?wolnelektury.pl/katalog/lektura/(?P[-a-z0-9]+)/?$') def __init__(self, uri): uri = unicode(uri) @@ -93,4 +97,3 @@ class WLURI(object): class URLOpener(urllib.FancyURLopener): version = 'FNP Librarian (http://git.nowoczesnapolska.org.pl/?p=librarian.git)' urllib._urlopener = URLOpener() - diff --git a/librarian/formats/cover/__init__.py b/librarian/formats/cover/__init__.py index 7a787e8..b9b515a 100644 --- a/librarian/formats/cover/__init__.py +++ b/librarian/formats/cover/__init__.py @@ -126,6 +126,7 @@ class Cover(Format): logo_bottom = None logo_width = None + logo_file = get_resource('res/wl-logo.png') uses_dc_cover = False format = 'JPEG' @@ -164,14 +165,17 @@ class Cover(Format): if self.background_img: background = Image.open(self.background_img) - img.paste(background, None, background) - del background + resized = background.resize((1024, background.height*1024/background.width), Image.ANTIALIAS) + resized = resized.convert('RGBA') + img.paste(resized, (0, 0), resized) + del background, resized - # WL logo if metr.logo_width: - logo = Image.open(get_resource('res/wl-logo.png')) - logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0])) - img.paste(logo, ((metr.width - metr.logo_width) / 2, img.size[1] - logo.size[1] - metr.logo_bottom)) + logo = Image.open(self.logo_file) + logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0]), Image.ANTIALIAS) + logo = logo.convert('RGBA') + img.paste(logo, ((metr.width - metr.logo_width) / 2, + img.size[1] - logo.size[1] - metr.logo_bottom), logo) top = metr.author_top tbox = TextBox( diff --git a/librarian/formats/cover/evens/__init__.py b/librarian/formats/cover/evens/__init__.py new file mode 100644 index 0000000..4207d46 --- /dev/null +++ b/librarian/formats/cover/evens/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +import urllib +from urllib2 import urlopen, URLError + +from librarian import DCNS, BuildError +from .. import Cover + + +class EvensCover(Cover): + format_name = u"Evens cover image" + width = 1024 + height = 1365 + author_top = 900 + title_top = 30 + logo_bottom = 100 + + def __init__(self, doc, format=None, width=None, height=None): + super(EvensCover, self).__init__(doc, format=format, width=width, height=height) + self.doc = doc + + def set_images(self, ctx): + cover_url = self.doc.meta.get(DCNS('relation.coverimage.url'))[0] + if cover_url.startswith('file://'): + cover_url = ctx.files_path + urllib.quote(cover_url[7:]) + try: + self.background_img = urlopen(cover_url) + except URLError: + raise BuildError('Cannot open the cover image: %s' % cover_url) + + if getattr(ctx, 'cover_logo', None): + self.logo_width = 150 + self.logo_file = urlopen(ctx.cover_logo) diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py index b9d1c7a..bf21a6f 100644 --- a/librarian/formats/epub/__init__.py +++ b/librarian/formats/epub/__init__.py @@ -15,7 +15,7 @@ from lxml import etree from librarian import OPFNS, NCXNS, XHTMLNS, DCNS from librarian import core from librarian.formats import Format -from librarian.formats.cover.wolnelektury import WLCover +from librarian.formats.cover.evens import EvensCover from librarian.output import OutputFile from librarian.renderers import Register, TreeRenderer, UnknownElement from librarian.utils import Context, get_resource, extend_element @@ -25,7 +25,7 @@ class EpubFormat(Format): format_name = 'EPUB' format_ext = 'epub' - cover = WLCover + cover = EvensCover renderers = Register() def __init__(self, doc, cover=None, with_fonts=True): @@ -79,8 +79,9 @@ class EpubFormat(Format): # nav_map = toc_file[-1] if self.cover is not None: - cover_image = self.doc.meta.get(DCNS('relation.coverimage.url'))[0] + # cover_image = self.doc.meta.get(DCNS('relation.coverimage.url'))[0] cover = self.cover(self.doc) + cover.set_images(ctx) cover_output = cover.build() cover_name = 'cover.%s' % cover.format_ext zip.writestr(os.path.join('OPS', cover_name), cover_output.get_string()) @@ -117,7 +118,7 @@ class EpubFormat(Format): wrap_tmpl = etree.parse(get_resource('formats/epub/res/chapter.html')) for e in self.render(self.doc.edoc.getroot(), ctx): - if not len(e) and not e.text.strip(): + if not len(e) and not (e.text and e.text.strip()): continue wrap = deepcopy(wrap_tmpl) extend_element(wrap.find('//*[@id="book-text"]'), e) diff --git a/librarian/formats/epub/res/cover.html b/librarian/formats/epub/res/cover.html index 784067c..3233201 100644 --- a/librarian/formats/epub/res/cover.html +++ b/librarian/formats/epub/res/cover.html @@ -2,12 +2,12 @@ - Okładka + Cover
- Okładka + Cover
\ No newline at end of file -- 2.20.1 From dcf52453a888b12b3fd562df71e30d95af847a29 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Fri, 9 Dec 2016 17:16:22 +0100 Subject: [PATCH 09/16] footer in epub --- librarian/formats/epub/__init__.py | 30 ++++++++++++++++++++++++- librarian/formats/epub/res/chapter.html | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py index bf21a6f..708b063 100644 --- a/librarian/formats/epub/__init__.py +++ b/librarian/formats/epub/__init__.py @@ -34,6 +34,9 @@ class EpubFormat(Format): if cover is not None: self.cover = cover + def dc(self, tag): + return self.doc.meta.get_one(DCNS(tag)) + def build(self, ctx=None): def add_file(url, file_id): @@ -52,7 +55,7 @@ class EpubFormat(Format): guide = opf.find(OPFNS('guide')) spine = opf.find(OPFNS('spine')) - author = ", ". join(self.doc.meta.get(DCNS('creator')) or '') + author = ", ". join(self.doc.meta.get(DCNS('creator')) or []) title = self.doc.meta.title() opf.find('.//' + DCNS('creator')).text = author opf.find('.//' + DCNS('title')).text = title @@ -150,6 +153,31 @@ class EpubFormat(Format): zip.writestr('OPS/footnotes.html', etree.tostring( wrap, method="html", pretty_print=True)) + footer_text = [ + 'Information about the resource', + 'Publisher: %s' % self.dc('publisher'), + 'Rights: %s' % self.dc('rights'), + 'Intended audience: %s' % self.dc('audience'), + self.dc('description'), + 'Resource prepared using MIL/PEER editing platform.', + 'Source available at %s' % ctx.source_url, + ] + footer_wrap = deepcopy(wrap_tmpl) + footer_body = footer_wrap.find('//*[@id="book-text"]') + for line in footer_text: + footer_line = etree.Element('p') + footer_line.text = line + footer_body.append(footer_line) + manifest.append(manifest.makeelement(OPFNS('item'), attrib={ + 'id': 'footer', + 'href': "footer.html", + 'media-type': 'application/xhtml+xml', + })) + spine.append(spine.makeelement(OPFNS('itemref'), attrib={ + 'idref': 'footer', + })) + zip.writestr('OPS/footer.html', etree.tostring(footer_wrap, method='html')) + zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True)) ctx.toc.render(toc_file[-1]) zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True)) diff --git a/librarian/formats/epub/res/chapter.html b/librarian/formats/epub/res/chapter.html index 342d5df..2123756 100644 --- a/librarian/formats/epub/res/chapter.html +++ b/librarian/formats/epub/res/chapter.html @@ -3,7 +3,7 @@ - WolneLektury.pl + Milpeer.eu -- 2.20.1 From d1037d617d8cd2cafc60e67ac0272f86d2e24dfa Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Mon, 12 Dec 2016 13:52:21 +0100 Subject: [PATCH 10/16] render missing tags for epub --- librarian/formats/epub/__init__.py | 51 +++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py index 708b063..80f9d5c 100644 --- a/librarian/formats/epub/__init__.py +++ b/librarian/formats/epub/__init__.py @@ -4,6 +4,7 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # import os +import re import urllib from copy import deepcopy from mimetypes import guess_type @@ -229,6 +230,24 @@ class EpubRenderer(TreeRenderer): yield wrapper +class NaturalText(EpubRenderer): + def render_text(self, text, ctx): + root, inner = self.text_container() + chunks = re.split('(?<=\s\w) ', text) + inner.text = chunks[0] + for chunk in chunks[1:]: + x = etree.Entity("nbsp") + x.tail = chunk + inner.append(x) + return root + + +class Silent(EpubRenderer): + def render_text(self, text, ctx): + root, inner = self.text_container() + return root + + class Footnotes(object): def __init__(self): self.counter = 0 @@ -290,7 +309,7 @@ class TOC(object): # Renderers -class AsideR(EpubRenderer): +class AsideR(NaturalText): def render(self, element, ctx): outputs = list(super(AsideR, self).render(element, ctx)) anchor = ctx.footnotes.append(outputs) @@ -299,8 +318,10 @@ class AsideR(EpubRenderer): yield wrapper EpubFormat.renderers.register(core.Aside, None, AsideR('div')) +EpubFormat.renderers.register(core.Aside, 'comment', Silent()) + -class DivR(EpubRenderer): +class DivR(NaturalText): def container(self, ctx): root, inner = super(DivR, self).container(ctx) if getattr(ctx, 'inline', False): @@ -308,6 +329,11 @@ class DivR(EpubRenderer): inner.set('style', 'display: block;') return root, inner EpubFormat.renderers.register(core.Div, None, DivR('div')) +EpubFormat.renderers.register(core.Div, 'p', NaturalText('p')) + +EpubFormat.renderers.register(core.Div, 'list', NaturalText('ul')) +EpubFormat.renderers.register(core.Div, 'list.enum', NaturalText('ol')) +EpubFormat.renderers.register(core.Div, 'item', NaturalText('li')) class DivImageR(EpubRenderer): @@ -326,13 +352,13 @@ class DivImageR(EpubRenderer): EpubFormat.renderers.register(core.Div, 'img', DivImageR('img')) -class HeaderR(EpubRenderer): +class HeaderR(NaturalText): def subcontext(self, element, ctx): return Context(ctx, inline=True) EpubFormat.renderers.register(core.Header, None, HeaderR('h1')) -class SectionR(EpubRenderer): +class SectionR(NaturalText): epub_separate = True def render(self, element, ctx): @@ -344,6 +370,21 @@ class SectionR(EpubRenderer): EpubFormat.renderers.register(core.Section, None, SectionR()) -class SpanR(EpubRenderer): +class SpanR(NaturalText): pass EpubFormat.renderers.register(core.Span, None, SpanR('span')) +EpubFormat.renderers.register(core.Span, 'cite', SpanR('i')) +EpubFormat.renderers.register(core.Span, 'emp', SpanR('b')) +EpubFormat.renderers.register(core.Span, 'emph', SpanR('i')) + + +class SpanLink(EpubRenderer): + def render(self, element, ctx): + parts = super(SpanLink, self).render(element, ctx) + for part in parts: + src = element.attrib.get('href', '') + if src.startswith('file://'): + src = ctx.files_path + src[7:] + part[0].attrib['href'] = src + yield part +EpubFormat.renderers.register(core.Span, 'link', SpanLink('a')) -- 2.20.1 From 3f24ff6b4246a5206555952f6e6c53f6ed5231d8 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Thu, 15 Dec 2016 12:43:14 +0100 Subject: [PATCH 11/16] pep8, style, dead code cleanup etc. --- librarian/__init__.py | 5 + librarian/book2anything.py | 44 +- librarian/dcparser.py | 383 ------------ librarian/document.py | 5 +- librarian/epub.py | 563 ------------------ librarian/fb2.py | 63 -- librarian/formats/__init__.py | 2 + librarian/formats/cover/__init__.py | 17 +- librarian/formats/cover/evens/__init__.py | 4 - .../formats/cover/wolnelektury/__init__.py | 61 +- librarian/formats/html/__init__.py | 27 +- librarian/formats/pdf/__init__.py | 63 +- librarian/meta.py | 1 - librarian/mobi.py | 60 -- librarian/packagers.py | 156 ----- librarian/parser.py | 2 +- librarian/pdf.py | 321 ---------- librarian/renderers.py | 1 - librarian/text.py | 91 --- librarian/utils.py | 4 +- scripts/book2partner | 53 -- setup.py | 1 - tests/test_dcparser.py | 48 -- tests/test_epub.py | 31 - tests/test_html.py | 40 -- tests/test_pdf.py | 28 - tests/test_picture.py | 60 -- tests/test_text.py | 34 -- tests/utils.py | 2 +- 29 files changed, 125 insertions(+), 2045 deletions(-) delete mode 100644 librarian/dcparser.py delete mode 100644 librarian/epub.py delete mode 100644 librarian/fb2.py delete mode 100644 librarian/mobi.py delete mode 100644 librarian/packagers.py delete mode 100644 librarian/pdf.py delete mode 100644 librarian/text.py delete mode 100755 scripts/book2partner delete mode 100644 tests/test_dcparser.py delete mode 100644 tests/test_epub.py delete mode 100644 tests/test_html.py delete mode 100644 tests/test_pdf.py delete mode 100644 tests/test_picture.py delete mode 100644 tests/test_text.py diff --git a/librarian/__init__.py b/librarian/__init__.py index 02464ef..a0d70e7 100644 --- a/librarian/__init__.py +++ b/librarian/__init__.py @@ -31,6 +31,11 @@ class ValidationError(UnicodeException): pass +# was deleted, but still used??? +class NoDublinCore(ValidationError): + pass + + class BuildError(Exception): pass diff --git a/librarian/book2anything.py b/librarian/book2anything.py index e46a4b4..d4b9a78 100755 --- a/librarian/book2anything.py +++ b/librarian/book2anything.py @@ -32,10 +32,10 @@ class Book2Anything(object): Subclass it for any format you want to convert to. """ - format_cls = None # A formats.Format subclass - document_options = [] # List of Option objects for document options. - format_options = [] # List of Option objects for format customization. - build_options = [] # List of Option objects for build options. + format_cls = None # A formats.Format subclass + document_options = [] # List of Option objects for document options. + format_options = [] # List of Option objects for format customization. + build_options = [] # List of Option objects for build options. @classmethod def run(cls): @@ -45,12 +45,14 @@ class Book2Anything(object): parser = optparse.OptionParser(usage=usage) - parser.add_option('-v', '--verbose', - action='store_true', dest='verbose', default=False, - help='print status messages to stdout') - parser.add_option('-o', '--output-file', - dest='output_file', metavar='FILE', - help='specifies the output file') + parser.add_option( + '-v', '--verbose', + action='store_true', dest='verbose', default=False, + help='print status messages to stdout') + parser.add_option( + '-o', '--output-file', + dest='output_file', metavar='FILE', + help='specifies the output file') for option in cls.document_options + cls.format_options + cls.build_options: option.add(parser) @@ -58,7 +60,7 @@ class Book2Anything(object): if len(input_filenames) < 1: parser.print_help() - return(1) + return 1 # Prepare additional args for document. document_args = {} @@ -79,18 +81,18 @@ class Book2Anything(object): if options.verbose: print main_input - # Do the transformation. - doc = Document.from_file(main_input, **document_args) - format_ = cls.format_cls(doc, **format_args) + # Do the transformation. + doc = Document.from_file(main_input, **document_args) + format_ = cls.format_cls(doc, **format_args) - # Where to write output? - if not options.output_file: - output_file = os.path.splitext(main_input)[0] + '.' + format_.format_ext - else: - output_file = None + # Where to write output? + if not options.output_file: + output_file = os.path.splitext(main_input)[0] + '.' + format_.format_ext + else: + output_file = None - output = format_.build(**build_args) - output.save_as(output_file) + output = format_.build(**build_args) + output.save_as(output_file) except ParseError, e: print '%(file)s:%(name)s:%(message)s' % { diff --git a/librarian/dcparser.py b/librarian/dcparser.py deleted file mode 100644 index eddd8e5..0000000 --- a/librarian/dcparser.py +++ /dev/null @@ -1,383 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from xml.parsers.expat import ExpatError -from datetime import date -import time - -from librarian import (ValidationError, NoDublinCore, ParseError, DCNS, RDFNS, - WLURI) - -import lxml.etree as etree # ElementTree API using libxml2 -from lxml.etree import XMLSyntaxError - - -# ============== -# = Converters = -# ============== -class Person(object): - """Single person with last name and a list of first names.""" - def __init__(self, last_name, *first_names): - self.last_name = last_name - self.first_names = first_names - - @classmethod - def from_text(cls, text): - parts = [ token.strip() for token in text.split(',') ] - if len(parts) == 1: - surname = parts[0] - names = [] - elif len(parts) != 2: - raise ValueError("Invalid person name. There should be at most one comma: \"%s\"." % text) - else: - surname = parts[0] - if len(parts[1]) == 0: - # there is no non-whitespace data after the comma - raise ValueError("Found a comma, but no names given: \"%s\" -> %r." % (text, parts)) - names = [ name for name in parts[1].split() if len(name) ] # all non-whitespace tokens - return cls(surname, *names) - - def readable(self): - return u" ".join(self.first_names + (self.last_name,)) - - def __eq__(self, right): - return self.last_name == right.last_name and self.first_names == right.first_names - - def __cmp__(self, other): - return cmp((self.last_name, self.first_names), (other.last_name, other.first_names)) - - def __hash__(self): - return hash((self.last_name, self.first_names)) - - def __unicode__(self): - if len(self.first_names) > 0: - return '%s, %s' % (self.last_name, ' '.join(self.first_names)) - else: - return self.last_name - - def __repr__(self): - return 'Person(last_name=%r, first_names=*%r)' % (self.last_name, self.first_names) - -def as_date(text): - try: - try: - t = time.strptime(text, '%Y-%m-%d') - except ValueError: - t = time.strptime(text, '%Y') - return date(t[0], t[1], t[2]) - except ValueError, e: - raise ValueError("Unrecognized date format. Try YYYY-MM-DD or YYYY.") - -def as_person(text): - return Person.from_text(text) - -def as_unicode(text): - if isinstance(text, unicode): - return text - else: - return text.decode('utf-8') - -def as_wluri_strict(text): - return WLURI.strict(text) - -class Field(object): - def __init__(self, uri, attr_name, validator=as_unicode, strict=None, multiple=False, salias=None, **kwargs): - self.uri = uri - self.name = attr_name - self.validator = validator - self.strict = strict - self.multiple = multiple - self.salias = salias - - self.required = kwargs.get('required', True) and not kwargs.has_key('default') - self.default = kwargs.get('default', [] if multiple else [None]) - - def validate_value(self, val, strict=False): - if strict and self.strict is not None: - validator = self.strict - else: - validator = self.validator - try: - if self.multiple: - if validator is None: - return val - return [ validator(v) if v is not None else v for v in val ] - elif len(val) > 1: - raise ValidationError("Multiple values not allowed for field '%s'" % self.uri) - elif len(val) == 0: - raise ValidationError("Field %s has no value to assign. Check your defaults." % self.uri) - else: - if validator is None or val[0] is None: - return val[0] - return validator(val[0]) - except ValueError, e: - raise ValidationError("Field '%s' - invald value: %s" % (self.uri, e.message)) - - def validate(self, fdict, fallbacks=None, strict=False): - if fallbacks is None: - fallbacks = {} - if not fdict.has_key(self.uri): - if not self.required: - # Accept single value for single fields and saliases. - if self.name in fallbacks: - if self.multiple: - f = fallbacks[self.name] - else: - f = [fallbacks[self.name]] - elif self.salias and self.salias in fallbacks: - f = [fallbacks[self.salias]] - else: - f = self.default - else: - raise ValidationError("Required field %s not found" % self.uri) - else: - f = fdict[self.uri] - - return self.validate_value(f, strict=strict) - - def __eq__(self, other): - if isinstance(other, Field) and other.name == self.name: - return True - return False - - -class DCInfo(type): - def __new__(meta, classname, bases, class_dict): - fields = list(class_dict['FIELDS']) - - for base in bases[::-1]: - if hasattr(base, 'FIELDS'): - for field in base.FIELDS[::-1]: - try: - fields.index(field) - except ValueError: - fields.insert(0, field) - - class_dict['FIELDS'] = tuple(fields) - return super(DCInfo, meta).__new__(meta, classname, bases, class_dict) - - -class WorkInfo(object): - __metaclass__ = DCInfo - - FIELDS = ( - Field( DCNS('creator'), 'authors', as_person, salias='author', multiple=True), - Field( DCNS('title'), 'title'), - Field( DCNS('type'), 'type', required=False, multiple=True), - - Field( DCNS('contributor.editor'), 'editors', \ - as_person, salias='editor', multiple=True, default=[]), - Field( DCNS('contributor.technical_editor'), 'technical_editors', - as_person, salias='technical_editor', multiple=True, default=[]), - - Field( DCNS('date'), 'created_at', as_date), - Field( DCNS('date.pd'), 'released_to_public_domain_at', as_date, required=False), - Field( DCNS('publisher'), 'publisher'), - - Field( DCNS('language'), 'language'), - Field( DCNS('description'), 'description', required=False), - - Field( DCNS('source'), 'source_name', required=False), - Field( DCNS('source.URL'), 'source_url', required=False), - Field( DCNS('identifier.url'), 'url', WLURI, strict=as_wluri_strict), - Field( DCNS('rights.license'), 'license', required=False), - Field( DCNS('rights'), 'license_description'), - ) - - @classmethod - def from_string(cls, xml, *args, **kwargs): - from StringIO import StringIO - return cls.from_file(StringIO(xml), *args, **kwargs) - - @classmethod - def from_file(cls, xmlfile, *args, **kwargs): - desc_tag = None - try: - iter = etree.iterparse(xmlfile, ['start', 'end']) - for (event, element) in iter: - if element.tag == RDFNS('RDF') and event == 'start': - desc_tag = element - break - - if desc_tag is None: - raise NoDublinCore("DublinCore section not found. \ - Check if there are rdf:RDF and rdf:Description tags.") - - # continue 'till the end of RDF section - for (event, element) in iter: - if element.tag == RDFNS('RDF') and event == 'end': - break - - # if there is no end, Expat should yell at us with an ExpatError - - # extract data from the element and make the info - return cls.from_element(desc_tag, *args, **kwargs) - except XMLSyntaxError, e: - raise ParseError(e) - except ExpatError, e: - raise ParseError(e) - - @classmethod - def from_element(cls, rdf_tag, *args, **kwargs): - # the tree is already parsed, so we don't need to worry about Expat errors - field_dict = {} - desc = rdf_tag.find(".//" + RDFNS('Description')) - - if desc is None: - raise NoDublinCore("No DublinCore section found.") - - for e in desc.getchildren(): - fv = field_dict.get(e.tag, []) - fv.append(e.text) - field_dict[e.tag] = fv - - return cls(desc.attrib, field_dict, *args, **kwargs) - - def __init__(self, rdf_attrs, dc_fields, fallbacks=None, strict=False): - """rdf_attrs should be a dictionary-like object with any attributes of the RDF:Description. - dc_fields - dictionary mapping DC fields (with namespace) to list of text values for the - given field. """ - - self.about = rdf_attrs.get(RDFNS('about')) - self.fmap = {} - - for field in self.FIELDS: - value = field.validate(dc_fields, fallbacks=fallbacks, - strict=strict) - setattr(self, 'prop_' + field.name, value) - self.fmap[field.name] = field - if field.salias: self.fmap[field.salias] = field - - def __getattribute__(self, name): - try: - field = object.__getattribute__(self, 'fmap')[name] - value = object.__getattribute__(self, 'prop_'+field.name) - if field.name == name: - return value - else: # singular alias - if not field.multiple: - raise "OUCH!! for field %s" % name - - return value[0] if value else None - except (KeyError, AttributeError): - return object.__getattribute__(self, name) - - def __setattr__(self, name, newvalue): - try: - field = object.__getattribute__(self, 'fmap')[name] - if field.name == name: - object.__setattr__(self, 'prop_'+field.name, newvalue) - else: # singular alias - if not field.multiple: - raise "OUCH! while setting field %s" % name - - object.__setattr__(self, 'prop_'+field.name, [newvalue]) - except (KeyError, AttributeError): - return object.__setattr__(self, name, newvalue) - - def update(self, field_dict): - """Update using field_dict. Verify correctness, but don't check if all - required fields are present.""" - for field in self.FIELDS: - if field_dict.has_key(field.name): - setattr(self, field.name, field_dict[field.name]) - - def to_etree(self, parent = None): - """XML representation of this object.""" - #etree._namespace_map[str(self.RDF)] = 'rdf' - #etree._namespace_map[str(self.DC)] = 'dc' - - if parent is None: - root = etree.Element(RDFNS('RDF')) - else: - root = parent.makeelement(RDFNS('RDF')) - - description = etree.SubElement(root, RDFNS('Description')) - - if self.about: - description.set(RDFNS('about'), self.about) - - for field in self.FIELDS: - v = getattr(self, field.name, None) - if v is not None: - if field.multiple: - if len(v) == 0: continue - for x in v: - e = etree.Element(field.uri) - if x is not None: - e.text = unicode(x) - description.append(e) - else: - e = etree.Element(field.uri) - e.text = unicode(v) - description.append(e) - - return root - - def serialize(self): - rdf = {} - rdf['about'] = { 'uri': RDFNS('about'), 'value': self.about } - - dc = {} - for field in self.FIELDS: - v = getattr(self, field.name, None) - if v is not None: - if field.multiple: - if len(v) == 0: continue - v = [ unicode(x) for x in v if x is not None ] - else: - v = unicode(v) - - dc[field.name] = {'uri': field.uri, 'value': v} - rdf['fields'] = dc - return rdf - - def to_dict(self): - result = {'about': self.about} - for field in self.FIELDS: - v = getattr(self, field.name, None) - - if v is not None: - if field.multiple: - if len(v) == 0: continue - v = [ unicode(x) for x in v if x is not None ] - else: - v = unicode(v) - result[field.name] = v - - if field.salias: - v = getattr(self, field.salias) - if v is not None: result[field.salias] = unicode(v) - - return result - - -class BookInfo(WorkInfo): - FIELDS = ( - Field( DCNS('audience'), 'audiences', salias='audience', multiple=True, - required=False), - - Field( DCNS('subject.period'), 'epochs', salias='epoch', multiple=True, - required=False), - Field( DCNS('subject.type'), 'kinds', salias='kind', multiple=True, - required=False), - Field( DCNS('subject.genre'), 'genres', salias='genre', multiple=True, - required=False), - - Field( DCNS('contributor.translator'), 'translators', \ - as_person, salias='translator', multiple=True, default=[]), - Field( DCNS('relation.hasPart'), 'parts', - WLURI, strict=as_wluri_strict, multiple=True, required=False), - Field( DCNS('relation.isVariantOf'), 'variant_of', - WLURI, strict=as_wluri_strict, required=False), - - Field( DCNS('relation.coverImage.url'), 'cover_url', required=False), - Field( DCNS('relation.coverImage.attribution'), 'cover_by', required=False), - Field( DCNS('relation.coverImage.source'), 'cover_source', required=False), - ) - - -def parse(file_name, cls=BookInfo): - return cls.from_file(file_name) diff --git a/librarian/document.py b/librarian/document.py index acc80ae..a3251a6 100755 --- a/librarian/document.py +++ b/librarian/document.py @@ -34,9 +34,8 @@ class Document(object): raise ValueError("Invalid root element. Found '%s', should be '%s'" % ( root_elem.tag, SSTNS('section'))) else: - raise ValueError("Invalid class of root element. " - "Use librarian.parser.SSTParser.") - #print etree.tostring(self.edoc.getroot()) + raise ValueError("Invalid class of root element. Use librarian.parser.SSTParser.") + # print etree.tostring(self.edoc.getroot()) @classmethod def from_string(cls, xml, *args, **kwargs): diff --git a/librarian/epub.py b/librarian/epub.py deleted file mode 100644 index 10922d4..0000000 --- a/librarian/epub.py +++ /dev/null @@ -1,563 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from __future__ import with_statement - -import os -import os.path -import re -import subprocess -from StringIO import StringIO -from copy import deepcopy -from lxml import etree -import zipfile -from tempfile import mkdtemp, NamedTemporaryFile -from shutil import rmtree - -from librarian import RDFNS, WLNS, NCXNS, OPFNS, XHTMLNS, OutputFile -from librarian.cover import WLCover - -from librarian import functions, get_resource - -functions.reg_person_name() - - -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) 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) - xyz - """ - - p = etree.fromstring('%s' % text) - node.text = p.text - node[:] = p[:] - - -def node_name(node): - """ Find out a node's name - - >>> print node_name(etree.fromstring('XYZ')) - XYZ - """ - - tempnode = deepcopy(node) - - for p in ('pe', 'pa', 'pt', 'pr', 'motyw'): - for e in tempnode.findall('.//%s' % p): - t = e.tail - e.clear() - e.tail = t - etree.strip_tags(tempnode, '*') - return tempnode.text - - -def xslt(xml, sheet): - if isinstance(xml, etree._Element): - xml = etree.ElementTree(xml) - with open(sheet) as xsltf: - return xml.xslt(etree.parse(xsltf)) - - -def replace_characters(node): - def replace_chars(text): - if text is None: - return None - return text.replace(u"\ufeff", u"")\ - .replace("---", u"\u2014")\ - .replace("--", u"\u2013")\ - .replace(",,", u"\u201E")\ - .replace('"', u"\u201D")\ - .replace("'", u"\u2019") - if node.tag in ('uwaga', 'extra'): - t = node.tail - node.clear() - node.tail = t - node.text = replace_chars(node.text) - node.tail = replace_chars(node.tail) - for child in node: - replace_characters(child) - - -def find_annotations(annotations, source, part_no): - for child in source: - if child.tag in ('pe', 'pa', 'pt', 'pr'): - annotation = deepcopy(child) - number = str(len(annotations)+1) - annotation.set('number', number) - annotation.set('part', str(part_no)) - annotation.tail = '' - annotations.append(annotation) - tail = child.tail - child.clear() - child.tail = tail - child.text = number - if child.tag not in ('extra', 'uwaga'): - find_annotations(annotations, child, part_no) - - -class Stanza(object): - """ - Converts / verse endings into verse elements in a stanza. - - Slashes may only occur directly in the stanza. Any slashes in subelements - will be ignored, and the subelements will be put inside verse elements. - - >>> s = etree.fromstring("a/\\nbx/\\nyc/ \\nd") - >>> Stanza(s).versify() - >>> print etree.tostring(s) - abx/ - ycd - - """ - def __init__(self, stanza_elem): - self.stanza = stanza_elem - self.verses = [] - self.open_verse = None - - def versify(self): - self.push_text(self.stanza.text) - for elem in self.stanza: - self.push_elem(elem) - self.push_text(elem.tail) - tail = self.stanza.tail - self.stanza.clear() - self.stanza.tail = tail - self.stanza.extend(self.verses) - - def open_normal_verse(self): - self.open_verse = self.stanza.makeelement("wers_normalny") - self.verses.append(self.open_verse) - - def get_open_verse(self): - if self.open_verse is None: - self.open_normal_verse() - return self.open_verse - - def push_text(self, text): - if not text or not text.strip(): - return - for i, verse_text in enumerate(re.split(r"/\s*\n", text)): - if i: - self.open_normal_verse() - verse = self.get_open_verse() - if len(verse): - verse[-1].tail = (verse[-1].tail or "") + verse_text.strip() - else: - verse.text = (verse.text or "") + verse_text.strip() - - def push_elem(self, elem): - if elem.tag.startswith("wers"): - verse = deepcopy(elem) - verse.tail = None - self.verses.append(verse) - self.open_verse = verse - else: - appended = deepcopy(elem) - appended.tail = None - self.get_open_verse().append(appended) - - -def replace_by_verse(tree): - """ Find stanzas and create new verses in place of a '/' character """ - - stanzas = tree.findall('.//' + WLNS('strofa')) - for stanza in stanzas: - Stanza(stanza).versify() - - -def add_to_manifest(manifest, partno): - """ Adds a node to the manifest section in content.opf file """ - - partstr = 'part%d' % partno - e = manifest.makeelement(OPFNS('item'), attrib={ - 'id': partstr, - 'href': partstr + '.html', - 'media-type': 'application/xhtml+xml', - }) - manifest.append(e) - - -def add_to_spine(spine, partno): - """ Adds a node to the spine section in content.opf file """ - - e = spine.makeelement(OPFNS('itemref'), attrib={'idref': 'part%d' % partno}); - spine.append(e) - - -class TOC(object): - def __init__(self, name=None, part_href=None): - self.children = [] - self.name = name - self.part_href = part_href - self.sub_number = None - - def add(self, name, part_href, level=0, is_part=True, index=None): - assert level == 0 or index is None - if level > 0 and self.children: - return self.children[-1].add(name, part_href, level-1, is_part) - else: - t = TOC(name) - t.part_href = part_href - if index is not None: - self.children.insert(index, t) - else: - self.children.append(t) - if not is_part: - t.sub_number = len(self.children) + 1 - return t.sub_number - - def append(self, toc): - self.children.append(toc) - - def extend(self, toc): - self.children.extend(toc.children) - - def depth(self): - if self.children: - return max((c.depth() for c in self.children)) + 1 - else: - return 0 - - def href(self): - src = self.part_href - if self.sub_number is not None: - src += '#sub%d' % self.sub_number - return src - - def write_to_xml(self, nav_map, counter=1): - for child in self.children: - nav_point = nav_map.makeelement(NCXNS('navPoint')) - nav_point.set('id', 'NavPoint-%d' % counter) - nav_point.set('playOrder', str(counter)) - - nav_label = nav_map.makeelement(NCXNS('navLabel')) - text = nav_map.makeelement(NCXNS('text')) - text.text = child.name - nav_label.append(text) - nav_point.append(nav_label) - - content = nav_map.makeelement(NCXNS('content')) - content.set('src', child.href()) - nav_point.append(content) - nav_map.append(nav_point) - counter = child.write_to_xml(nav_point, counter + 1) - return counter - - def html_part(self, depth=0): - texts = [] - for child in self.children: - texts.append( - "" % - (depth, child.href(), child.name)) - texts.append(child.html_part(depth+1)) - return "\n".join(texts) - - def html(self): - with open(get_resource('epub/toc.html')) as f: - t = unicode(f.read(), 'utf-8') - return t % self.html_part() - - -def used_chars(element): - """ Lists characters used in an ETree Element """ - chars = set((element.text or '') + (element.tail or '')) - for child in element: - chars = chars.union(used_chars(child)) - return chars - - -def chop(main_text): - """ divide main content of the XML file into chunks """ - - # prepare a container for each chunk - part_xml = etree.Element('utwor') - etree.SubElement(part_xml, 'master') - main_xml_part = part_xml[0] # master - - last_node_part = False - for one_part in main_text: - name = one_part.tag - if name == 'naglowek_czesc': - yield part_xml - last_node_part = True - main_xml_part[:] = [deepcopy(one_part)] - elif not last_node_part and name in ("naglowek_rozdzial", "naglowek_akt", "srodtytul"): - yield part_xml - main_xml_part[:] = [deepcopy(one_part)] - else: - main_xml_part.append(deepcopy(one_part)) - last_node_part = False - yield part_xml - - -def transform_chunk(chunk_xml, chunk_no, annotations, empty=False, _empty_html_static=[]): - """ transforms one chunk, returns a HTML string, a TOC object and a set of used characters """ - - toc = TOC() - for element in chunk_xml[0]: - if element.tag in ("naglowek_czesc", "naglowek_rozdzial", "naglowek_akt", "srodtytul"): - toc.add(node_name(element), "part%d.html" % chunk_no) - elif element.tag in ('naglowek_podrozdzial', 'naglowek_scena'): - subnumber = toc.add(node_name(element), "part%d.html" % chunk_no, level=1, is_part=False) - element.set('sub', str(subnumber)) - if empty: - if not _empty_html_static: - _empty_html_static.append(open(get_resource('epub/emptyChunk.html')).read()) - chars = set() - output_html = _empty_html_static[0] - else: - find_annotations(annotations, chunk_xml, chunk_no) - replace_by_verse(chunk_xml) - html_tree = xslt(chunk_xml, get_resource('epub/xsltScheme.xsl')) - chars = used_chars(html_tree.getroot()) - output_html = etree.tostring(html_tree, method="html", pretty_print=True) - return output_html, toc, chars - - -def transform(wldoc, verbose=False, - style=None, html_toc=False, - sample=None, cover=None, flags=None): - """ 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): - """ processes one input file and proceeds to its children """ - - replace_characters(wldoc.edoc.getroot()) - - # every input file will have a TOC entry, - # pointing to starting chunk - toc = TOC(wldoc.book_info.title, "part%d.html" % chunk_counter) - chars = set() - if first: - # write book title page - html_tree = xslt(wldoc.edoc, get_resource('epub/xsltTitle.xsl')) - chars = used_chars(html_tree.getroot()) - zip.writestr('OPS/title.html', - etree.tostring(html_tree, method="html", pretty_print=True)) - # add a title page TOC entry - toc.add(u"Strona tytułowa", "title.html") - elif wldoc.book_info.parts: - # write title page for every parent - if sample is not None and sample <= 0: - chars = set() - html_string = open(get_resource('epub/emptyChunk.html')).read() - else: - html_tree = xslt(wldoc.edoc, get_resource('epub/xsltChunkTitle.xsl')) - chars = used_chars(html_tree.getroot()) - html_string = etree.tostring(html_tree, method="html", pretty_print=True) - zip.writestr('OPS/part%d.html' % chunk_counter, html_string) - add_to_manifest(manifest, chunk_counter) - add_to_spine(spine, chunk_counter) - chunk_counter += 1 - - if len(wldoc.edoc.getroot()) > 1: - # rdf before style master - main_text = wldoc.edoc.getroot()[1] - else: - # rdf in style master - main_text = wldoc.edoc.getroot()[0] - if main_text.tag == RDFNS('RDF'): - main_text = None - - if main_text is not None: - for chunk_xml in chop(main_text): - empty = False - if sample is not None: - if sample <= 0: - empty = True - else: - sample -= len(chunk_xml.xpath('//strofa|//akap|//akap_cd|//akap_dialog')) - chunk_html, chunk_toc, chunk_chars = transform_chunk(chunk_xml, chunk_counter, annotations, empty) - - toc.extend(chunk_toc) - chars = chars.union(chunk_chars) - zip.writestr('OPS/part%d.html' % chunk_counter, chunk_html) - add_to_manifest(manifest, chunk_counter) - add_to_spine(spine, chunk_counter) - chunk_counter += 1 - - for child in wldoc.parts(): - child_toc, chunk_counter, chunk_chars, sample = transform_file( - child, chunk_counter, first=False, sample=sample) - toc.append(child_toc) - chars = chars.union(chunk_chars) - - return toc, chunk_counter, chars, sample - - - document = deepcopy(wldoc) - del wldoc - - if flags: - for flag in flags: - document.edoc.getroot().set(flag, 'yes') - - # add editors info - document.edoc.getroot().set('editors', u', '.join(sorted( - editor.readable() for editor in document.editors()))) - - opf = xslt(document.book_info.to_etree(), get_resource('epub/xsltContent.xsl')) - manifest = opf.find('.//' + OPFNS('manifest')) - guide = opf.find('.//' + OPFNS('guide')) - spine = opf.find('.//' + OPFNS('spine')) - - output_file = NamedTemporaryFile(prefix='librarian', suffix='.epub', delete=False) - zip = zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) - - # write static elements - mime = zipfile.ZipInfo() - mime.filename = 'mimetype' - mime.compress_type = zipfile.ZIP_STORED - mime.extra = '' - zip.writestr(mime, 'application/epub+zip') - zip.writestr('META-INF/container.xml', '' \ - '' \ - '') - zip.write(get_resource('res/wl-logo-small.png'), os.path.join('OPS', 'logo_wolnelektury.png')) - zip.write(get_resource('res/jedenprocent.png'), os.path.join('OPS', 'jedenprocent.png')) - if not style: - style = get_resource('epub/style.css') - zip.write(style, os.path.join('OPS', 'style.css')) - - if cover: - if cover is True: - cover = WLCover - - cover_file = StringIO() - bound_cover = cover(document.book_info) - bound_cover.save(cover_file) - cover_name = 'cover.%s' % bound_cover.ext() - zip.writestr(os.path.join('OPS', cover_name), cover_file.getvalue()) - del cover_file - - cover_tree = etree.parse(get_resource('epub/cover.html')) - cover_tree.find('//' + XHTMLNS('img')).set('src', cover_name) - zip.writestr('OPS/cover.html', etree.tostring( - cover_tree, method="html", pretty_print=True)) - - if bound_cover.uses_dc_cover: - if document.book_info.cover_by: - document.edoc.getroot().set('data-cover-by', document.book_info.cover_by) - if document.book_info.cover_source: - document.edoc.getroot().set('data-cover-source', document.book_info.cover_source) - - manifest.append(etree.fromstring( - '')) - manifest.append(etree.fromstring( - '' % (cover_name, bound_cover.mime_type()))) - spine.insert(0, etree.fromstring('')) - opf.getroot()[0].append(etree.fromstring('')) - guide.append(etree.fromstring('')) - - - annotations = etree.Element('annotations') - - toc_file = etree.fromstring('' \ - '' \ - '') - nav_map = toc_file[-1] - - if html_toc: - manifest.append(etree.fromstring( - '')) - spine.append(etree.fromstring( - '')) - guide.append(etree.fromstring('')) - - toc, chunk_counter, chars, sample = transform_file(document, sample=sample) - - if len(toc.children) < 2: - toc.add(u"Początek utworu", "part1.html") - - # Last modifications in container files and EPUB creation - if len(annotations) > 0: - toc.add("Przypisy", "annotations.html") - manifest.append(etree.fromstring( - '')) - spine.append(etree.fromstring( - '')) - replace_by_verse(annotations) - html_tree = xslt(annotations, get_resource('epub/xsltAnnotations.xsl')) - chars = chars.union(used_chars(html_tree.getroot())) - zip.writestr('OPS/annotations.html', etree.tostring( - html_tree, method="html", pretty_print=True)) - - toc.add("Strona redakcyjna", "last.html") - manifest.append(etree.fromstring( - '')) - spine.append(etree.fromstring( - '')) - html_tree = xslt(document.edoc, get_resource('epub/xsltLast.xsl')) - chars.update(used_chars(html_tree.getroot())) - zip.writestr('OPS/last.html', etree.tostring( - html_tree, method="html", pretty_print=True)) - - if not flags or not 'without-fonts' in flags: - # strip fonts - 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')) - for fname in 'DejaVuSerif.ttf', 'DejaVuSerif-Bold.ttf', 'DejaVuSerif-Italic.ttf', 'DejaVuSerif-BoldItalic.ttf': - optimizer_call = ['perl', 'subset.pl', '--chars', ''.join(chars).encode('utf-8'), - get_resource('fonts/' + fname), os.path.join(tmpdir, fname)] - if verbose: - print "Running font-optimizer" - subprocess.check_call(optimizer_call) - else: - subprocess.check_call(optimizer_call, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - zip.write(os.path.join(tmpdir, fname), os.path.join('OPS', fname)) - manifest.append(etree.fromstring( - '' % (fname, fname))) - rmtree(tmpdir) - if cwd is not None: - os.chdir(cwd) - - zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True)) - title = document.book_info.title - attributes = "dtb:uid", "dtb:depth", "dtb:totalPageCount", "dtb:maxPageNumber" - for st in attributes: - meta = toc_file.makeelement(NCXNS('meta')) - meta.set('name', st) - meta.set('content', '0') - toc_file[0].append(meta) - toc_file[0][0].set('content', ''.join((title, 'WolneLektury.pl'))) - toc_file[0][1].set('content', str(toc.depth())) - set_inner_xml(toc_file[1], ''.join(('', title, ''))) - - # write TOC - if html_toc: - toc.add(u"Spis treści", "toc.html", index=1) - zip.writestr('OPS/toc.html', toc.html().encode('utf-8')) - toc.write_to_xml(nav_map) - zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True)) - zip.close() - - return OutputFile.from_filename(output_file.name) diff --git a/librarian/fb2.py b/librarian/fb2.py deleted file mode 100644 index d979566..0000000 --- a/librarian/fb2.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import os.path -from copy import deepcopy -from lxml import etree - -from librarian import functions, OutputFile -from .epub import replace_by_verse - - -functions.reg_substitute_entities() -functions.reg_person_name() - - -def sectionify(tree): - """Finds section headers and adds a tree of _section tags.""" - sections = ['naglowek_czesc', - 'naglowek_akt', 'naglowek_rozdzial', 'naglowek_scena', - 'naglowek_podrozdzial'] - section_level = dict((v,k) for (k,v) in enumerate(sections)) - - # We can assume there are just subelements an no text at section level. - for level, section_name in reversed(list(enumerate(sections))): - for header in tree.findall('//' + section_name): - section = header.makeelement("_section") - header.addprevious(section) - section.append(header) - sibling = section.getnext() - while (sibling is not None and - section_level.get(sibling.tag, 1000) > level): - section.append(sibling) - sibling = section.getnext() - - -def transform(wldoc, verbose=False, - cover=None, flags=None): - """ produces a FB2 file - - cover: a cover.Cover object or True for default - flags: less-advertising, working-copy - """ - - document = deepcopy(wldoc) - del wldoc - - if flags: - for flag in flags: - document.edoc.getroot().set(flag, 'yes') - - style_filename = os.path.join(os.path.dirname(__file__), 'fb2/fb2.xslt') - style = etree.parse(style_filename) - - replace_by_verse(document.edoc) - sectionify(document.edoc) - - result = document.transform(style) - - return OutputFile.from_string(unicode(result).encode('utf-8')) - -# vim:et diff --git a/librarian/formats/__init__.py b/librarian/formats/__init__.py index cfe4fc2..8f8556f 100644 --- a/librarian/formats/__init__.py +++ b/librarian/formats/__init__.py @@ -3,6 +3,8 @@ # This file is part of Librarian, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # + + class Format(object): """ Generic format class. """ def __init__(self, doc): diff --git a/librarian/formats/cover/__init__.py b/librarian/formats/cover/__init__.py index b9b515a..d410058 100644 --- a/librarian/formats/cover/__init__.py +++ b/librarian/formats/cover/__init__.py @@ -4,9 +4,9 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # import re -from PIL import Image, ImageFont, ImageDraw, ImageFilter, ImageEnhance +from PIL import Image, ImageFont, ImageDraw, ImageFilter from StringIO import StringIO -from librarian import DCNS, URLOpener +from librarian import DCNS from librarian.output import OutputFile from librarian.utils import get_resource from librarian.formats import Format @@ -143,6 +143,7 @@ class Cover(Format): } def __init__(self, doc, format=None, width=None, height=None): + super(Cover, self).__init__(doc) self.author = ", ".join(auth for auth in doc.meta.get(DCNS('creator'))) self.title = doc.meta.title() if format is not None: @@ -185,7 +186,8 @@ class Cover(Format): author_font = ImageFont.truetype( self.author_font_ttf, metr.author_font_size) - tbox.text(self.pretty_author(), self.author_color, author_font, + tbox.text( + self.pretty_author(), self.author_color, author_font, metr.author_lineskip, self.author_shadow) text_img = tbox.image() img.paste(text_img, (metr.author_margin_left, top), text_img) @@ -197,15 +199,16 @@ class Cover(Format): ) title_font = ImageFont.truetype( self.title_font_ttf, metr.title_font_size) - tbox.text(self.pretty_title(), self.title_color, title_font, + tbox.text( + self.pretty_title(), self.title_color, title_font, metr.title_lineskip, self.title_shadow) text_img = tbox.image() img.paste(text_img, (metr.title_margin_left, top), text_img) return img - imgstr = StringIO() - img.save(imgstr, format=self.format, quality=95) - OutputFile.from_string(imgstr.getvalue()) + # imgstr = StringIO() + # img.save(imgstr, format=self.format, quality=95) + # OutputFile.from_stringing(imgstr.getvalue()) def mime_type(self): return self.mime_types[self.format] diff --git a/librarian/formats/cover/evens/__init__.py b/librarian/formats/cover/evens/__init__.py index 4207d46..e470001 100644 --- a/librarian/formats/cover/evens/__init__.py +++ b/librarian/formats/cover/evens/__init__.py @@ -18,10 +18,6 @@ class EvensCover(Cover): title_top = 30 logo_bottom = 100 - def __init__(self, doc, format=None, width=None, height=None): - super(EvensCover, self).__init__(doc, format=format, width=width, height=height) - self.doc = doc - def set_images(self, ctx): cover_url = self.doc.meta.get(DCNS('relation.coverimage.url'))[0] if cover_url.startswith('file://'): diff --git a/librarian/formats/cover/wolnelektury/__init__.py b/librarian/formats/cover/wolnelektury/__init__.py index 4218770..0824d51 100644 --- a/librarian/formats/cover/wolnelektury/__init__.py +++ b/librarian/formats/cover/wolnelektury/__init__.py @@ -4,6 +4,8 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # from PIL import Image, ImageFont, ImageDraw +from PIL import ImageEnhance + from librarian.utils import get_resource from .. import Cover, Metric, TextBox @@ -60,15 +62,15 @@ class WLCover(Cover): self.epoch = doc.meta.get_one('epoch') self.with_logo = with_logo # TODO - if doc.meta.get('cover_url'): - url = doc.meta.get('cover_url')[0] - bg_src = None - if bg_src is None: - bg_src = URLOpener().open(url) - self.background_img = StringIO(bg_src.read()) - bg_src.close() - else: - self.background_img = self.default_background + # if doc.meta.get('cover_url'): + # url = doc.meta.get('cover_url')[0] + # bg_src = None + # if bg_src is None: + # bg_src = URLOpener().open(url) + # self.background_img = StringIO(bg_src.read()) + # bg_src.close() + # else: + self.background_img = self.default_background def pretty_author(self): return self.author.upper() @@ -110,26 +112,29 @@ class WLCover(Cover): box = TextBox(metr.title_box_width, metr.height, padding_y=metr.box_padding_y) author_font = ImageFont.truetype( self.author_font_ttf, metr.author_font_size) - box.text(self.pretty_author(), - font=author_font, - line_height=metr.author_lineskip, - color=self.author_color, - shadow_color=self.author_shadow, - ) + box.text( + self.pretty_author(), + font=author_font, + line_height=metr.author_lineskip, + color=self.author_color, + shadow_color=self.author_shadow, + ) box.skip(metr.box_above_line) - box.draw.line((metr.box_line_left, box.height, metr.box_line_right, box.height), - fill=self.author_color, width=metr.box_line_width) + box.draw.line( + (metr.box_line_left, box.height, metr.box_line_right, box.height), + fill=self.author_color, width=metr.box_line_width) box.skip(metr.box_below_line) title_font = ImageFont.truetype( self.title_font_ttf, metr.title_font_size) - box.text(self.pretty_title(), - line_height=metr.title_lineskip, - font=title_font, - color=epoch_color, - shadow_color=self.title_shadow, - ) + box.text( + self.pretty_title(), + line_height=metr.title_lineskip, + font=title_font, + color=epoch_color, + shadow_color=self.title_shadow, + ) if self.with_logo: logo = Image.open(get_resource('res/wl-logo-mono.png')) @@ -151,15 +156,13 @@ class WLCover(Cover): # center box_top = (metr.height - box_img.size[1]) / 2 - box_left = metr.bar_width + (metr.width - metr.bar_width - - box_img.size[0]) / 2 - draw.rectangle((box_left, box_top, - box_left + box_img.size[0], box_top + box_img.size[1]), - fill='#fff') + box_left = metr.bar_width + (metr.width - metr.bar_width - box_img.size[0]) / 2 + draw.rectangle((box_left, box_top, box_left + box_img.size[0], box_top + box_img.size[1]), fill='#fff') img.paste(box_img, (box_left, box_top), box_img) if self.with_logo: - img.paste(logo, + img.paste( + logo, (box_left + (box_img.size[0] - logo.size[0]) / 2, box_top + box_img.size[1] - metr.box_padding_y - logo.size[1]), mask=logo) diff --git a/librarian/formats/html/__init__.py b/librarian/formats/html/__init__.py index 2cf2601..ae6470a 100644 --- a/librarian/formats/html/__init__.py +++ b/librarian/formats/html/__init__.py @@ -40,7 +40,7 @@ class HtmlFormat(Format): t.find('.//div[@id="content"]').extend( self.render(self.doc.edoc.getroot(), ctx)) - #t.find('.//div[@id="toc"]').append(ctx.toc.render()) + # t.find('.//div[@id="toc"]').append(ctx.toc.render()) t.find('.//div[@id="footnotes"]').extend(ctx.footnotes.output) return OutputFile.from_string(etree.tostring( @@ -81,7 +81,8 @@ class Footnotes(object): def append(self, item): self.counter += 1 - e = etree.Element("a", + e = etree.Element( + "a", href="#footnote-anchor-%d" % self.counter, id="footnote-%d" % self.counter, style="float:left;margin-right:1em") @@ -89,7 +90,8 @@ class Footnotes(object): e.tail = " " self.output.append(e) self.output.extend(item) - anchor = etree.Element("a", + anchor = etree.Element( + "a", id="footnote-anchor-%d" % self.counter, href="#footnote-%d" % self.counter) anchor.text = "[%d]" % self.counter @@ -131,6 +133,7 @@ class TOC(object): HtmlFormat.renderers.register(core.Aside, None, NaturalText('aside')) HtmlFormat.renderers.register(core.Aside, 'comment', Silent()) + class AsideFootnote(NaturalText): def render(self, element, ctx): output = super(AsideFootnote, self).render(element, ctx) @@ -150,21 +153,23 @@ class Header(NaturalText): else: root[0].tag = 'h2' if root[0].text: - d = etree.SubElement(root[0], 'a', {'id': root[0].text, 'style': 'pointer: hand; color:#ddd; font-size:.8em'}) - #d.text = "per" + d = etree.SubElement( + root[0], 'a', {'id': root[0].text, 'style': 'pointer: hand; color:#ddd; font-size:.8em'}) + # d.text = "per" return root - + HtmlFormat.renderers.register(core.Header, None, Header('h1')) HtmlFormat.renderers.register(core.Div, None, NaturalText('div')) + class DivDefined(NaturalText): def render(self, element, ctx): output = super(DivDefined, self).render(element, ctx) output[0].text = (output[0].text or '') + ':' - output[0].attrib['id'] = output[0].text # not so cool? + output[0].attrib['id'] = output[0].text # not so cool? return output HtmlFormat.renderers.register(core.Div, 'defined', DivDefined('dt', {'style': 'display: inline-block'})) @@ -186,11 +191,12 @@ HtmlFormat.renderers.register(core.Div, 'item', NaturalText('li')) HtmlFormat.renderers.register(core.Div, 'list', NaturalText('ul')) HtmlFormat.renderers.register(core.Div, 'list.enum', NaturalText('ol')) + class DivListDefinitions(NaturalText): def render(self, element, ctx): output = super(DivListDefinitions, self).render(element, ctx) - #if ctx.toc_level > 2: - # output[0].attrib['style'] = 'float: right' + # if ctx.toc_level > 2: + # output[0].attrib['style'] = 'float: right' return output HtmlFormat.renderers.register(core.Div, 'list.definitions', DivListDefinitions('ul')) @@ -215,6 +221,7 @@ HtmlFormat.renderers.register(core.Span, 'cite.code', LiteralText('code')) HtmlFormat.renderers.register(core.Span, 'emph', NaturalText('em')) HtmlFormat.renderers.register(core.Span, 'emp', NaturalText('strong')) + class SpanUri(LiteralText): def render(self, element, ctx): root = super(SpanUri, self).render(element, ctx) @@ -222,6 +229,7 @@ class SpanUri(LiteralText): return root HtmlFormat.renderers.register(core.Span, 'uri', SpanUri('a')) + class SpanLink(LiteralText): def render(self, element, ctx): root = super(SpanLink, self).render(element, ctx) @@ -231,4 +239,3 @@ class SpanLink(LiteralText): root[0].attrib['href'] = src return root HtmlFormat.renderers.register(core.Span, 'link', SpanLink('a')) - diff --git a/librarian/formats/pdf/__init__.py b/librarian/formats/pdf/__init__.py index 298db09..e8e936b 100644 --- a/librarian/formats/pdf/__init__.py +++ b/librarian/formats/pdf/__init__.py @@ -4,7 +4,6 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # import os -import re import shutil from subprocess import call, PIPE from tempfile import NamedTemporaryFile, mkdtemp @@ -16,7 +15,7 @@ from librarian import DCNS, XMLNamespace from librarian.formats import Format from librarian.output import OutputFile from librarian.renderers import Register, TreeRenderer -from librarian.utils import Context, get_resource, extend_element +from librarian.utils import Context, get_resource from librarian import core from PIL import Image from ..html import Silent @@ -80,7 +79,8 @@ class PdfFormat(Format): call(['convert', save_as + '_.' + ext, save_as]) else: # JPEGs with bad density will break LaTeX with 'Dimension too large'. - r = call(['convert', '-units', 'PixelsPerInch', save_as + '_.' + ext, '-density', '300', save_as + '_2.' + ext]) + r = call(['convert', '-units', 'PixelsPerInch', save_as + '_.' + ext, '-density', '300', + save_as + '_2.' + ext]) if r: shutil.move(save_as + '_.' + ext, save_as) else: @@ -124,7 +124,7 @@ class PdfFormat(Format): img = Image.open(self.get_file(build_ctx, 'cover.png')) size = img.size - if (size[1] > size[0]): + if size[1] > size[0]: img = img.crop((0, 0, size[0], size[0])) img.save(self.get_file(build_ctx, 'cover.png'), format=img.format, quality=90) size = img.size @@ -146,14 +146,14 @@ class PdfFormat(Format): p[0].append(texml_cmd("noindent")) p[0].append(texml_cmd("nohyphens", author)) p[0].append(texml_cmd("vspace", "1em")) - #p[0][-1].tail = author + # p[0][-1].tail = author if title: p = texml_cmd("par", "") grp.append(p) p[0].append(texml_cmd("Huge")) p[0].append(texml_cmd("noindent")) p[0].append(texml_cmd("nohyphens", title)) - #p[0][-1].tail = title + # p[0][-1].tail = title doc.append(texml_cmd("vfill")) doc.append(texml_cmd("vfill")) @@ -161,7 +161,7 @@ class PdfFormat(Format): cover_logo_url = getattr(build_ctx, 'cover_logo', None) # TEST # TODO: convert - #cover_logo_url = 'http://milpeer.mdrn.pl/media/dynamic/people/logo/nowoczesnapolska.org.pl.png' + # cover_logo_url = 'http://milpeer.mdrn.pl/media/dynamic/people/logo/nowoczesnapolska.org.pl.png' if cover_logo_url: self.add_file(build_ctx, 'coverlogo.png', cover_logo_url, image=True) size = Image.open(self.get_file(build_ctx, 'coverlogo.png')).size @@ -183,11 +183,10 @@ class PdfFormat(Format): doc.append(texml_cmd("vspace", "1em")) for m, f in ( - ('Publisher: ', DCNS('publisher')), - ('Rights: ', DCNS('rights')), - ('Intended audience: ', DCNS('audience')), - ('', DCNS('description')), - ): + ('Publisher: ', DCNS('publisher')), + ('Rights: ', DCNS('rights')), + ('Intended audience: ', DCNS('audience')), + ('', DCNS('description'))): v = self.doc.meta.get_one(f) if v: e = texml_cmd("par", "") @@ -196,7 +195,6 @@ class PdfFormat(Format): doc.append(e) doc.append(texml_cmd("vspace", "1em")) - e = texml_cmd("par", "") e[0].append(texml_cmd("noindent")) e[0][0].tail = "Resource prepared using " @@ -205,7 +203,7 @@ class PdfFormat(Format): doc.append(e) source_url = getattr(build_ctx, 'source_url', None) - #source_url = 'http://milpeer.mdrn.pl/documents/27/' + # source_url = 'http://milpeer.mdrn.pl/documents/27/' if source_url: e = texml_cmd("par", "") doc.append(e) @@ -220,16 +218,14 @@ class PdfFormat(Format): texml = self.get_texml(ctx) tex_path = os.path.join(ctx.workdir, 'doc.tex') with open(tex_path, 'w') as fout: - #print etree.tostring(texml) + # print etree.tostring(texml) process(StringIO(etree.tostring(texml)), fout, 'utf-8') - #~ if self.save_tex: - #~ shutil.copy(tex_path, self.save_tex) - + # if self.save_tex: + # shutil.copy(tex_path, self.save_tex) - - #for sfile in ['wasysym.sty', 'uwasyvar.fd', 'uwasy.fd']: - # shutil.copy(get_resource(os.path.join('res/wasysym', sfile)), temp) + # for sfile in ['wasysym.sty', 'uwasyvar.fd', 'uwasy.fd']: + # shutil.copy(get_resource(os.path.join('res/wasysym', sfile)), temp) return ctx.workdir def build(self, ctx=None, verbose=False): @@ -247,9 +243,9 @@ class PdfFormat(Format): else: for i in range(self.tex_passes): p = call(['xelatex', '-interaction=batchmode', tex_path], - stdout=PIPE, stderr=PIPE) + stdout=PIPE, stderr=PIPE) if p: - #raise ParseError("Error parsing .tex file: %s" % tex_path) + # raise ParseError("Error parsing .tex file: %s" % tex_path) raise RuntimeError("Error parsing .tex file: %s" % tex_path) if cwd is not None: @@ -266,23 +262,24 @@ class PdfFormat(Format): return self.renderers.get_for(element).render(element, ctx) - - class CmdRenderer(TreeRenderer): def parms(self): return [] + def container(self): root = etree.Element(self.root_name) root.append(texml_cmd(self.tag_name, *(self.parms() + [""]))) inner = root[0][-1] return root, inner + class EnvRenderer(TreeRenderer): def container(self): root = etree.Element(self.root_name) inner = etree.SubElement(root, 'env', name=self.tag_name) return root, inner + class GroupRenderer(CmdRenderer): def container(self): root = etree.Element(self.root_name) @@ -311,6 +308,7 @@ PdfFormat.renderers.register(core.Header, None, CmdRenderer('section*')) PdfFormat.renderers.register(core.Div, None, CmdRenderer('par')) + class ImgRenderer(CmdRenderer): def parms(self): return ["", ""] @@ -324,8 +322,8 @@ class ImgRenderer(CmdRenderer): root[0][0].text = 'f%d.png' % nr try: size = Image.open(ctx.format.get_file(ctx, 'f%d.png' % nr)).size - except IOError: # not an image - del root[0]; + except IOError: # not an image + del root[0] return root root[0][1].text = '15cm' root[0][2].text = '%fcm' % (15.0 * size[1] / size[0]) @@ -340,21 +338,22 @@ PdfFormat.renderers.register(core.Div, 'list', EnvRenderer('itemize')) PdfFormat.renderers.register(core.Div, 'list.enum', EnvRenderer('enumerate')) - PdfFormat.renderers.register(core.Span, None, TreeRenderer()) PdfFormat.renderers.register(core.Span, 'cite', CmdRenderer('emph')) PdfFormat.renderers.register(core.Span, 'cite.code', CmdRenderer('texttt')) PdfFormat.renderers.register(core.Span, 'emp', CmdRenderer('textbf')) PdfFormat.renderers.register(core.Span, 'emph', CmdRenderer('emph')) + class SpanUri(CmdRenderer): def parms(self): return [""] + def render(self, element, ctx): root = super(SpanUri, self).render(element, ctx) src = element.text if src.startswith('file://'): - src = ctx.files_path + src[7:] + src = ctx.files_path + src[7:] root[0][0].text = src return root PdfFormat.renderers.register(core.Span, 'uri', SpanUri('href')) @@ -363,19 +362,17 @@ PdfFormat.renderers.register(core.Span, 'uri', SpanUri('href')) class SpanLink(CmdRenderer): def parms(self): return [""] + def render(self, element, ctx): root = super(SpanLink, self).render(element, ctx) src = element.attrib.get('href', '') if src.startswith('file://'): - src = ctx.files_path + src[7:] + src = ctx.files_path + src[7:] root[0][0].text = src return root PdfFormat.renderers.register(core.Span, 'link', SpanLink('href')) - - PdfFormat.renderers.register(core.Aside, None, TreeRenderer()) PdfFormat.renderers.register(core.Aside, 'editorial', CmdRenderer('editorialpage')) PdfFormat.renderers.register(core.Aside, 'comment', Silent()) - diff --git a/librarian/meta.py b/librarian/meta.py index 5b50d92..a16b73e 100755 --- a/librarian/meta.py +++ b/librarian/meta.py @@ -53,7 +53,6 @@ class Metadata(etree.ElementBase): return values[0] else: return None - # Specials. diff --git a/librarian/mobi.py b/librarian/mobi.py deleted file mode 100644 index d98b838..0000000 --- a/librarian/mobi.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from copy import deepcopy -import os -import subprocess -from tempfile import NamedTemporaryFile - -from librarian import OutputFile -from librarian.cover import WLCover -from librarian import get_resource - - -def transform(wldoc, verbose=False, - sample=None, cover=None, flags=None): - """ produces a MOBI file - - wldoc: a WLDocument - sample=n: generate sample e-book (with at least n paragraphs) - cover: a cover.Cover factory overriding default - flags: less-advertising, - """ - - document = deepcopy(wldoc) - del wldoc - book_info = document.book_info - - # provide a cover by default - if not cover: - cover = WLCover - cover_file = NamedTemporaryFile(suffix='.png', delete=False) - bound_cover = cover(book_info) - bound_cover.save(cover_file) - - if bound_cover.uses_dc_cover: - if document.book_info.cover_by: - document.edoc.getroot().set('data-cover-by', document.book_info.cover_by) - if document.book_info.cover_source: - document.edoc.getroot().set('data-cover-source', document.book_info.cover_source) - - if not flags: - flags = [] - flags = list(flags) + ['without-fonts'] - epub = document.as_epub(verbose=verbose, sample=sample, html_toc=True, - flags=flags, style=get_resource('mobi/style.css')) - - if verbose: - kwargs = {} - else: - devnull = open("/dev/null", 'w') - kwargs = {"stdout": devnull, "stderr": devnull} - - output_file = NamedTemporaryFile(prefix='librarian', suffix='.mobi', delete=False) - output_file.close() - subprocess.check_call(['ebook-convert', epub.get_filename(), output_file.name, - '--no-inline-toc', '--cover=%s' % cover_file.name], **kwargs) - os.unlink(cover_file.name) - return OutputFile.from_filename(output_file.name) \ No newline at end of file diff --git a/librarian/packagers.py b/librarian/packagers.py deleted file mode 100644 index ddfd7c8..0000000 --- a/librarian/packagers.py +++ /dev/null @@ -1,156 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import os -from copy import deepcopy -from lxml import etree -from librarian import pdf, epub, DirDocProvider, ParseError, cover -from librarian.parser import WLDocument - - -class Packager(object): - cover = None - flags = None - - @classmethod - def prepare_file(cls, main_input, output_dir, verbose=False): - path, fname = os.path.realpath(main_input).rsplit('/', 1) - provider = DirDocProvider(path) - slug, ext = os.path.splitext(fname) - - if output_dir != '': - try: - os.makedirs(output_dir) - except: - pass - outfile = os.path.join(output_dir, slug + '.' + cls.ext) - - doc = WLDocument.from_file(main_input, provider=provider) - output_file = cls.converter.transform(doc, - cover=cls.cover, flags=cls.flags) - doc.save_output_file(output_file, output_path=outfile) - - - @classmethod - def prepare(cls, input_filenames, output_dir='', verbose=False): - try: - for main_input in input_filenames: - if verbose: - print main_input - cls.prepare_file(main_input, output_dir, verbose) - except ParseError, e: - print '%(file)s:%(name)s:%(message)s' % { - 'file': main_input, - 'name': e.__class__.__name__, - 'message': e.message - } - - -class EpubPackager(Packager): - converter = epub - ext = 'epub' - -class PdfPackager(Packager): - converter = pdf - ext = 'pdf' - - -class GandalfEpubPackager(EpubPackager): - cover = cover.GandalfCover - -class GandalfPdfPackager(PdfPackager): - cover = cover.GandalfCover - -class BookotekaEpubPackager(EpubPackager): - cover = cover.BookotekaCover - -class PrestigioEpubPackager(EpubPackager): - cover = cover.PrestigioCover - flags = ('less-advertising',) - -class PrestigioPdfPackager(PdfPackager): - cover = cover.PrestigioCover - flags = ('less-advertising',) - - -class VirtualoPackager(Packager): - @staticmethod - def utf_trunc(text, limit): - """ truncates text to at most `limit' bytes in utf-8 """ - if text is None: - return text - if len(text.encode('utf-8')) > limit: - newlimit = limit - 3 - while len(text.encode('utf-8')) > newlimit: - text = text[:(newlimit - len(text.encode('utf-8'))) / 4] - text += '...' - return text - - @classmethod - def prepare(cls, input_filenames, output_dir='', verbose=False): - xml = etree.fromstring(""" - """) - product = etree.fromstring(""" - - - - - - - Jan - Kowalski - - - 0.0 - PL - """) - - try: - for main_input in input_filenames: - if verbose: - print main_input - path, fname = os.path.realpath(main_input).rsplit('/', 1) - provider = DirDocProvider(path) - slug, ext = os.path.splitext(fname) - - outfile_dir = os.path.join(output_dir, slug) - os.makedirs(os.path.join(output_dir, slug)) - - doc = WLDocument.from_file(main_input, provider=provider) - info = doc.book_info - - product_elem = deepcopy(product) - product_elem[0].text = cls.utf_trunc(slug, 100) - product_elem[1].text = cls.utf_trunc(info.title, 255) - product_elem[2].text = cls.utf_trunc(info.description, 255) - product_elem[3].text = cls.utf_trunc(info.source_name, 3000) - product_elem[4][0][0].text = cls.utf_trunc(u' '.join(info.author.first_names), 100) - product_elem[4][0][1].text = cls.utf_trunc(info.author.last_name, 100) - xml.append(product_elem) - - cover.VirtualoCover(info).save(os.path.join(outfile_dir, slug+'.jpg')) - outfile = os.path.join(outfile_dir, '1.epub') - outfile_sample = os.path.join(outfile_dir, '1.sample.epub') - doc.save_output_file(doc.as_epub(), - output_path=outfile) - doc.save_output_file(doc.as_epub(doc, sample=25), - output_path=outfile_sample) - outfile = os.path.join(outfile_dir, '1.mobi') - outfile_sample = os.path.join(outfile_dir, '1.sample.mobi') - doc.save_output_file(doc.as_mobi(cover=cover.VirtualoCover), - output_path=outfile) - doc.save_output_file( - doc.as_mobi(doc, cover=cover.VirtualoCover, sample=25), - output_path=outfile_sample) - except ParseError, e: - print '%(file)s:%(name)s:%(message)s' % { - 'file': main_input, - 'name': e.__class__.__name__, - 'message': e.message - } - - xml_file = open(os.path.join(output_dir, 'import_products.xml'), 'w') - xml_file.write(etree.tostring(xml, pretty_print=True, encoding=unicode).encode('utf-8')) - xml_file.close() diff --git a/librarian/parser.py b/librarian/parser.py index a0b8a7f..7b48624 100644 --- a/librarian/parser.py +++ b/librarian/parser.py @@ -11,7 +11,7 @@ from . import core, meta class SSTParser(etree.XMLParser): """ XML parser using relevant element classes. """ def __init__(self): - super(SSTParser, self).__init__(remove_blank_text=False) + super(SSTParser, self).__init__() lookup = etree.ElementNamespaceClassLookup() self.set_element_class_lookup(lookup) diff --git a/librarian/pdf.py b/librarian/pdf.py deleted file mode 100644 index 9fb92b1..0000000 --- a/librarian/pdf.py +++ /dev/null @@ -1,321 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -"""PDF creation library. - -Creates one big XML from the book and its children, converts it to LaTeX -with TeXML, then runs it by XeLaTeX. - -""" -from __future__ import with_statement -import os -import os.path -import shutil -from StringIO import StringIO -from tempfile import mkdtemp, NamedTemporaryFile -import re -from copy import deepcopy -from subprocess import call, PIPE - -from Texml.processor import process -from lxml import etree -from lxml.etree import XMLSyntaxError, XSLTApplyError - -from librarian.dcparser import Person -from librarian.parser import WLDocument -from librarian import ParseError, DCNS, get_resource, OutputFile -from librarian import functions -from librarian.cover import WLCover - - -functions.reg_substitute_entities() -functions.reg_strip() -functions.reg_starts_white() -functions.reg_ends_white() -functions.reg_texcommand() - -STYLESHEETS = { - 'wl2tex': 'pdf/wl2tex.xslt', -} - -#CUSTOMIZATIONS = [ -# 'nofootnotes', -# 'nothemes', -# 'defaultleading', -# 'onehalfleading', -# 'doubleleading', -# 'nowlfont', -# ] - -def insert_tags(doc, split_re, tagname, exclude=None): - """ inserts for every occurence of `split_re' in text nodes in the `doc' tree - - >>> t = etree.fromstring('A-B-CX-Y-Z'); - >>> insert_tags(t, re.compile('-'), 'd'); - >>> print etree.tostring(t) - ABCXYZ - """ - - for elem in doc.iter(tag=etree.Element): - if exclude and elem.tag in exclude: - continue - if elem.text: - chunks = split_re.split(elem.text) - while len(chunks) > 1: - ins = etree.Element(tagname) - ins.tail = chunks.pop() - elem.insert(0, ins) - elem.text = chunks.pop(0) - if elem.tail: - chunks = split_re.split(elem.tail) - parent = elem.getparent() - ins_index = parent.index(elem) + 1 - while len(chunks) > 1: - ins = etree.Element(tagname) - ins.tail = chunks.pop() - parent.insert(ins_index, ins) - elem.tail = chunks.pop(0) - - -def substitute_hyphens(doc): - insert_tags(doc, - re.compile("(?<=[^-\s])-(?=[^-\s])"), - "dywiz", - exclude=[DCNS("identifier.url"), DCNS("rights.license")] - ) - - -def fix_hanging(doc): - insert_tags(doc, - re.compile("(?<=\s\w)\s+"), - "nbsp", - exclude=[DCNS("identifier.url"), DCNS("rights.license")] - ) - - -def move_motifs_inside(doc): - """ moves motifs to be into block elements """ - for master in doc.xpath('//powiesc|//opowiadanie|//liryka_l|//liryka_lp|//dramat_wierszowany_l|//dramat_wierszowany_lp|//dramat_wspolczesny'): - for motif in master.xpath('motyw'): - for sib in motif.itersiblings(): - if sib.tag not in ('sekcja_swiatlo', 'sekcja_asterysk', 'separator_linia', 'begin', 'end', 'motyw', 'extra', 'uwaga'): - # motif shouldn't have a tail - it would be untagged text - motif.tail = None - motif.getparent().remove(motif) - sib.insert(0, motif) - break - - -def hack_motifs(doc): - """ dirty hack for the marginpar-creates-orphans LaTeX problem - see http://www.latex-project.org/cgi-bin/ltxbugs2html?pr=latex/2304 - - moves motifs in stanzas from first verse to second - and from next to last to last, then inserts negative vspace before them - """ - for motif in doc.findall('//strofa//motyw'): - # find relevant verse-level tag - verse, stanza = motif, motif.getparent() - while stanza is not None and stanza.tag != 'strofa': - verse, stanza = stanza, stanza.getparent() - breaks_before = sum(1 for i in verse.itersiblings('br', preceding=True)) - breaks_after = sum(1 for i in verse.itersiblings('br')) - if (breaks_before == 0 and breaks_after > 0) or breaks_after == 1: - move_by = 1 - if breaks_after == 2: - move_by += 1 - moved_motif = deepcopy(motif) - motif.tag = 'span' - motif.text = None - moved_motif.tail = None - moved_motif.set('moved', str(move_by)) - - for br in verse.itersiblings('br'): - if move_by > 1: - move_by -= 1 - continue - br.addnext(moved_motif) - break - - -def parse_creator(doc): - """Generates readable versions of creator and translator tags. - - Finds all dc:creator and dc.contributor.translator tags - and adds *_parsed versions with forenames first. - """ - for person in doc.xpath("|".join('//dc:'+(tag) for tag in ( - 'creator', 'contributor.translator')), - namespaces = {'dc': str(DCNS)})[::-1]: - if not person.text: - continue - p = Person.from_text(person.text) - person_parsed = deepcopy(person) - person_parsed.tag = person.tag + '_parsed' - person_parsed.set('sortkey', person.text) - person_parsed.text = p.readable() - person.getparent().insert(0, person_parsed) - - -def get_stylesheet(name): - return get_resource(STYLESHEETS[name]) - - -def package_available(package, args='', verbose=False): - """ check if a verion of a latex package accepting given args is available """ - tempdir = mkdtemp('-wl2pdf-test') - fpath = os.path.join(tempdir, 'test.tex') - f = open(fpath, 'w') - f.write(r""" - \documentclass{wl} - \usepackage[%s]{%s} - \begin{document} - \end{document} - """ % (args, package)) - f.close() - if verbose: - p = call(['xelatex', '-output-directory', tempdir, fpath]) - else: - p = call(['xelatex', '-interaction=batchmode', '-output-directory', tempdir, fpath], stdout=PIPE, stderr=PIPE) - shutil.rmtree(tempdir) - return p == 0 - - -def transform(wldoc, verbose=False, save_tex=None, morefloats=None, - cover=None, flags=None, customizations=None): - """ produces a PDF file with XeLaTeX - - wldoc: a WLDocument - verbose: prints all output from LaTeX - save_tex: path to save the intermediary LaTeX file to - morefloats (old/new/none): force specific morefloats - cover: a cover.Cover factory or True for default - flags: less-advertising, - customizations: user requested customizations regarding various formatting parameters (passed to wl LaTeX class) - """ - - # Parse XSLT - try: - book_info = wldoc.book_info - document = load_including_children(wldoc) - root = document.edoc.getroot() - - if cover: - if cover is True: - cover = WLCover - bound_cover = cover(book_info) - root.set('data-cover-width', str(bound_cover.width)) - root.set('data-cover-height', str(bound_cover.height)) - if bound_cover.uses_dc_cover: - if book_info.cover_by: - root.set('data-cover-by', book_info.cover_by) - if book_info.cover_source: - root.set('data-cover-source', - book_info.cover_source) - if flags: - for flag in flags: - root.set('flag-' + flag, 'yes') - - # check for LaTeX packages - if morefloats: - root.set('morefloats', morefloats.lower()) - elif package_available('morefloats', 'maxfloats=19'): - root.set('morefloats', 'new') - - # add customizations - if customizations is not None: - root.set('customizations', u','.join(customizations)) - - # add editors info - root.set('editors', u', '.join(sorted( - editor.readable() for editor in document.editors()))) - - # hack the tree - move_motifs_inside(document.edoc) - hack_motifs(document.edoc) - parse_creator(document.edoc) - substitute_hyphens(document.edoc) - fix_hanging(document.edoc) - - # wl -> TeXML - style_filename = get_stylesheet("wl2tex") - style = etree.parse(style_filename) - - texml = document.transform(style) - - # TeXML -> LaTeX - temp = mkdtemp('-wl2pdf') - - if cover: - with open(os.path.join(temp, 'cover.png'), 'w') as f: - bound_cover.save(f) - - del document # no longer needed large object :) - - tex_path = os.path.join(temp, 'doc.tex') - fout = open(tex_path, 'w') - process(StringIO(texml), fout, 'utf-8') - fout.close() - del texml - - if save_tex: - shutil.copy(tex_path, save_tex) - - # LaTeX -> PDF - shutil.copy(get_resource('pdf/wl.cls'), temp) - shutil.copy(get_resource('res/wl-logo.png'), temp) - - try: - cwd = os.getcwd() - except OSError: - cwd = None - os.chdir(temp) - - if verbose: - p = call(['xelatex', tex_path]) - else: - p = call(['xelatex', '-interaction=batchmode', tex_path], stdout=PIPE, stderr=PIPE) - if p: - raise ParseError("Error parsing .tex file") - - if cwd is not None: - os.chdir(cwd) - - output_file = NamedTemporaryFile(prefix='librarian', suffix='.pdf', delete=False) - pdf_path = os.path.join(temp, 'doc.pdf') - shutil.move(pdf_path, output_file.name) - shutil.rmtree(temp) - return OutputFile.from_filename(output_file.name) - - except (XMLSyntaxError, XSLTApplyError), e: - raise ParseError(e) - - -def load_including_children(wldoc=None, provider=None, uri=None): - """ Makes one big xml file with children inserted at end. - - Either wldoc or provider and URI must be provided. - """ - - if uri and provider: - f = provider.by_uri(uri) - text = f.read().decode('utf-8') - f.close() - elif wldoc is not None: - text = etree.tostring(wldoc.edoc, encoding=unicode) - provider = wldoc.provider - else: - raise ValueError('Neither a WLDocument, nor provider and URI were provided.') - - text = re.sub(ur"([\u0400-\u04ff]+)", ur"\1", text) - - document = WLDocument.from_string(text, - parse_dublincore=True, provider=provider) - document.swap_endlines() - - for child_uri in document.book_info.parts: - child = load_including_children(provider=provider, uri=child_uri) - document.edoc.getroot().append(child.edoc.getroot()) - return document diff --git a/librarian/renderers.py b/librarian/renderers.py index 59ed8a4..fd4ec16 100755 --- a/librarian/renderers.py +++ b/librarian/renderers.py @@ -71,7 +71,6 @@ class TreeRenderer(Renderer): return root - class Register(object): """ Class-renderer register. diff --git a/librarian/text.py b/librarian/text.py deleted file mode 100644 index d99e7cf..0000000 --- a/librarian/text.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import copy -from librarian import functions, OutputFile -from lxml import etree -import os - - -functions.reg_substitute_entities() -functions.reg_wrap_words() -functions.reg_strip() -functions.reg_person_name() - -TEMPLATE = u"""\ -%(text)s - - ------ -Ta lektura, podobnie jak tysiące innych, dostępna jest na stronie wolnelektury.pl. -Wersja lektury w opracowaniu merytorycznym i krytycznym (przypisy i motywy) dostępna jest na stronie %(url)s. - -Utwór opracowany został w ramach projektu Wolne Lektury przez fundację Nowoczesna Polska. - -%(license_description)s.%(source)s - -%(description)s%(contributors)s -""" - -def transform(wldoc, flags=None, **options): - """ - Transforms input_file in XML to output_file in TXT. - possible flags: raw-text, - """ - # Parse XSLT - style_filename = os.path.join(os.path.dirname(__file__), 'xslt/book2txt.xslt') - style = etree.parse(style_filename) - - document = copy.deepcopy(wldoc) - del wldoc - document.swap_endlines() - - if flags: - for flag in flags: - document.edoc.getroot().set(flag, 'yes') - - result = document.transform(style, **options) - - if not flags or 'raw-text' not in flags: - if document.book_info: - parsed_dc = document.book_info - description = parsed_dc.description - url = document.book_info.url - - license_description = parsed_dc.license_description - license = parsed_dc.license - if license: - license_description = u"Ten utwór jest udostepniony na licencji %s: \n%s" % (license_description, license) - else: - license_description = u"Ten utwór nie jest chroniony 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/)" - - source = parsed_dc.source_name - if source: - source = "\n\nTekst opracowany na podstawie: " + source - else: - source = '' - - contributors = ', '.join(person.readable() for person in - sorted(set(p for p in (parsed_dc.technical_editors + parsed_dc.editors) if p))) - if contributors: - contributors = "\n\nOpracowanie redakcyjne i przypisy: %s" % contributors - else: - description = 'Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl).' - url = '*' * 10 - license = "" - license_description = "" - source = "" - contributors = "" - return OutputFile.from_string((TEMPLATE % { - 'description': description, - 'url': url, - 'license_description': license_description, - 'text': unicode(result), - 'source': source, - 'contributors': contributors, - }).encode('utf-8')) - else: - return OutputFile.from_string(unicode(result).encode('utf-8')) - diff --git a/librarian/utils.py b/librarian/utils.py index a2e3522..04b6d69 100755 --- a/librarian/utils.py +++ b/librarian/utils.py @@ -26,7 +26,7 @@ class Context(object): elif self._upctx is not None: return getattr(self._upctx, name) else: - raise AttributeError, "'%s' object has no attribute '%s'" % (type(self), name) + raise AttributeError("'%s' object has no attribute '%s'" % (type(self), name)) def __setattr__(self, name, value): try: @@ -44,7 +44,7 @@ class Context(object): class XMLNamespace(object): - '''A handy structure to repsent names in an XML namespace.''' + """A handy structure to repsent names in an XML namespace.""" def __init__(self, uri): self.uri = uri diff --git a/scripts/book2partner b/scripts/book2partner deleted file mode 100755 index 4b84c2f..0000000 --- a/scripts/book2partner +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import optparse - -from librarian import packagers - - -if __name__ == '__main__': - # Parse commandline arguments - usage = """Usage: %prog [options] SOURCE [SOURCE...] - Prepare SOURCE files for a partner.""" - - parser = optparse.OptionParser(usage=usage) - - parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, - help='print status messages to stdout') - parser.add_option('-O', '--output-dir', dest='output_dir', metavar='DIR', default='', - help='specifies the directory for output') - parser.add_option('--bookoteka', action='store_true', dest='bookoteka', default=False, - help='prepare files for Bookoteka') - parser.add_option('--gandalf', action='store_true', dest='gandalf', default=False, - help='prepare EPUB files for Gandalf') - parser.add_option('--gandalf-pdf', action='store_true', dest='gandalf_pdf', default=False, - help='prepare PDF files for Gandalf') - parser.add_option('--virtualo', action='store_true', dest='virtualo', default=False, - help='prepare files for Virtualo API') - parser.add_option('--prestigio', action='store_true', dest='prestigio', default=False, - help='prepare files for Prestigio') - parser.add_option('--prestigio-pdf', action='store_true', dest='prestigio_pdf', default=False, - help='prepare PDF files for Prestigio') - - options, input_filenames = parser.parse_args() - - if len(input_filenames) < 1: - parser.print_help() - exit(1) - - if options.bookoteka: - packagers.BookotekaEpubPackager.prepare(input_filenames, options.output_dir, options.verbose) - if options.gandalf: - packagers.GandalfEpubPackager.prepare(input_filenames, options.output_dir, options.verbose) - if options.gandalf_pdf: - packagers.GandalfPdfPackager.prepare(input_filenames, options.output_dir, options.verbose) - if options.virtualo: - packagers.VirtualoPackager.prepare(input_filenames, options.output_dir, options.verbose) - if options.prestigio: - packagers.PrestigioEpubPackager.prepare(input_filenames, options.output_dir, options.verbose) - if options.prestigio_pdf: - packagers.PrestigioPdfPackager.prepare(input_filenames, options.output_dir, options.verbose) diff --git a/setup.py b/setup.py index a0e4e53..8c4240c 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,6 @@ setup( 'scripts/book2mobi', 'scripts/book2pdf', 'scripts/book2fb2', - 'scripts/book2partner', 'scripts/book2cover', 'scripts/bookfragments', 'scripts/genslugs'], diff --git a/tests/test_dcparser.py b/tests/test_dcparser.py deleted file mode 100644 index ee29bc9..0000000 --- a/tests/test_dcparser.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from librarian import dcparser -from lxml import etree -from nose.tools import * -from os.path import splitext -from tests.utils import get_all_fixtures -import codecs - - -def check_dcparser(xml_file, result_file): - xml = file(xml_file).read() - result = codecs.open(result_file, encoding='utf-8').read() - info = dcparser.BookInfo.from_string(xml).to_dict() - should_be = eval(result) - for key in should_be: - assert_equals(info[key], should_be[key]) - - -def test_dcparser(): - for fixture in get_all_fixtures('dcparser', '*.xml'): - base_name = splitext(fixture)[0] - yield check_dcparser, fixture, base_name + '.out' - - -def check_serialize(xml_file): - xml = file(xml_file).read() - info = dcparser.BookInfo.from_string(xml) - - # serialize - serialized = etree.tostring(info.to_etree(), encoding=unicode).encode('utf-8') - # then parse again - info_bis = dcparser.BookInfo.from_string(serialized) - - # check if they are the same - for key in vars(info): - assert_equals(getattr(info, key), getattr(info_bis, key)) - for key in vars(info_bis): - assert_equals(getattr(info, key), getattr(info_bis, key)) - - -def test_serialize(): - for fixture in get_all_fixtures('dcparser', '*.xml'): - yield check_serialize, fixture - diff --git a/tests/test_epub.py b/tests/test_epub.py deleted file mode 100644 index faa76e7..0000000 --- a/tests/test_epub.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from zipfile import ZipFile -from lxml import html -from nose.tools import * -from librarian import DirDocProvider -from librarian.parser import WLDocument -from tests.utils import get_fixture - - -def test_transform(): - epub = WLDocument.from_file( - get_fixture('text', 'asnyk_zbior.xml'), - provider=DirDocProvider(get_fixture('text', '')) - ).as_epub(flags=['without_fonts']).get_file() - zipf = ZipFile(epub) - - # Check contributor list. - last = zipf.open('OPS/last.html') - tree = html.parse(last) - editors_attribution = False - for par in tree.findall("//p"): - if par.text.startswith(u'Opracowanie redakcyjne i przypisy:'): - editors_attribution = True - assert_equal(par.text.rstrip(), - u'Opracowanie redakcyjne i przypisy: ' - u'Adam Fikcyjny, Aleksandra Sekuła, Olga Sutkowska.') - assert_true(editors_attribution) diff --git a/tests/test_html.py b/tests/test_html.py deleted file mode 100644 index 51d6acd..0000000 --- a/tests/test_html.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from librarian import NoDublinCore -from librarian.parser import WLDocument -from nose.tools import * -from utils import get_fixture - - -def test_transform(): - expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_expected.html') - - html = WLDocument.from_file( - get_fixture('text', 'miedzy-nami-nic-nie-bylo.xml') - ).as_html().get_string() - - assert_equal(html, file(expected_output_file_path).read()) - - -@raises(NoDublinCore) -def test_no_dublincore(): - WLDocument.from_file( - get_fixture('text', 'asnyk_miedzy_nami_nodc.xml') - ).as_html() - - -def test_passing_parse_dublincore_to_transform(): - """Passing parse_dublincore=False to transform omits DublinCore parsing.""" - WLDocument.from_file( - get_fixture('text', 'asnyk_miedzy_nami_nodc.xml'), - parse_dublincore=False, - ).as_html() - -def test_empty(): - assert not WLDocument.from_string( - '', - parse_dublincore=False, - ).as_html() diff --git a/tests/test_pdf.py b/tests/test_pdf.py deleted file mode 100644 index 75b73bc..0000000 --- a/tests/test_pdf.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import re -from tempfile import NamedTemporaryFile -from nose.tools import * -from librarian import DirDocProvider -from librarian.parser import WLDocument -from utils import get_fixture - - -def test_transform(): - temp = NamedTemporaryFile(delete=False) - temp.close() - WLDocument.from_file( - get_fixture('text', 'asnyk_zbior.xml'), - provider=DirDocProvider(get_fixture('text', '')) - ).as_pdf(save_tex=temp.name) - tex = open(temp.name).read().decode('utf-8') - print tex - - # Check contributor list. - editors = re.search(ur'\\def\\editors\{' - ur'Opracowanie redakcyjne i przypisy: ([^}]*?)\.\s*\}', tex) - assert_equal(editors.group(1), - u"Adam Fikcyjny, Aleksandra Sekuła, Olga Sutkowska") diff --git a/tests/test_picture.py b/tests/test_picture.py deleted file mode 100644 index f64f624..0000000 --- a/tests/test_picture.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from librarian import picture, dcparser -from lxml import etree -from nose.tools import * -from os.path import splitext -from tests.utils import get_all_fixtures, get_fixture -import codecs -from os import path - -def test_wlpictureuri(): - uri = picture.WLPictureURI('http://wolnelektury.pl/katalog/obraz/angelus-novus') - -def check_load(xml_file): - pi = dcparser.parse(xml_file, picture.PictureInfo) - assert pi is not None - assert isinstance(pi, picture.PictureInfo) - - -def test_load(): - for fixture in get_all_fixtures('picture', '*.xml'): - yield check_load, fixture - - -def test_wlpicture(): - wlp = picture.WLPicture.from_file(open(get_fixture('picture', 'angelus-novus.xml'))) - pi = wlp.picture_info - - # from nose.tools import set_trace; set_trace() - assert pi.type[0] == u"Image" - assert pi.mime_type == u'image/jpeg' == wlp.mime_type - assert wlp.slug == 'angelus-novus' - - assert path.exists(wlp.image_path) - - f = wlp.image_file('r') - f.close() - -def test_picture_parts(): - wlp = picture.WLPicture.from_file(open(get_fixture('picture', 'angelus-novus.xml'))) - parts = list(wlp.partiter()) - assert len(parts) == 5, "there should be %d parts of the picture" % 5 - motifs = set() - names = set() - - print parts - for p in parts: - for m in p['themes']: - motifs.add(m) - for p in parts: - if p['object']: - names.add(p['object']) - - assert motifs == set([u'anioł historii', u'spojrzenie']), "missing motifs, got: %s" % motifs - assert names == set([u'obraz cały', u'skrzydło']), 'missing objects, got: %s' % names - - diff --git a/tests/test_text.py b/tests/test_text.py deleted file mode 100644 index 70dfb60..0000000 --- a/tests/test_text.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Librarian, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from librarian import NoDublinCore -from librarian.parser import WLDocument -from nose.tools import * -from utils import get_fixture - - -def test_transform(): - expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_expected.txt') - - text = WLDocument.from_file( - get_fixture('text', 'miedzy-nami-nic-nie-bylo.xml') - ).as_text().get_string() - - assert_equal(text, file(expected_output_file_path).read()) - - -@raises(NoDublinCore) -def test_no_dublincore(): - WLDocument.from_file( - get_fixture('text', 'asnyk_miedzy_nami_nodc.xml') - ).as_text() - - -def test_passing_parse_dublincore_to_transform(): - """Passing parse_dublincore=False to the constructor omits DublinCore parsing.""" - WLDocument.from_file( - get_fixture('text', 'asnyk_miedzy_nami_nodc.xml'), - parse_dublincore=False, - ).as_text() diff --git a/tests/utils.py b/tests/utils.py index 3b1f4f5..fc87532 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -6,7 +6,7 @@ from __future__ import with_statement from os.path import realpath, join, dirname import glob -import os + def get_fixture_dir(dir_name): """Returns path to fixtures directory dir_name.""" -- 2.20.1 From 5671d33ab05838c3532ed84cfff7a954b6af9f7f Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Wed, 21 Dec 2016 13:44:57 +0100 Subject: [PATCH 12/16] document validation (stub) --- librarian/document.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/librarian/document.py b/librarian/document.py index a3251a6..a4a8593 100755 --- a/librarian/document.py +++ b/librarian/document.py @@ -11,14 +11,16 @@ from .parser import SSTParser class Document(object): - # Do I use meta_context? def __init__(self, edoc, meta_context=None): self.edoc = edoc - root_elem = edoc.getroot() + # Do I use meta_context? if meta_context is not None: root_elem.meta_context = meta_context + self.validate() + def validate(self): + root_elem = self.edoc.getroot() if not isinstance(root_elem, Section): if root_elem.tag != SSTNS('section'): if root_elem.tag == 'section': @@ -30,12 +32,20 @@ class Document(object): tree = etree.parse(StringIO(etree.tostring(root_elem)), parser) tree.xinclude() self.edoc = tree + root_elem = self.edoc.getroot() else: raise ValueError("Invalid root element. Found '%s', should be '%s'" % ( root_elem.tag, SSTNS('section'))) else: raise ValueError("Invalid class of root element. Use librarian.parser.SSTParser.") - # print etree.tostring(self.edoc.getroot()) + if len(root_elem) < 1 or root_elem[0].tag != SSTNS('metadata'): + raise ValueError("The first tag in section should be metadata") + if len(root_elem) < 2 or root_elem[1].tag != SSTNS('header'): + raise ValueError("The first tag after metadata should be header") + header = root_elem[1] + if not getattr(header, 'text', None) or not header.text.strip(): + raise ValueError( + "The first header should contain the title in plain text (no links, emphasis etc.) and cannot be empty") @classmethod def from_string(cls, xml, *args, **kwargs): -- 2.20.1 From 5135e076096ce04b1dbbb2f6210512d1a44fa237 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Fri, 23 Dec 2016 12:52:17 +0100 Subject: [PATCH 13/16] render video --- librarian/formats/epub/__init__.py | 15 +++++++++++++++ librarian/formats/html/__init__.py | 18 ++++++++++++++++++ librarian/formats/pdf/__init__.py | 12 ++++++++++++ 3 files changed, 45 insertions(+) diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py index 80f9d5c..38443ee 100644 --- a/librarian/formats/epub/__init__.py +++ b/librarian/formats/epub/__init__.py @@ -352,6 +352,21 @@ class DivImageR(EpubRenderer): EpubFormat.renderers.register(core.Div, 'img', DivImageR('img')) +class DivVideoR(Silent): + def render(self, element, ctx): + src = 'https://www.youtube.com/watch?v=%s' % element.attrib.get('videoid', '') + return super(DivVideoR, self).render(element, Context(ctx, src=src)) + + def container(self, ctx): + root, inner = super(DivVideoR, self).container(ctx) + src = getattr(ctx, 'src', '') + link = etree.Element('a', {'href': src}) + link.text = src + inner.append(link) + return root, inner +EpubFormat.renderers.register(core.Div, 'video', DivVideoR('p')) + + class HeaderR(NaturalText): def subcontext(self, element, ctx): return Context(ctx, inline=True) diff --git a/librarian/formats/html/__init__.py b/librarian/formats/html/__init__.py index ae6470a..01dcbde 100644 --- a/librarian/formats/html/__init__.py +++ b/librarian/formats/html/__init__.py @@ -187,6 +187,24 @@ class DivImage(NaturalText): HtmlFormat.renderers.register(core.Div, 'img', DivImage('img')) + +class DivVideo(NaturalText): + def render(self, element, ctx): + output = super(DivVideo, self).render(element, ctx) + video_id = element.attrib.get('videoid', '') + attribs = { + 'width': '854', + 'height': '480', + 'src': '//www.youtube.com/embed/%s?controls=2&rel=0&showinfo=0&theme=light' % video_id, + 'frameborder': '0', + 'allowfullscreen': '', + } + for attrib, value in attribs.iteritems(): + output[0].attrib[attrib] = value + return output + +HtmlFormat.renderers.register(core.Div, 'video', DivVideo('iframe')) + HtmlFormat.renderers.register(core.Div, 'item', NaturalText('li')) HtmlFormat.renderers.register(core.Div, 'list', NaturalText('ul')) HtmlFormat.renderers.register(core.Div, 'list.enum', NaturalText('ol')) diff --git a/librarian/formats/pdf/__init__.py b/librarian/formats/pdf/__init__.py index e8e936b..0ff985f 100644 --- a/librarian/formats/pdf/__init__.py +++ b/librarian/formats/pdf/__init__.py @@ -332,6 +332,18 @@ class ImgRenderer(CmdRenderer): PdfFormat.renderers.register(core.Div, 'img', ImgRenderer('insertimage')) +class VideoRenderer(CmdRenderer): + def render(self, element, ctx): + root = super(VideoRenderer, self).render(element, ctx) + url = 'https://www.youtube.com/watch?v=%s' % element.attrib.get('videoid') + link = texml_cmd('href', url, url) + root[0][0].text = None + root[0][0].append(link) + return root + +PdfFormat.renderers.register(core.Div, 'video', VideoRenderer('par')) + + PdfFormat.renderers.register(core.Div, 'defined', CmdRenderer('textbf')) PdfFormat.renderers.register(core.Div, 'item', CmdRenderer('item')) PdfFormat.renderers.register(core.Div, 'list', EnvRenderer('itemize')) -- 2.20.1 From 1428a9d8f91b754fe7f9fb96cbdd0d6a2536ffc1 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 27 Dec 2016 12:12:33 +0100 Subject: [PATCH 14/16] error reporting --- librarian/formats/cover/__init__.py | 5 ++++- librarian/formats/cover/evens/__init__.py | 2 ++ librarian/formats/epub/__init__.py | 4 +++- librarian/formats/pdf/__init__.py | 5 +++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/librarian/formats/cover/__init__.py b/librarian/formats/cover/__init__.py index d410058..4788e39 100644 --- a/librarian/formats/cover/__init__.py +++ b/librarian/formats/cover/__init__.py @@ -6,7 +6,7 @@ import re from PIL import Image, ImageFont, ImageDraw, ImageFilter from StringIO import StringIO -from librarian import DCNS +from librarian import DCNS, BuildError from librarian.output import OutputFile from librarian.utils import get_resource from librarian.formats import Format @@ -165,6 +165,9 @@ class Cover(Format): img = Image.new('RGB', (metr.width, metr.height), self.background_color) if self.background_img: + IMG_EXT = ('png', 'jpg', 'jpeg') + if '.' not in self.background_img or self.background_img.rsplit('.')[1].lower() not in IMG_EXT: + raise BuildError('Wrong cover format, should be PNG or JPG') background = Image.open(self.background_img) resized = background.resize((1024, background.height*1024/background.width), Image.ANTIALIAS) resized = resized.convert('RGBA') diff --git a/librarian/formats/cover/evens/__init__.py b/librarian/formats/cover/evens/__init__.py index e470001..a4b721c 100644 --- a/librarian/formats/cover/evens/__init__.py +++ b/librarian/formats/cover/evens/__init__.py @@ -20,6 +20,8 @@ class EvensCover(Cover): def set_images(self, ctx): cover_url = self.doc.meta.get(DCNS('relation.coverimage.url'))[0] + if not cover_url: + raise BuildError('No cover specified') if cover_url.startswith('file://'): cover_url = ctx.files_path + urllib.quote(cover_url[7:]) try: diff --git a/librarian/formats/epub/__init__.py b/librarian/formats/epub/__init__.py index 38443ee..4787a74 100644 --- a/librarian/formats/epub/__init__.py +++ b/librarian/formats/epub/__init__.py @@ -13,7 +13,7 @@ import zipfile from urllib2 import urlopen from lxml import etree -from librarian import OPFNS, NCXNS, XHTMLNS, DCNS +from librarian import OPFNS, NCXNS, XHTMLNS, DCNS, BuildError from librarian import core from librarian.formats import Format from librarian.formats.cover.evens import EvensCover @@ -340,6 +340,8 @@ class DivImageR(EpubRenderer): def render(self, element, ctx): src = element.attrib.get('src', '') ctx.images.append(src) + if '/' not in src: + raise BuildError('Bad image URL') src = src.rsplit('/', 1)[1] return super(DivImageR, self).render(element, Context(ctx, src=src)) diff --git a/librarian/formats/pdf/__init__.py b/librarian/formats/pdf/__init__.py index 0ff985f..fd1fd5e 100644 --- a/librarian/formats/pdf/__init__.py +++ b/librarian/formats/pdf/__init__.py @@ -11,7 +11,7 @@ from lxml import etree from urllib import urlretrieve from StringIO import StringIO from Texml.processor import process -from librarian import DCNS, XMLNamespace +from librarian import DCNS, XMLNamespace, BuildError from librarian.formats import Format from librarian.output import OutputFile from librarian.renderers import Register, TreeRenderer @@ -52,7 +52,8 @@ class PdfFormat(Format): def add_file(self, ctx, filename, url=None, path=None, image=False): from subprocess import call - assert url or path + if not url or path: + raise BuildError('No URL or path for image') save_as = os.path.join(ctx.workdir, filename) if path is not None: ext = path.rsplit('.', 1)[-1] -- 2.20.1 From e93c90799341b6627eca7fe03c7fefb95b4db9eb Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 27 Dec 2016 15:30:01 +0100 Subject: [PATCH 15/16] more error reporting --- librarian/formats/cover/evens/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/librarian/formats/cover/evens/__init__.py b/librarian/formats/cover/evens/__init__.py index a4b721c..e07f78b 100644 --- a/librarian/formats/cover/evens/__init__.py +++ b/librarian/formats/cover/evens/__init__.py @@ -19,9 +19,12 @@ class EvensCover(Cover): logo_bottom = 100 def set_images(self, ctx): - cover_url = self.doc.meta.get(DCNS('relation.coverimage.url'))[0] + try: + cover_url = self.doc.meta.get(DCNS('relation.coverimage.url'))[0] + except IndexError: + raise BuildError('No cover specified (metadata field relation.coverimage.url missing)') if not cover_url: - raise BuildError('No cover specified') + raise BuildError('No cover specified (metadata field relation.coverimage.url empty)') if cover_url.startswith('file://'): cover_url = ctx.files_path + urllib.quote(cover_url[7:]) try: -- 2.20.1 From 30e68368555ff801a645746dfe9edc7aa39a70ff Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 27 Dec 2016 15:40:33 +0100 Subject: [PATCH 16/16] stupid bug --- librarian/formats/pdf/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librarian/formats/pdf/__init__.py b/librarian/formats/pdf/__init__.py index fd1fd5e..7d170b9 100644 --- a/librarian/formats/pdf/__init__.py +++ b/librarian/formats/pdf/__init__.py @@ -52,7 +52,7 @@ class PdfFormat(Format): def add_file(self, ctx, filename, url=None, path=None, image=False): from subprocess import call - if not url or path: + if not url and not path: raise BuildError('No URL or path for image') save_as = os.path.join(ctx.workdir, filename) if path is not None: -- 2.20.1