import os
import re
import shutil
+import urllib
-class ParseError(Exception):
+
+class UnicodeException(Exception):
def __str__(self):
""" Dirty workaround for Python Unicode handling problems. """
- return self.message
+ return unicode(self).encode('utf-8')
def __unicode__(self):
""" Dirty workaround for Python Unicode handling problems. """
- return self.message
+ args = self.args[0] if len(self.args) == 1 else self.args
+ try:
+ message = unicode(args)
+ except UnicodeDecodeError:
+ message = unicode(args, encoding='utf-8', errors='ignore')
+ return message
+
+class ParseError(UnicodeException):
+ pass
-class ValidationError(Exception):
+class ValidationError(UnicodeException):
pass
class NoDublinCore(ValidationError):
"""There's no DublinCore section, and it's required."""
pass
-class NoProvider(Exception):
+class NoProvider(UnicodeException):
"""There's no DocProvider specified, and it's needed."""
pass
class WLURI(object):
- """Represents a WL URI. Extracts slug and language from it."""
- DEFAULT_LANGUAGE = u'pol'
-
+ """Represents a WL URI. Extracts slug from it."""
slug = None
- language = None
example = 'http://wolnelektury.pl/katalog/lektura/template/'
- _re_wl_uri = re.compile('http://wolnelektury.pl/katalog/lektura/'
- '(?P<slug>[-a-z0-9]+)(/(?P<lang>[a-z]{3}))?/?$')
-
- def __init__(self, uri=None):
- if uri is not None:
- uri = unicode(uri)
- self.uri = uri
- match = self._re_wl_uri.match(uri)
- if not match:
- raise ValueError('Supplied URI (%s) does not match '
- 'the WL document URI template.' % uri)
- self.slug = match.group('slug')
- self.language = match.group('lang') or self.DEFAULT_LANGUAGE
+ _re_wl_uri = re.compile(r'http://(www\.)?wolnelektury.pl/katalog/lektura/'
+ '(?P<slug>[-a-z0-9]+)/?$')
+
+ def __init__(self, uri):
+ uri = unicode(uri)
+ self.uri = uri
+ self.slug = uri.rstrip('/').rsplit('/', 1)[-1]
+
+ @classmethod
+ def strict(cls, uri):
+ match = cls._re_wl_uri.match(uri)
+ if not match:
+ raise ValidationError(u'Invalid URI (%s). Should match: %s' % (
+ uri, cls._re_wl_uri.pattern))
+ return cls(uri)
@classmethod
- def from_slug_and_lang(cls, slug, lang):
- """Contructs an URI from slug and language code.
+ def from_slug(cls, slug):
+ """Contructs an URI from slug.
- >>> WLURI.from_slug_and_lang('a-slug', WLURI.DEFAULT_LANGUAGE).uri
+ >>> WLURI.from_slug('a-slug').uri
u'http://wolnelektury.pl/katalog/lektura/a-slug/'
- >>> WLURI.from_slug_and_lang('a-slug', 'deu').uri
- u'http://wolnelektury.pl/katalog/lektura/a-slug/deu/'
"""
- if lang is None:
- lang = cls.DEFAULT_LANGUAGE
uri = 'http://wolnelektury.pl/katalog/lektura/%s/' % slug
- if lang is not None and lang != cls.DEFAULT_LANGUAGE:
- uri += lang + '/'
return cls(uri)
def __unicode__(self):
return self.uri
def __eq__(self, other):
- return self.slug, self.language == other.slug, other.language
-
- def filename_stem(self):
- stem = self.slug
- if self.language != self.DEFAULT_LANGUAGE:
- stem += '_' + self.language
- return stem
-
- def validate_language(self, language):
- if language != self.language:
- raise ValidationError("Incorrect language definition in URI")
+ return self.slug == other.slug
class DocProvider(object):
Used for generating joined files, like EPUBs.
"""
- def by_slug_and_lang(self, slug, lang=None):
- """Should return a file-like object with a WL document XML."""
- raise NotImplementedError
-
def by_slug(self, slug):
"""Should return a file-like object with a WL document XML."""
- return self.by_slug_and_lang(slug)
+ 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_and_lang(wluri.slug, wluri.language)
+ return self.by_slug(wluri.slug)
class DirDocProvider(DocProvider):
self.dir = dir_
self.files = {}
- def by_slug_and_lang(self, slug, lang=None):
- fname = WLURI.from_slug_and_lang(slug, lang).filename_stem() + '.xml'
+ def by_slug(self, slug):
+ fname = slug + '.xml'
return open(os.path.join(self.dir, fname))
DCNS('subject.type'): [u'Unknown'],
DCNS('subject.genre'): [u'Unknown'],
DCNS('date'): ['1970-01-01'],
- DCNS('language'): [WLURI.DEFAULT_LANGUAGE],
+ DCNS('language'): [u'pol'],
# DCNS('date'): [creation_date],
DCNS('publisher'): [u"Fundacja Nowoczesna Polska"],
DCNS('description'):
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)'
+urllib._urlopener = URLOpener()
--- /dev/null
+#!/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.
+#
+from collections import namedtuple
+import os.path
+import optparse
+
+from librarian import DirDocProvider, ParseError
+from librarian.parser import WLDocument
+from librarian.cover import WLCover
+
+
+class Option(object):
+ """Option for optparse. Use it like `optparse.OptionParser.add_option`."""
+ def __init__(self, *names, **options):
+ self.names = names
+ self.options = options
+
+ def add(self, parser):
+ parser.add_option(*self.names, **self.options)
+
+ def name(self):
+ return self.options['dest']
+
+ def value(self, options):
+ return getattr(options, self.name())
+
+
+class Book2Anything(object):
+ """A class for creating book2... scripts.
+
+ 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.
+
+
+ @classmethod
+ def run(cls):
+ # Parse commandline arguments
+ usage = """Usage: %%prog [options] SOURCE [SOURCE...]
+ Convert SOURCE files to %s 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:
+ option.add(parser)
+
+ options, input_filenames = parser.parse_args()
+
+ if len(input_filenames) < 1:
+ 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
+
+
+ # Do some real work
+ try:
+ for main_input in input_filenames:
+ 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
+
+ # Where to write output?
+ if not (options.output_file or options.output_dir):
+ output_file = os.path.splitext(main_input)[0] + '.' + cls.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)
+
+ except ParseError, e:
+ print '%(file)s:%(name)s:%(message)s' % {
+ 'file': main_input,
+ 'name': e.__class__.__name__,
+ 'message': e
+ }
# This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
-import Image, ImageFont, ImageDraw, ImageFilter
-from librarian import get_resource
+import re
+from PIL 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):
"""Skips some vertical space."""
self.height += height
- def text(self, text, color='#000', font=None, line_height=20,
+ 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)
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
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',
'PNG': 'image/png',
}
- def __init__(self, book_info):
+ 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.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)
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)
-
- top += text_img.size[1] + self.title_top
+ img.paste(text_img, (metr.author_margin_left, top), text_img)
+
+ 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
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()
+ 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 = 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
+
+ logo_top = 15
+ logo_width = 140
+
bar_width = 35
background_color = '#444'
author_color = '#444'
+ default_background = get_resource('res/cover.png')
+ format = 'JPEG'
- epochs = {
- u'Starożytność': 0,
- u'Średniowiecze': 30,
- u'Renesans': 60,
- u'Barok': 90,
- u'Oświecenie': 120,
- u'Romantyzm': 150,
- u'Pozytywizm': 180,
- u'Modernizm': 210,
- u'Dwudziestolecie międzywojenne': 240,
- u'Współczesność': 270,
+ 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):
- super(WLCover, self).__init__(book_info)
+ 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:
- from urllib2 import urlopen
- from StringIO import StringIO
-
- bg_src = urlopen(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):
- from colorsys import hsv_to_rgb
-
- 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.epochs:
- epoch_color = tuple(int(255 * c) for c in hsv_to_rgb(
- float(self.epochs[self.epoch]) / 360, .7, .7))
+ 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],
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 = (
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, (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.text(self.pretty_author(),
- font=self.author_font,
- line_height=self.author_lineskip,
+ 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(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,
)
+
+ 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 = 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,
+ 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)
- 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):
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
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
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'
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'
else:
return text.decode('utf-8')
+def as_wluri_strict(text):
+ return WLURI.strict(text)
+
class Field(object):
- def __init__(self, uri, attr_name, type=as_unicode, multiple=False, salias=None, **kwargs):
+ 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 = type
+ 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):
+ 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 self.validator is None:
+ if validator is None:
return val
- return [ self.validator(v) if v is not None else v for v in 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 self.validator is None or val[0] is None:
+ if validator is None or val[0] is None:
return val[0]
- return self.validator(val[0])
+ return validator(val[0])
except ValueError, e:
raise ValidationError("Field '%s' - invald value: %s" % (self.uri, e.message))
- def validate(self, fdict):
+ def validate(self, fdict, fallbacks=None, strict=False):
+ if fallbacks is None:
+ fallbacks = {}
if not fdict.has_key(self.uri):
if not self.required:
- f = self.default
+ # 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)
+ return self.validate_value(f, strict=strict)
def __eq__(self, other):
if isinstance(other, Field) and other.name == self.name:
as_person, salias='editor', multiple=True, default=[]),
Field( DCNS('contributor.technical_editor'), 'technical_editors',
as_person, salias='technical_editor', multiple=True, default=[]),
+ Field( DCNS('contributor.funding'), 'funders',
+ salias='funder', multiple=True, default=[]),
+ Field( DCNS('contributor.thanks'), 'thanks', required=False),
Field( DCNS('date'), 'created_at', as_date),
Field( DCNS('date.pd'), 'released_to_public_domain_at', as_date, required=False),
Field( DCNS('source'), 'source_name', required=False),
Field( DCNS('source.URL'), 'source_url', required=False),
- Field( DCNS('identifier.url'), 'url', WLURI),
+ 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):
+ def from_string(cls, xml, *args, **kwargs):
from StringIO import StringIO
- return cls.from_file(StringIO(xml))
+ return cls.from_file(StringIO(xml), *args, **kwargs)
@classmethod
- def from_file(cls, xmlfile):
+ def from_file(cls, xmlfile, *args, **kwargs):
desc_tag = None
try:
iter = etree.iterparse(xmlfile, ['start', 'end'])
# 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)
+ 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):
+ 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'))
fv.append(e.text)
field_dict[e.tag] = fv
- return cls(desc.attrib, field_dict)
+ return cls(desc.attrib, field_dict, *args, **kwargs)
- def __init__(self, rdf_attrs, dc_fields):
+ 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.fmap = {}
for field in self.FIELDS:
- value = field.validate( dc_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
- self.validate()
-
- def validate(self):
- self.url.validate_language(self.language)
-
def __getattribute__(self, name):
try:
field = object.__getattribute__(self, 'fmap')[name]
if not field.multiple:
raise "OUCH!! for field %s" % name
- return value[0]
+ return value[0] if value else None
except (KeyError, AttributeError):
return object.__getattribute__(self, name)
Field( DCNS('audience'), 'audiences', salias='audience', multiple=True,
required=False),
- Field( DCNS('subject.period'), 'epochs', salias='epoch', multiple=True),
- Field( DCNS('subject.type'), 'kinds', salias='kind', multiple=True),
- Field( DCNS('subject.genre'), 'genres', salias='genre', multiple=True),
+ 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, multiple=True, required=False),
-
- Field( DCNS('relation.cover_image.url'), 'cover_url', required=False),
- Field( DCNS('relation.cover_image.attribution'), 'cover_by', required=False),
- Field( DCNS('relation.cover_image.source'), 'cover_source', required=False),
+ 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),
)
import os
import os.path
+import re
import subprocess
from StringIO import StringIO
from copy import deepcopy
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("<strofa>a <b>c</b> <b>c</b>/\\nb<x>x/\\ny</x>c/ \\nd</strofa>")
+ >>> Stanza(s).versify()
+ >>> print etree.tostring(s)
+ <strofa><wers_normalny>a <b>c</b> <b>c</b></wers_normalny><wers_normalny>b<x>x/
+ y</x>c</wers_normalny><wers_normalny>d</wers_normalny></strofa>
+
+ """
+ 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:
+ 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
+ else:
+ verse.text = (verse.text or "") + verse_text
+
+ 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 node in stanzas:
- for child_node in node:
- if child_node.tag in ('slowo_obce', 'wyroznienie'):
- foreign_verses = inner_xml(child_node).split('/\n')
- if len(foreign_verses) > 1:
- new_foreign = ''
- for foreign_verse in foreign_verses:
- if foreign_verse.startswith('<wers'):
- new_foreign += foreign_verse
- else:
- new_foreign += ''.join(('<wers_normalny>', foreign_verse, '</wers_normalny>'))
- set_inner_xml(child_node, new_foreign)
- verses = inner_xml(node).split('/\n')
- if len(verses) > 1:
- modified_inner_xml = ''
- for verse in verses:
- if verse.startswith('<wers') or verse.startswith('<extra'):
- modified_inner_xml += verse
- else:
- modified_inner_xml += ''.join(('<wers_normalny>', verse, '</wers_normalny>'))
- set_inner_xml(node, modified_inner_xml)
+ for stanza in stanzas:
+ Stanza(stanza).versify()
def add_to_manifest(manifest, partno):
""" produces a EPUB file
sample=n: generate sample e-book (with at least n paragraphs)
- cover: a cover.Cover object or True for default
- flags: less-advertising, without-fonts
+ 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):
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())))
+ if document.book_info.funders:
+ document.edoc.getroot().set('funders', u', '.join(
+ document.book_info.funders))
+ if document.book_info.thanks:
+ document.edoc.getroot().set('thanks', document.book_info.thanks)
+
opf = xslt(document.book_info.to_etree(), get_resource('epub/xsltContent.xsl'))
manifest = opf.find('.//' + OPFNS('manifest'))
guide = opf.find('.//' + OPFNS('guide'))
if cover:
if cover is True:
cover = WLCover
- if 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)
cover_file = StringIO()
- c = cover(document.book_info)
- c.save(cover_file)
- c_name = 'cover.%s' % c.ext()
- zip.writestr(os.path.join('OPS', c_name), cover_file.getvalue())
+ 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', c_name)
+ 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(
'<item id="cover" href="cover.html" media-type="application/xhtml+xml" />'))
manifest.append(etree.fromstring(
- '<item id="cover-image" href="%s" media-type="%s" />' % (c_name, c.mime_type())))
+ '<item id="cover-image" href="%s" media-type="%s" />' % (cover_name, bound_cover.mime_type())))
spine.insert(0, etree.fromstring('<itemref idref="cover" linear="no" />'))
opf.getroot()[0].append(etree.fromstring('<meta name="cover" content="cover-image"/>'))
guide.append(etree.fromstring('<reference href="cover.html" type="cover" title="Okładka"/>'))
zip.writestr('OPS/annotations.html', etree.tostring(
html_tree, method="html", pretty_print=True))
+ toc.add("Wesprzyj Wolne Lektury", "support.html")
+ manifest.append(etree.fromstring(
+ '<item id="support" href="support.html" media-type="application/xhtml+xml" />'))
+ spine.append(etree.fromstring(
+ '<itemref idref="support" />'))
+ html_string = open(get_resource('epub/support.html')).read()
+ chars.update(used_chars(etree.fromstring(html_string)))
+ zip.writestr('OPS/support.html', html_string)
+
toc.add("Strona redakcyjna", "last.html")
manifest.append(etree.fromstring(
'<item id="last" href="last.html" media-type="application/xhtml+xml" />'))
if not flags or not 'without-fonts' in flags:
# strip fonts
tmpdir = mkdtemp('-librarian-epub')
- cwd = os.getcwd()
+ 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':
manifest.append(etree.fromstring(
'<item id="%s" href="%s" media-type="font/ttf" />' % (fname, fname)))
rmtree(tmpdir)
- os.chdir(cwd)
+ if cwd is not None:
+ os.chdir(cwd)
zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True))
title = document.book_info.title
margin-right: 2em;
}
-p.minor {
+p.minor-info {
+ text-align: center;
+ margin-bottom: 1em;
font-size: 0.75em;
}
p.footer {
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
+ <title>Wesprzyj Wolne Lektury</title>
+ <link rel="stylesheet" href="style.css" type="text/css" />
+ </head>
+ <body>
+ <div id="book-text" >
+
+ <h2 class="info">Wesprzyj Wolne Lektury!</h2>
+
+ <p class="info">
+ Wolne Lektury to projekt fundacji Nowoczesna Polska – organizacji
+ pożytku publicznego działającej na rzecz wolności korzystania
+ z dóbr kultury.</p>
+
+ <p class="info">
+ Co roku do domeny publicznej przechodzi twórczość kolejnych autorów.
+ Dzięki Twojemu wsparciu będziemy je mogli udostępnić wszystkim bezpłatnie.
+ </p>
+
+ <p class="info">
+ <strong>Jak możesz pomóc?</strong>
+ </p>
+
+ <p class="info">
+ <img src="jedenprocent.png" alt="Logo 1%" /><br/>
+ Przekaż 1% podatku na rozwój Wolnych Lektur:<br/>
+ Fundacja Nowoczesna Polska<br/>
+ KRS 0000070056
+ </p>
+
+ <p class="info">
+ Pomóż uwolnić konkretną książkę, wspierając
+ <a href="http://www.wolnelektury.pl/wesprzyj/">zbiórkę
+ na stronie wolnelektury.pl</a>.
+ </p>
+
+ <p class="info">
+ Przekaż darowiznę na konto:
+ <a href="http://nowoczesnapolska.org.pl/pomoz-nam/wesprzyj-nas/">szczegóły
+ na stronie Fundacji</a>.
+ </p>
+
+ </div>
+ </body>
+</html>
</a>
</xsl:when>
<xsl:otherwise>
- Ten utwór nie jest chroniony prawem autorskim i znajduje się w domenie
+ Ten utwór nie jest objęty majątkowym prawem autorskim i znajduje się w domenie
publicznej, co oznacza że możesz go swobodnie wykorzystywać, publikować
i rozpowszechniać. Jeśli utwór opatrzony jest dodatkowymi materiałami
(przypisy, motywy literackie etc.), które podlegają prawu autorskiemu, to
<xsl:call-template name="editors" />
+ <xsl:call-template name="funders" />
+
<xsl:if test="@data-cover-by">
<p class="info">Okładka na podstawie:
<xsl:choose>
</p>
</xsl:if>
- <div class="info">
- <img src="jedenprocent.png" alt="Logo 1%" />
- <div>Przekaż 1% podatku na rozwój Wolnych Lektur.</div>
- <div>Nazwa organizacji: Fundacja Nowoczesna Polska</div>
- <div>KRS 0000070056</div>
- </div>
-
<p class="info"> </p>
- <p class="minor info">
+ <p class="minor-info">
Plik wygenerowany dnia <span id="file_date"><xsl:value-of select="substring(date:date(), 1, 10)" /></span>.
</p>
</xsl:template>
<xsl:template name="editors">
- <xsl:if test="//dc:contributor.editor[text()]|//dc:contributor.technical_editor[text()]">
+ <xsl:if test="@editors">
<p class="info">
<xsl:text>Opracowanie redakcyjne i przypisy: </xsl:text>
- <xsl:for-each select="//dc:contributor.editor[text()]|//dc:contributor.technical_editor[text() and not(//dc:contributor.editor/text()=text())]">
- <xsl:sort />
- <xsl:if test="position() != 1">, </xsl:if>
- <xsl:apply-templates mode="person" />
- </xsl:for-each>.
- </p>
+ <xsl:value-of select="@editors" />.</p>
</xsl:if>
</xsl:template>
- <xsl:template match="dc:contributor.editor|dc:contributor.technical_editor">
- <br /><xsl:apply-templates mode='person' />
+ <xsl:template name="funders">
+ <xsl:if test="@funders">
+ <p class="minor-info">Publikację ufundowali i ufundowały:
+ <xsl:value-of select="@funders" />.</p>
+ </xsl:if>
</xsl:template>
<xsl:template match="text()" mode="person">
<xsl:call-template name="translators" />
+ <xsl:if test="utwor/@working-copy">
+ <p class="info">[Kopia robocza]</p>
+ </xsl:if>
+
<xsl:if test="not(utwor/@less-advertising)">
<p class="info">
<a>
</p>
</xsl:if>
+ <xsl:if test="utwor/@thanks">
+ <p class="info"><xsl:value-of select="utwor/@thanks" /></p>
+ </xsl:if>
+
<p class="info">
Utwór opracowany został w ramach projektu<a href="http://www.wolnelektury.pl/"> Wolne Lektury</a> przez<a href="http://www.nowoczesnapolska.org.pl/"> fundację Nowoczesna Polska</a>.
</p>
--- /dev/null
+# -*- 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
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+ Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns="http://www.gribuser.ru/xml/fictionbook/2.0">
+
+ <!-- description parsing -->
+ <xsl:template match="rdf:Description" mode="outer">
+ <description>
+ <!-- need to keep ordering here... -->
+
+ <title-info>
+ <!-- obligatory: genre, author, book-title, lang -->
+
+ <!-- XXX -->
+ <genre>literature</genre>
+ <xsl:apply-templates mode="description"
+ select="dc:creator"/>
+ <xsl:apply-templates mode="description"
+ select="dc:title"/>
+ <xsl:apply-templates mode="description"
+ select="dc:date.pd"/>
+ <xsl:apply-templates mode="description"
+ select="dc:language"/>
+ </title-info>
+ <document-info>
+ <!-- obligatory: author, date, id, version -->
+
+ <xsl:apply-templates mode="description"
+ select="dc:contributor.editor"/>
+ <xsl:apply-templates mode="description"
+ select="dc:contributor.technical_editor"/>
+ <program-used>book2fb2</program-used>
+ <!-- maybe today's date instead? -->
+ <xsl:apply-templates mode="description"
+ select="dc:date"/>
+ <xsl:apply-templates mode="description"
+ select="dc:identifier.url"/>
+ <!-- XXX -->
+ <version>0</version>
+ </document-info>
+ <publish-info>
+ <xsl:apply-templates mode="description"
+ select="dc:publisher"/>
+ </publish-info>
+ </description>
+ </xsl:template>
+
+ <xsl:template mode="description"
+ match="dc:creator|dc:contributor.editor|dc:contributor.technical_editor">
+ <!-- last name, first name -->
+ <xsl:variable name="last"
+ select="normalize-space(substring-before(., ','))"/>
+ <xsl:variable name="first"
+ select="normalize-space(substring-after(., ','))"/>
+
+ <author>
+ <first-name><xsl:value-of select="$first"/></first-name>
+ <last-name><xsl:value-of select="$last"/></last-name>
+ </author>
+ </xsl:template>
+ <xsl:template mode="description" match="dc:title">
+ <book-title><xsl:value-of select="."/></book-title>
+ </xsl:template>
+ <xsl:template mode="description" match="dc:language">
+ <lang><xsl:value-of select="."/></lang>
+ </xsl:template>
+ <xsl:template mode="description" match="dc:date.pd|dc:date">
+ <date><xsl:value-of select="."/></date>
+ </xsl:template>
+ <xsl:template mode="description" match="dc:publisher">
+ <publisher><xsl:value-of select="."/></publisher>
+ </xsl:template>
+ <xsl:template mode="description" match="dc:identifier.url">
+ <id><xsl:value-of select="."/></id>
+ </xsl:template>
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+ Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:wl="http://wolnelektury.pl/functions"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns="http://www.gribuser.ru/xml/fictionbook/2.0"
+ xmlns:l="http://www.w3.org/1999/xlink">
+
+ <xsl:template mode="para" match="lista_osob">
+ <empty-line/>
+ <xsl:apply-templates mode="para"/>
+ <empty-line/>
+ </xsl:template>
+
+ <xsl:template mode="para" match="kwestia">
+ <empty-line/>
+ <xsl:apply-templates mode="para"/>
+ <empty-line/>
+ </xsl:template>
+
+ <xsl:template mode="para" match="lista_osoba">
+ <p><xsl:apply-templates mode="inline"/></p>
+ </xsl:template>
+
+ <xsl:template mode="para" match="naglowek_listy|naglowek_osoba">
+ <p><strong><xsl:apply-templates mode="inline"/></strong></p>
+ </xsl:template>
+
+ <xsl:template mode="para" match="miejsce_czas|didaskalia|didask_tekst">
+ <p><emphasis><xsl:apply-templates mode="inline"/></emphasis></p>
+ </xsl:template>
+
+ <xsl:template mode="inline" match="didaskalia|didask_tekst">
+ <emphasis><xsl:apply-templates mode="inline"/></emphasis>
+ </xsl:template>
+
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+ Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:wl="http://wolnelektury.pl/functions"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns="http://www.gribuser.ru/xml/fictionbook/2.0"
+ xmlns:l="http://www.w3.org/1999/xlink">
+
+ <xsl:include href="description.xslt"/>
+ <xsl:include href="footnotes.xslt"/>
+ <xsl:include href="inline.xslt"/>
+ <xsl:include href="paragraphs.xslt"/>
+ <xsl:include href="poems.xslt"/>
+ <xsl:include href="sections.xslt"/>
+ <xsl:include href="drama.xslt"/>
+
+ <xsl:strip-space elements="*"/>
+ <xsl:output encoding="utf-8" method="xml" indent="yes"/>
+
+ <xsl:template match="utwor">
+ <FictionBook>
+ <xsl:apply-templates mode="outer"/>
+
+ <body name="footnotes">
+ <xsl:apply-templates mode="footnotes"/>
+ </body>
+ </FictionBook>
+ </xsl:template>
+
+ <!-- we can't handle lyrics nicely yet -->
+ <xsl:template match="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp" mode="outer">
+ <body> <!-- main body for main book flow -->
+ <xsl:if test="autor_utworu or nazwa_utworu">
+ <title>
+ <xsl:apply-templates mode="title"
+ select="autor_utworu|dzielo_nadrzedne|nazwa_utworu|podtytul"/>
+ <xsl:call-template name="translators" />
+ </title>
+ </xsl:if>
+
+ <epigraph>
+ <p>
+ Utwór opracowany został w ramach projektu
+ <a l:href="http://www.wolnelektury.pl/">Wolne Lektury</a>
+ przez <a l:href="http://www.nowoczesnapolska.org.pl/">fundację
+ Nowoczesna Polska</a>.
+ </p>
+ </epigraph>
+
+ <xsl:call-template name="section" />
+ </body>
+ </xsl:template>
+
+ <xsl:template match="uwaga" mode="outer"/>
+ <xsl:template match="extra" mode="outer"/>
+
+ <xsl:template mode="title" match="*">
+ <!-- title -->
+
+ <p><xsl:apply-templates mode="inline"/></p>
+ </xsl:template>
+
+ <xsl:template name="translators">
+ <xsl:if test="//dc:contributor.translator">
+ <p>
+ <xsl:text>tłum. </xsl:text>
+ <xsl:for-each select="//dc:contributor.translator">
+ <xsl:if test="position() != 1">, </xsl:if>
+ <xsl:apply-templates mode="person" />
+ </xsl:for-each>
+ </p>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="text()" mode="person">
+ <xsl:value-of select="wl:person_name(.)" />
+ </xsl:template>
+
+
+ <xsl:template match="uwaga" mode="title"/>
+ <xsl:template match="extra" mode="title"/>
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+ Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:wl="http://wolnelektury.pl/functions"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns="http://www.gribuser.ru/xml/fictionbook/2.0"
+ xmlns:l="http://www.w3.org/1999/xlink">
+
+ <!-- footnote body mode -->
+ <xsl:template match="pa|pe|pr|pt" mode="footnotes">
+ <!-- we number them absolutely -->
+ <xsl:variable name="n" select="count(preceding::pa) + count(preceding::pe) + count(preceding::pr) + count(preceding::pt) + 1"/>
+
+ <xsl:element name="section">
+ <xsl:attribute name="id">fn<xsl:value-of select="$n"/></xsl:attribute>
+
+ <p><xsl:apply-templates mode="inline"/>
+ <xsl:if test="local-name() = 'pa'">
+ <xsl:text> [przypis autorski]</xsl:text>
+ </xsl:if></p>
+ </xsl:element>
+ </xsl:template>
+ <xsl:template match="text()" mode="footnotes"/>
+
+ <!-- footnote links -->
+ <xsl:template match="pa|pe|pr|pt" mode="inline">
+ <xsl:variable name="n" select="count(preceding::pa) + count(preceding::pe) + count(preceding::pr) + count(preceding::pt) + 1"/>
+ <xsl:element name="a">
+ <xsl:attribute name="type">note</xsl:attribute>
+ <xsl:attribute name="l:href">#fn<xsl:value-of select="$n"/></xsl:attribute>
+
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$n"/>
+ <xsl:text>]</xsl:text>
+ </xsl:element>
+ </xsl:template>
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+ Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:wl="http://wolnelektury.pl/functions"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns="http://www.gribuser.ru/xml/fictionbook/2.0"
+ xmlns:l="http://www.w3.org/1999/xlink">
+
+ <!-- inline elements -->
+
+ <!-- ignored -->
+ <xsl:template match="motyw" mode="inline"/>
+
+ <!-- formatting -->
+ <xsl:template match="slowo_obce" mode="inline">
+ <emphasis>
+ <xsl:apply-templates mode="inline"/>
+ </emphasis>
+ </xsl:template>
+ <xsl:template match="tytul_dziela" mode="inline">
+ <emphasis>
+ <xsl:if test="@typ">„</xsl:if>
+ <xsl:apply-templates mode="inline"/>
+ <xsl:if test="@typ">”</xsl:if>
+ </emphasis>
+ </xsl:template>
+ <xsl:template match="wyroznienie" mode="inline">
+ <strong>
+ <xsl:apply-templates mode="inline"/>
+ </strong>
+ </xsl:template>
+
+ <!-- text -->
+ <xsl:template match="text()" mode="inline">
+ <xsl:value-of select="wl:substitute_entities(.)"/>
+ </xsl:template>
+
+ <xsl:template match="uwaga" mode="inline"/>
+ <xsl:template match="extra" mode="inline"/>
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+ Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:wl="http://wolnelektury.pl/functions"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns="http://www.gribuser.ru/xml/fictionbook/2.0"
+ xmlns:l="http://www.w3.org/1999/xlink">
+
+ <!-- in paragraph mode -->
+
+ <xsl:template mode="para" match="akap|akap_dialog|akap_cd|motto_podpis">
+ <!-- paragraphs & similar -->
+
+ <p><xsl:apply-templates mode="inline"/></p>
+ </xsl:template>
+
+ <xsl:template mode="para" match="dlugi_cytat|motto|dedykacja|nota">
+ <cite><xsl:apply-templates mode="para"/></cite>
+ </xsl:template>
+
+ <xsl:template mode="para" match="srodtytul">
+ <p><strong><xsl:apply-templates mode="inline"/></strong></p>
+ </xsl:template>
+
+ <xsl:template mode="para" match="sekcja_swiatlo">
+ <empty-line/><empty-line/><empty-line/>
+ </xsl:template>
+
+ <xsl:template mode="para" match="sekcja_asterysk">
+ <empty-line/><p>*</p><empty-line/>
+ </xsl:template>
+
+ <xsl:template mode="para" match="separator_linia">
+ <empty-line/><p>————————</p><empty-line/>
+ </xsl:template>
+
+
+
+ <xsl:template mode="para" match="*"/>
+ <xsl:template mode="sections" match="*"/>
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+ Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:wl="http://wolnelektury.pl/functions"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns="http://www.gribuser.ru/xml/fictionbook/2.0"
+ xmlns:l="http://www.w3.org/1999/xlink">
+
+ <!-- poems -->
+
+ <!-- match poem citations -->
+ <xsl:template mode="para" match="poezja_cyt">
+ <cite>
+ <poem>
+ <xsl:apply-templates mode="para"/>
+ </poem>
+ </cite>
+ </xsl:template>
+
+ <!-- regular poem elements -->
+ <xsl:template mode="para" match="strofa">
+ <stanza>
+ <xsl:apply-templates mode="poem"/>
+ </stanza>
+ </xsl:template>
+
+ <!-- XXX: it should be done elsewhere but our cheap verse splitting
+ puts it here -->
+ <xsl:template match="motyw" mode="poem"/>
+
+ <xsl:template mode="poem" match="wers_normalny|wers_cd|wers_wciety|wers_akap">
+ <v><xsl:apply-templates mode="inline"/></v>
+ </xsl:template>
+</xsl:stylesheet>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
+ Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:wl="http://wolnelektury.pl/functions"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns="http://www.gribuser.ru/xml/fictionbook/2.0"
+ xmlns:l="http://www.w3.org/1999/xlink">
+
+ <xsl:template name="section">
+ <!-- All the <_section> are in the end. -->
+ <xsl:if test="count(*) > count(_section)">
+ <section>
+ <xsl:choose>
+ <xsl:when test="(local-name() = 'liryka_l' or local-name() = 'liryka_lp')
+ and count(_section) = 0">
+ <poem>
+ <xsl:apply-templates mode="para" />
+ </poem>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates mode="para" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </section>
+ </xsl:if>
+
+ <!-- Now, recursively, all the _section tags. -->
+ <xsl:apply-templates mode="section" select="_section" />
+ </xsl:template>
+
+ <xsl:template match="_section" mode="para" />
+ <xsl:template match="_section" mode="section" >
+ <section>
+ <xsl:call-template name="section" />
+ </section>
+ </xsl:template>
+
+ <!-- actual headings -->
+ <xsl:template match="naglowek_czesc|naglowek_rozdzial|naglowek_podrozdzial|naglowek_akt|naglowek_scena" mode="para">
+ <title><p><xsl:apply-templates mode="inline"/></p></title>
+ </xsl:template>
+</xsl:stylesheet>
if html_has_content(result):
add_anchors(result.getroot())
+ add_table_of_themes(result.getroot())
add_table_of_contents(result.getroot())
return OutputFile.from_string(etree.tostring(result, method='html',
# Process all elements except begin and end
else:
# Omit annotation tags
- if len(element.get('name', '')) or element.get('class', '') == 'annotation':
+ 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)
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',) or e.get('class') in ('person-list',)):
+ 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, ''.join(element.xpath('text()')), []))
+ sections[-1][3].append((counter, element.tag, element_text, []))
else:
- sections.append((counter, element.tag, ''.join(element.xpath('text()')), []))
+ sections.append((counter, element.tag, element_text, []))
add_anchor(element, "s%d" % counter, with_link=False)
counter += 1
root.insert(0, toc)
+
+def add_table_of_themes(root):
+ try:
+ from sortify import sortify
+ except ImportError:
+ sortify = lambda x: x
+
+ book_themes = {}
+ for fragment in root.findall('.//a[@class="theme-begin"]'):
+ if not fragment.text:
+ continue
+ theme_names = [s.strip() for s in fragment.text.split(',')]
+ for theme_name in theme_names:
+ book_themes.setdefault(theme_name, []).append(fragment.get('name'))
+ book_themes = book_themes.items()
+ book_themes.sort(key=lambda s: sortify(s[0]))
+ themes_div = etree.Element('div', id="themes")
+ themes_ol = etree.SubElement(themes_div, 'ol')
+ for theme_name, fragments in book_themes:
+ themes_li = etree.SubElement(themes_ol, 'li')
+ themes_li.text = "%s: " % theme_name
+ for i, fragment in enumerate(fragments):
+ item = etree.SubElement(themes_li, 'a', href="#%s" % fragment)
+ item.text = str(i + 1)
+ item.tail = ' '
+ root.insert(0, themes_div)
+
+
def extract_annotations(html_path):
"""For each annotation, yields a tuple: anchor, text, html."""
wldoc: a WLDocument
sample=n: generate sample e-book (with at least n paragraphs)
- cover: a cover.Cover object
+ cover: a cover.Cover factory overriding default
flags: less-advertising,
"""
if not cover:
cover = WLCover
cover_file = NamedTemporaryFile(suffix='.png', delete=False)
- c = cover(book_info)
- c.save(cover_file)
+ bound_cover = cover(book_info)
+ bound_cover.save(cover_file)
- if cover.uses_dc_cover:
+ 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:
flags = ('less-advertising',)
-class VirtualoEpubPackager(Packager):
+class VirtualoPackager(Packager):
@staticmethod
def utf_trunc(text, limit):
""" truncates text to at most `limit' bytes in utf-8 """
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(epub.transform(doc),
+ doc.save_output_file(doc.as_epub(),
output_path=outfile)
- doc.save_output_file(epub.transform(doc, sample=25),
+ 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' % {
#
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
LINE_SWAP_EXPR = re.compile(r'/\s', re.MULTILINE | re.UNICODE)
provider = None
- def __init__(self, edoc, parse_dublincore=True, provider=None):
+ def __init__(self, edoc, parse_dublincore=True, provider=None,
+ strict=False, meta_fallbacks=None):
self.edoc = edoc
self.provider = provider
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)
+ self.book_info = dcparser.BookInfo.from_element(
+ self.rdf_elem, fallbacks=meta_fallbacks, strict=strict)
else:
self.book_info = None
return cls.from_file(StringIO(xml), *args, **kwargs)
@classmethod
- def from_file(cls, xmlfile, parse_dublincore=True, provider=None):
+ def from_file(cls, xmlfile, *args, **kwargs):
# first, prepare for parsing
if isinstance(xmlfile, basestring):
parser = etree.XMLParser(remove_blank_text=False)
tree = etree.parse(StringIO(data.encode('utf-8')), parser)
- return cls(tree, parse_dublincore=parse_dublincore, provider=provider)
+ return cls(tree, *args, **kwargs)
except (ExpatError, XMLSyntaxError, XSLTApplyError), e:
raise ParseError(e)
xpath = self.path_to_xpath(key)
node = self.edoc.xpath(xpath)[0]
repl = etree.fromstring(u"<%s>%s</%s>" %(node.tag, data, node.tag) )
- node.getparent().replace(node, repl);
+ node.getparent().replace(node, repl)
except Exception, e:
unmerged.append( repr( (key, xpath, e) ) )
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 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 = os.path.join(save_path,
unicode(self.book_info.author).encode('utf-8'))
save_path = os.path.join(save_path,
- self.book_info.uri.filename_stem())
+ self.book_info.uri.slug)
if ext:
save_path += '.%s' % ext
else:
# 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
'wl2tex': 'pdf/wl2tex.xslt',
}
-CUSTOMIZATIONS = [
- 'nofootnotes',
- 'nothemes',
- 'onehalfleading',
- 'doubleleading',
- 'nowlfont',
- ]
+#CUSTOMIZATIONS = [
+# 'nofootnotes',
+# 'nothemes',
+# 'defaultleading',
+# 'onehalfleading',
+# 'doubleleading',
+# 'nowlfont',
+# ]
def insert_tags(doc, split_re, tagname, exclude=None):
""" inserts <tagname> for every occurence of `split_re' in text nodes in the `doc' tree
def parse_creator(doc):
- """ find all dc:creator and dc.contributor tags and add *_parsed versions with forenames first """
+ """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', 'contributor.editor', 'contributor.technical_editor')),
+ 'creator', 'contributor.translator')),
namespaces = {'dc': str(DCNS)})[::-1]:
if not person.text:
continue
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 object
+ 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
- document.edoc.getroot().set('data-cover-width', str(cover.width))
- document.edoc.getroot().set('data-cover-height', str(cover.height))
- if 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)
+ 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:
- document.edoc.getroot().set('flag-' + flag, 'yes')
+ root.set('flag-' + flag, 'yes')
# check for LaTeX packages
if morefloats:
- document.edoc.getroot().set('morefloats', morefloats.lower())
+ root.set('morefloats', morefloats.lower())
elif package_available('morefloats', 'maxfloats=19'):
- document.edoc.getroot().set('morefloats', 'new')
+ root.set('morefloats', 'new')
# add customizations
if customizations is not None:
- document.edoc.getroot().set('customizations', u','.join(customizations))
+ root.set('customizations', u','.join(customizations))
+
+ # add editors info
+ root.set('editors', u', '.join(sorted(
+ editor.readable() for editor in document.editors())))
+ if document.book_info.funders:
+ root.set('funders', u', '.join(document.book_info.funders))
+ if document.book_info.thanks:
+ root.set('thanks', document.book_info.thanks)
# hack the tree
move_motifs_inside(document.edoc)
temp = mkdtemp('-wl2pdf')
if cover:
- c = cover(document.book_info)
with open(os.path.join(temp, 'cover.png'), 'w') as f:
- c.save(f)
+ bound_cover.save(f)
del document # no longer needed large object :)
shutil.copy(get_resource('pdf/wl.cls'), temp)
shutil.copy(get_resource('res/wl-logo.png'), temp)
- cwd = os.getcwd()
+ try:
+ cwd = os.getcwd()
+ except OSError:
+ cwd = None
os.chdir(temp)
if verbose:
if p:
raise ParseError("Error parsing .tex file")
- os.chdir(cwd)
+ 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')
text = re.sub(ur"([\u0400-\u04ff]+)", ur"<alien>\1</alien>", text)
- document = WLDocument.from_string(text, parse_dublincore=True)
+ document = WLDocument.from_string(text,
+ parse_dublincore=True, provider=provider)
document.swap_endlines()
for child_uri in document.book_info.parts:
%
% 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
%% \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}}
\vspace{.5em}
\fi
+ \ifdefined\thanknote
+ \thanknote
+ \vspace{.5em}
+ \fi
+
Utwór opracowany został w ramach projektu \href{http://www.wolnelektury.pl/}{Wolne Lektury}
przez \href{http://nowoczesnapolska.org.pl}{fundację Nowoczesna Polska}.
\editors
+ \ifdefined\funders
+ \vspace{.6em}
+ \funders
+ \fi
+
+ \ifdefined\coverby
+ \vspace{.6em}
+ \coverby
+ \fi
+
\vspace{.6em}
- \coverby
+ \emph{Wesprzyj Wolne Lektury!}
+
+ Wolne Lektury to projekt fundacji Nowoczesna Polska – organizacji
+ pożytku publicznego działającej na rzecz wolności korzystania
+ z dóbr kultury.
+
+ Co roku do domeny publicznej przechodzi twórczość kolejnych autorów.
+ Dzięki Twojemu wsparciu będziemy je mogli udostępnić wszystkim bezpłatnie.
+
+ \vspace{.6em}
+ \emph{Jak możesz pomóc?}
+
+ Przekaż 1\% podatku na rozwój Wolnych Lektur:
+ Fundacja Nowoczesna Polska, KRS 0000070056.
+
+ Pomóż uwolnić konkretną książkę, wspierając
+ \href{http://www.wolnelektury.pl/wesprzyj/}{zbiórkę na stronie wolnelektury.pl}.
+
+ Przekaż darowiznę na konto:
+ \href{http://nowoczesnapolska.org.pl/pomoz-nam/wesprzyj-nas/}{szczegóły na stronie Fundacji}.
\color{black}
}
<parm>210mm</parm>
</cmd>
</xsl:if>
+ <xsl:if test="@thanks">
+ <TeXML escape="0">
+ \def\thanknote{<TeXML escape="1"><xsl:value-of select="@thanks" /></TeXML>}
+ </TeXML>
+ </xsl:if>
<cmd name="maketitle" />
<cmd name="tytul"><parm>
<xsl:apply-templates select="utwor" mode="part" />
<TeXML escape="0">
- \def\coverby{
- <xsl:if test="@data-cover-by">Okładka na podstawie:
+ <xsl:if test="@data-cover-by">
+ \def\coverby{Okładka na podstawie:
<xsl:choose>
<xsl:when test="@data-cover-source">
\href{\datacoversource}{\datacoverby}
\datacoverby{}
</xsl:otherwise>
</xsl:choose>
- </xsl:if>
}
+ </xsl:if>
+ \def\editors{<TeXML escape="1"><xsl:call-template name="editors" /></TeXML>}
+ <xsl:if test="@funders">
+ \def\funders{Publikację ufundowali i ufundowały:
+ <TeXML escape="1"><xsl:value-of select="@funders" /></TeXML>.}
+ </xsl:if>
</TeXML>
<cmd name="editorialsection" />
+
</env>
</TeXML>
</xsl:template>
\def\bookurl{<xsl:value-of select=".//dc:identifier.url" />}
- \def\rightsinfo{Ten utwór nie jest chroniony prawem autorskim i~znajduje się w~domenie
+ \def\rightsinfo{Ten utwór nie jest objęty majątkowym prawem autorskim i~znajduje się w~domenie
publicznej, co oznacza że możesz go swobodnie wykorzystywać, publikować
i~rozpowszechniać. Jeśli utwór opatrzony jest dodatkowymi materiałami
(przypisy, motywy literackie etc.), które podlegają prawu autorskiemu, to
\vspace{.6em}
</xsl:if>}
\def\description{<xsl:apply-templates select=".//dc:description" mode="inline" />}
- \def\editors{<xsl:call-template name="editors" />}
</TeXML>
</xsl:template>
</xsl:template>
<xsl:template name="editors">
- <xsl:if test="//dc:contributor.editor_parsed|//dc:contributor.technical_editor_parsed">
+ <xsl:if test="@editors">
<xsl:text>Opracowanie redakcyjne i przypisy: </xsl:text>
- <xsl:for-each select="//dc:contributor.editor_parsed|//dc:contributor.technical_editor_parsed[not(//dc:contributor.editor_parsed/text()=text())]">
- <xsl:sort select="@sortkey" />
- <xsl:if test="position() != 1">, </xsl:if>
- <xsl:apply-templates mode="inline" />
- </xsl:for-each>.
+ <xsl:value-of select="@editors" />
+ <xsl:text>.</xsl:text>
</xsl:if>
</xsl:template>
class WLPictureURI(WLURI):
_re_wl_uri = re.compile('http://wolnelektury.pl/katalog/obraz/'
- '(?P<slug>[-a-z0-9]+)(/(?P<lang>[a-z]{3}))?/?$')
-
- def __init__(self, *args, **kw):
- super(WLPictureURI, self).__init__(*args, **kw)
+ '(?P<slug>[-a-z0-9]+)/?$')
@classmethod
- def from_slug_and_lang(cls, slug, lang):
+ def from_slug(cls, slug):
uri = 'http://wolnelektury.pl/katalog/obraz/%s/' % slug
return cls(uri)
- def filename_stem(self):
- return self.slug
+def as_wlpictureuri_strict(text):
+ return WLPictureURI.strict(text)
class PictureInfo(WorkInfo):
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),
+ Field(DCNS('identifier.url'), 'url', WLPictureURI,
+ strict=as_wlpictureuri_strict),
)
- def validate(self):
- """
- WorkInfo has a language validation code only, which we do not need.
- """
- pass
-
class ImageStore(object):
EXT = ['gif', 'jpeg', 'png', 'swf', 'psd', 'bmp'
%(license_description)s.%(source)s
-%(description)s%(contributors)s
+%(description)s%(contributors)s%(funders)s
"""
def transform(wldoc, flags=None, **options):
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/)"
+ license_description = u"Ten utwór nie jest objęty majątkowym prawem autorskim i znajduje się w domenie publicznej, co oznacza że możesz go swobodnie wykorzystywać, publikować i rozpowszechniać. Jeśli utwór opatrzony jest dodatkowymi materiałami (przypisy, motywy literackie etc.), które podlegają prawu autorskiemu, to te dodatkowe materiały udostępnione są na licencji Creative Commons Uznanie Autorstwa – Na Tych Samych Warunkach 3.0 PL (http://creativecommons.org/licenses/by-sa/3.0/)"
source = parsed_dc.source_name
if 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
+ contributors = "\n\nOpracowanie redakcyjne i przypisy: %s." % contributors
+ funders = ', '.join(parsed_dc.funders)
+ if funders:
+ funders = u"\n\nPublikację ufundowali i ufundowały: %s." % funders
else:
description = 'Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl).'
url = '*' * 10
license_description = ""
source = ""
contributors = ""
+ funders = ""
return OutputFile.from_string((TEMPLATE % {
'description': description,
'url': url,
'text': unicode(result),
'source': source,
'contributors': contributors,
+ 'funders': funders,
}).encode('utf-8'))
else:
return OutputFile.from_string(unicode(result).encode('utf-8'))
xmlns:dc="http://purl.org/dc/elements/1.1/" >
<xsl:output encoding="utf-8" indent="yes" omit-xml-declaration = "yes" version="2.0" />
+<xsl:strip-space elements="opowiadanie powiesc dramat_wierszowany_l dramat_wierszowany_lp dramat_wspolczesny liryka_l liryka_lp wywiad"/>
<xsl:template match="utwor">
<xsl:choose>
<xsl:when test="@full-page">
<!-- Section headers (included in index)-->
<xsl:template match="naglowek_akt|naglowek_czesc|srodtytul">
+ <xsl:call-template name="section-anchor"/>
<h2><xsl:apply-templates mode="inline" /></h2>
</xsl:template>
<xsl:template match="naglowek_scena|naglowek_rozdzial">
+ <xsl:call-template name="section-anchor"/>
<h3><xsl:apply-templates mode="inline" /></h3>
</xsl:template>
<xsl:template match="naglowek_osoba|naglowek_podrozdzial">
+ <xsl:call-template name="section-anchor"/>
<h4><xsl:apply-templates mode="inline" /></h4>
</xsl:template>
<!-- Other paragraph tags -->
<xsl:template match="miejsce_czas">
+ <xsl:call-template name="section-anchor"/>
<p class="place-and-time"><xsl:apply-templates mode="inline" /></p>
</xsl:template>
<xsl:template match="didaskalia">
+ <xsl:call-template name="section-anchor"/>
<div class="didaskalia"><xsl:apply-templates mode="inline" /></div>
</xsl:template>
</xsl:template>
<xsl:template match="akap|akap_dialog|akap_cd">
- <p class="paragraph"><xsl:apply-templates mode="inline" /></p>
+ <p class="paragraph">
+ <xsl:call-template name="section-anchor"/>
+ <xsl:apply-templates mode="inline" />
+ </p>
</xsl:template>
<xsl:template match="strofa">
<div class="stanza">
+ <xsl:call-template name="section-anchor"/>
<xsl:choose>
<xsl:when test="count(br) > 0">
<xsl:call-template name="verse">
<xsl:value-of select="wl:substitute_entities(.)" />
</xsl:template>
+<!-- ========= -->
+<!-- = utils = -->
+<!-- ========= -->
+<xsl:template name="section-anchor">
+ <!--
+ this formula works as follows:
+ - get all ancestors including self
+ - choose the header (third one from root): utwor/book-type/header
+ - get all preceding siblings
+ - count them
+ - create an <a name="sec123"/> tag.
+ -->
+ <a name="{concat('sec', count(ancestor-or-self::*[last()-2]/preceding-sibling::*) + 1)}" />
+</xsl:template>
-</xsl:stylesheet>
\ No newline at end of file
+</xsl:stylesheet>
# 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 optparse
+from StringIO import StringIO
+from librarian import OutputFile
+from librarian.book2anything import Book2Anything, Option
-from librarian import ParseError
-from librarian.parser import WLDocument
-from librarian.cover import WLCover
+class Book2Cover(Book2Anything):
+ format_name = "JPEG"
+ ext = "jpg"
+ uses_cover = True
+ cover_optional = False
-if __name__ == '__main__':
- # Parse commandline arguments
- usage = """Usage: %prog [options] SOURCE [SOURCE...]
- Create cover images for SOURCE files."""
-
- parser = optparse.OptionParser(usage=usage)
-
- parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False,
- help='print status messages to stdout')
+ 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.'),
+ ]
- options, input_filenames = parser.parse_args()
+ @staticmethod
+ def transform(wldoc, cover, *args, **kwargs):
+ return wldoc.as_cover(cover_class=cover, *args, **kwargs)
- if len(input_filenames) < 1:
- parser.print_help()
- exit(1)
- # Do some real work
- for input_filename in input_filenames:
- if options.verbose:
- print input_filename
-
- output_filename = os.path.splitext(input_filename)[0] + '.png'
-
- doc = WLDocument.from_file(input_filename)
- WLCover(doc.book_info).save(output_filename)
+if __name__ == '__main__':
+ Book2Cover.run()
# 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
-import optparse
+from librarian.book2anything import Book2Anything, Option
-from librarian import DirDocProvider, ParseError
-from librarian.parser import WLDocument
+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')
+ ]
-if __name__ == '__main__':
- # Parse commandline arguments
- usage = """Usage: %prog [options] SOURCE [SOURCE...]
- Convert SOURCE files to EPUB format."""
-
- 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('-c', '--with-cover', action='store_true', dest='with_cover', default=False,
- help='create default cover')
- parser.add_option('-d', '--make-dir', action='store_true', dest='make_dir', default=False,
- help='create a directory for author and put the PDF 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')
-
- options, input_filenames = parser.parse_args()
-
- if len(input_filenames) < 1:
- parser.print_help()
- exit(1)
- # Do some real work
- try:
- for main_input in input_filenames:
- if options.verbose:
- print main_input
-
- path, fname = os.path.realpath(main_input).rsplit('/', 1)
- provider = DirDocProvider(path)
- if not (options.output_file or options.output_dir):
- output_file = os.path.splitext(main_input)[0] + '.epub'
- else:
- output_file = None
-
- doc = WLDocument.from_file(main_input, provider=provider)
- epub = doc.as_epub(cover=options.with_cover)
-
- doc.save_output_file(epub,
- output_file, options.output_dir, options.make_dir, 'epub')
-
- except ParseError, e:
- print '%(file)s:%(name)s:%(message)s' % {
- 'file': main_input,
- 'name': e.__class__.__name__,
- 'message': e
- }
+if __name__ == '__main__':
+ Book2Epub.run()
--- /dev/null
+#!/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.
+#
+from librarian.book2anything import Book2Anything
+
+
+class Book2Fb2(Book2Anything):
+ format_name = "FB2"
+ ext = "fb2"
+ uses_cover = False
+ uses_provider = True
+
+
+if __name__ == '__main__':
+ Book2Fb2.run()
# 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 optparse
-
-from librarian import ParseError
-from librarian.parser import WLDocument
+from librarian.book2anything import Book2Anything, Option
+
+
+class Book2Html(Book2Anything):
+ format_name = "HTML"
+ ext = "html"
+ uses_cover = False
+ uses_provider = False
+ transform_flags = [
+ Option('-r', '--raw', dest='full-page',
+ 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__':
- # Parse commandline arguments
- usage = """Usage: %prog [options] SOURCE [SOURCE...]
- Convert SOURCE files to HTML format."""
-
- 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('-i', '--ignore-dublin-core', action='store_false', dest='parse_dublincore', default=True,
- help='don\'t try to parse dublin core metadata')
-
- options, input_filenames = parser.parse_args()
-
- if len(input_filenames) < 1:
- parser.print_help()
- exit(1)
-
- # Do some real work
- for input_filename in input_filenames:
- if options.verbose:
- print input_filename
-
- output_filename = os.path.splitext(input_filename)[0] + '.html'
- try:
- doc = WLDocument.from_file(input_filename,
- parse_dublincore=options.parse_dublincore)
- html = doc.as_html(flags=('full-page',))
- doc.save_output_file(html, output_path=output_filename)
- except ParseError, e:
- print '%(file)s:%(name)s:%(message)s' % {
- 'file': input_filename,
- 'name': e.__class__.__name__,
- 'message': e,
- }
- except IOError, e:
- print '%(file)s:%(name)s:%(message)s' % {
- 'file': input_filename,
- 'name': e.__class__.__name__,
- 'message': e.strerror,
- }
- except BaseException, e:
- print '%(file)s:%(etype)s:%(message)s' % {
- 'file': input_filename,
- 'etype': e.__class__.__name__,
- 'message': e,
- }
- raise
-
+ Book2Html.run()
+++ /dev/null
-#!/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 os
-import optparse
-
-from librarian import ParseError
-from librarian.parser import WLDocument
-
-
-if __name__ == '__main__':
- # Parse commandline arguments
- usage = """Usage: %prog [options] SOURCE [SOURCE...]
- Convert SOURCE files to HTML format."""
-
- 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('-i', '--ignore-dublin-core', action='store_false', dest='parse_dublincore', default=True,
- help='don\'t try to parse dublin core metadata')
-
- options, input_filenames = parser.parse_args()
-
- if len(input_filenames) < 1:
- parser.print_help()
- exit(1)
-
- # Do some real work
- for input_filename in input_filenames:
- if options.verbose:
- print input_filename
-
- output_filename = os.path.splitext(input_filename)[0] + '.html'
- try:
- doc = WLDocument.from_file(input_filename,
- parse_dublincore=options.parse_dublincore)
- html = doc.as_html(flags=('full-page',), stylesheet='partial')
- doc.save_output_file(html, output_path=output_filename)
- except ParseError, e:
- print '%(file)s:%(name)s:%(message)s' % {
- 'file': input_filename,
- 'name': e.__class__.__name__,
- 'message': e.message.encode('utf-8')
- }
- except IOError, e:
- print '%(file)s:%(name)s:%(message)s' % {
- 'file': input_filename,
- 'name': e.__class__.__name__,
- 'message': e.strerror,
- }
- except BaseException, e:
- print '%(file)s:%(etype)s:%(message)s' % {
- 'file': input_filename,
- 'etype': e.__class__.__name__,
- 'message': e.message.encode('utf-8'),
- }
- raise
-
# 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
-import optparse
+from librarian.book2anything import Book2Anything
-from librarian import DirDocProvider, ParseError
-from librarian.parser import WLDocument
+class Book2Mobi(Book2Anything):
+ format_name = "MOBI"
+ ext = "mobi"
+ uses_cover = True
+ cover_optional = False
+ uses_provider = True
-if __name__ == '__main__':
- # Parse commandline arguments
- usage = """Usage: %prog [options] SOURCE [SOURCE...]
- Convert SOURCE files to MOBI format."""
-
- 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 PDF 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')
-
- options, input_filenames = parser.parse_args()
- if len(input_filenames) < 1:
- parser.print_help()
- exit(1)
-
- # Do some real work
- try:
- for main_input in input_filenames:
- path, fname = os.path.realpath(main_input).rsplit('/', 1)
- provider = DirDocProvider(path)
- if not (options.output_file or options.output_dir):
- output_file = os.path.splitext(main_input)[0] + '.mobi'
- else:
- output_file = None
-
- doc = WLDocument.from_file(main_input, provider=provider)
- mobi = doc.as_mobi()
-
- doc.save_output_file(mobi,
- output_file, options.output_dir, options.make_dir, 'mobi')
- except ParseError, e:
- print '%(file)s:%(name)s:%(message)s' % {
- 'file': main_input,
- 'name': e.__class__.__name__,
- 'message': e
- }
+if __name__ == '__main__':
+ Book2Mobi.run()
if options.gandalf_pdf:
packagers.GandalfPdfPackager.prepare(input_filenames, options.output_dir, options.verbose)
if options.virtualo:
- packagers.VirtualoEpubPackager.prepare(input_filenames, options.output_dir, options.verbose)
+ 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:
# 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 optparse import OptionParser
+from librarian.book2anything import Book2Anything, Option
-from librarian import DirDocProvider, ParseError
-from librarian.parser import WLDocument
+class Book2Pdf(Book2Anything):
+ format_name = "PDF"
+ ext = "pdf"
+ uses_cover = True
+ uses_provider = True
+ transform_args = [
+ Option('-t', '--save-tex', dest='save_tex', metavar='FILE',
+ help='path to save the intermediary LaTeX file to'),
+ Option('-m', '--morefloats', dest='morefloats', metavar='old/new/none',
+ help='force morefloats in old (<1.0c), new (>=1.0c) or none')
+ ]
-if __name__ == '__main__':
- usage = """Usage: %prog [options] SOURCE [SOURCE...]
- Convert SOURCE files to PDF format."""
-
- parser = OptionParser(usage)
- parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False,
- help='make lots of noise and revert to default interaction in LaTeX')
- parser.add_option('-c', '--with-cover', action='store_true', dest='with_cover', default=False,
- help='create default cover')
- parser.add_option('-d', '--make-dir', action='store_true', dest='make_dir', default=False,
- help='create a directory for author and put the PDF in it')
- parser.add_option('-t', '--save-tex', dest='save_tex', metavar='FILE',
- help='path to save the intermediary LaTeX file to')
- 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')
- parser.add_option('-m', '--morefloats', dest='morefloats', metavar='old/new/none',
- help='force morefloats in old (<1.0c), new (>=1.0c) or none')
- (options, args) = parser.parse_args()
-
- if len(args) < 1:
- parser.print_help()
- exit(1)
-
- if options.output_dir and options.output_file:
- raise ValueError("Either --output-dir or --output file should be specified")
- try:
- for main_input in args:
- path, fname = os.path.realpath(main_input).rsplit('/', 1)
- provider = DirDocProvider(path)
- output_file, output_dir = options.output_file, options.output_dir
- if not (options.output_file or options.output_dir):
- output_file = os.path.splitext(main_input)[0] + '.pdf'
- else:
- output_file = None
-
- doc = WLDocument.from_file(main_input, provider=provider)
- pdf = doc.as_pdf(save_tex=options.save_tex,
- cover=options.with_cover,
- morefloats=options.morefloats)
-
- doc.save_output_file(pdf,
- output_file, options.output_dir, options.make_dir, 'pdf')
- except ParseError, e:
- print '%(file)s:%(name)s:%(message)s; use -v to see more output' % {
- 'file': main_input,
- 'name': e.__class__.__name__,
- 'message': e
- }
+if __name__ == '__main__':
+ Book2Pdf.run()
# 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 optparse
-
-from librarian import ParseError
+from librarian.book2anything import Book2Anything, Option
from librarian.parser import WLDocument
-if __name__ == '__main__':
- # Parse commandline arguments
- usage = """Usage: %prog [options] SOURCE [SOURCE...]
- Convert SOURCE files to TXT format."""
-
- 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('-w', '--wrap', action='store', type='int', dest='wrapping', default=0,
- help='set line wrap column')
- parser.add_option('-i', '--ignore-dublin-core', action='store_false', dest='parse_dublincore', default=True,
- help='don\'t try to parse dublin core metadata')
+class Book2Txt(Book2Anything):
+ format_name = "TXT"
+ ext = "txt"
+ uses_cover = False
+ uses_provider = False
+ parser_args = [
+ Option('-i', '--ignore-dublin-core', dest='parse_dublincore',
+ action='store_false', default=True,
+ help='don\'t try to parse dublin core metadata')
+ ]
+ transform_args = [
+ Option('-w', '--wrap', action='store', type='int', dest='wrapping', default=0,
+ help='set line wrap column')
+ ]
+ transform = WLDocument.as_text
- options, input_filenames = parser.parse_args()
- if len(input_filenames) < 1:
- parser.print_help()
- exit(1)
-
- # Do some real work
- for input_filename in input_filenames:
- if options.verbose:
- print input_filename
-
- output_filename = os.path.splitext(input_filename)[0] + '.txt'
- try:
- doc = WLDocument.from_file(input_filename,
- parse_dublincore=options.parse_dublincore)
- html = doc.as_text(wrapping=str(options.wrapping))
- doc.save_output_file(html, output_path=output_filename)
- except ParseError, e:
- print '%(file)s:%(name)s:%(message)s' % {
- 'file': input_filename,
- 'name': e.__class__.__name__,
- 'message': e
- }
- except IOError, e:
- print '%(file)s:%(name)s:%(message)s' % {
- 'file': input_filename,
- 'name': e.__class__.__name__,
- 'message': e.strerror,
- }
- except BaseException, e:
- print '%(file)s:%(etype)s:%(message)s' % {
- 'file': input_filename,
- 'etype': e.__class__.__name__,
- 'message': e,
- }
- raise
+if __name__ == '__main__':
+ Book2Txt.run()
setup(
name='librarian',
- version='1.4.1',
+ version='1.5.1',
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='radek.czajka@gmail.com',
+ maintainer_email='radoslaw.czajka@nowoczesnapolska.org.pl',
url='http://github.com/fnp/librarian',
packages=['librarian'],
- package_data={'librarian': ['xslt/*.xslt', 'epub/*', 'mobi/*', 'pdf/*', 'fonts/*', 'res/*'] +
+ package_data={'librarian': ['xslt/*.xslt', 'epub/*', 'mobi/*', 'pdf/*', 'fb2/*', 'fonts/*', 'res/*'] +
whole_tree(os.path.join(os.path.dirname(__file__), 'librarian'), 'font-optimizer')},
include_package_data=True,
- install_requires=['lxml>=2.2'],
+ install_requires=[
+ 'lxml>=2.2',
+ 'Pillow',
+ ],
scripts=['scripts/book2html',
'scripts/book2txt',
'scripts/book2epub',
'scripts/book2mobi',
'scripts/book2pdf',
+ 'scripts/book2fb2',
'scripts/book2partner',
'scripts/book2cover',
'scripts/bookfragments',
--- /dev/null
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ Example file for testing a converter.
+ Just run it through and see if everything looks ok.
+-->
+<utwor>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+xmlns:dc="http://purl.org/dc/elements/1.1/">
+<rdf:Description rdf:about="LINK DO PLATFORMY REDAKCYJNEJ">
+<dc:creator xml:lang="pl">Utworu, Autor</dc:creator>
+<dc:title xml:lang="pl">Tytuł w DC</dc:title>
+<dc:contributor.translator xml:lang="pl">Utworu, Tłumacz</dc:contributor.translator>
+<dc:contributor.editor xml:lang="pl">Literacki, Redaktor</dc:contributor.editor>
+<dc:contributor.technical_editor xml:lang="pl">Techniczny, Redaktor</dc:contributor.technical_editor>
+<dc:publisher xml:lang="pl">Fundacja Nowoczesna Polska</dc:publisher>
+<dc:subject.period xml:lang="pl">period</dc:subject.period>
+<dc:subject.type xml:lang="pl">type</dc:subject.type>
+<dc:subject.genre xml:lang="pl">genre</dc:subject.genre>
+<dc:description xml:lang="pl">Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana
+ przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.</dc:description>
+<dc:identifier.url xml:lang="pl">http://wolnelektury.pl/katalog/lektura/test1</dc:identifier.url>
+<dc:source xml:lang="pl">source</dc:source>
+<dc:rights xml:lang="pl">Domena publiczna</dc:rights>
+<dc:date.pd xml:lang="pl">1500</dc:date.pd>
+<dc:format xml:lang="pl">xml</dc:format>
+<dc:type xml:lang="pl">text</dc:type>
+<dc:type xml:lang="en">text</dc:type>
+<dc:date xml:lang="pl">2000</dc:date>
+<dc:language xml:lang="pl">pol</dc:language>
+</rdf:Description>
+</rdf:RDF>
+<liryka_l>
+
+<nota_red><akap>nota_red</akap></nota_red>
+
+<autor_utworu>autor_utworu</autor_utworu>
+<dzielo_nadrzedne>dzielo_nadrzedne</dzielo_nadrzedne>
+<nazwa_utworu>nazwa_utworu</nazwa_utworu>
+<podtytul>podtytul</podtytul>
+
+<akap>[powyżej:
+nota_red (nie pojawia się w tekście, może być podana osobno),
+autor_utworu, dzielo_nadrzedne, nazwa_utworu, podtytul, tłumacz (z DC)]</akap>
+
+<akap>[Noty: nota/akap, dedykacja/akap, motto/akap, motto_podpis]</akap>
+
+
+<nota><akap>nota/akap</akap></nota>
+<dedykacja><akap>dedykacja/akap</akap></dedykacja>
+<motto><akap>motto/akap</akap></motto>
+<motto_podpis>motto_podpis</motto_podpis>
+
+<akap>[Początek dramatu: lista_osob, naglowek_listy, lista_osoba, miejsce_czas]</akap>
+
+<lista_osob>
+ <naglowek_listy>lista_osob/naglowek_listy</naglowek_listy>
+ <lista_osoba>lista_osob/lista_osoba</lista_osoba>
+ <lista_osoba>lista_osob/lista_osoba</lista_osoba>
+</lista_osob>
+<miejsce_czas>miejsce_czas</miejsce_czas>
+
+<akap>[naglowek_czesc, naglowek_rozdzial, naglowek_podrozdzial, srodtytul]</akap>
+
+<naglowek_czesc>naglowek_czesc</naglowek_czesc>
+<naglowek_rozdzial>naglowek_rozdzial</naglowek_rozdzial>
+<naglowek_podrozdzial>naglowek_podrozdzial</naglowek_podrozdzial>
+<srodtytul>srodtytul</srodtytul>
+
+<akap>[akap, akap_cd, akap_dialog, motyw]</akap>
+
+<akap>akap<begin id="a"/><motyw>motyw</motyw></akap>
+<akap_cd>akap_cd</akap_cd>
+<akap_dialog>akap_dialog<end id="a"/></akap_dialog>
+
+<akap>[strofa, wers_akap, wers_wciety,typ=1-6, wers_cd, zastepnik_wersu]</akap>
+
+<strofa>strofa/
+<wers_akap>wers_akap</wers_akap>/
+<wers_wciety typ="1">wers_wciety@typ=1</wers_wciety>/
+<wers_wciety typ="2">wers_wciety@typ=2</wers_wciety>/
+<wers_wciety typ="3">wers_wciety@typ=3</wers_wciety>
+</strofa><strofa>
+<wers_wciety typ="4">wers_wciety@typ=4</wers_wciety>/
+<wers_wciety typ="5">wers_wciety@typ=5</wers_wciety>/
+<wers_wciety typ="6">wers_wciety@typ=6</wers_wciety>/
+<wers_cd>wers_cd</wers_cd>/
+<zastepnik_wersu />. . . . . . . . . . . . . . . .
+</strofa>
+
+<akap>[dlugi_cytat/akap]</akap>
+
+<dlugi_cytat><akap>Cytowany akapit powinien wyglądać jak cytowany akapit.
+Znaczy, może mieć jakieś dodatkowe wcięcie, jakiś rodzaj wyróżnienia czy coś.</akap></dlugi_cytat>
+
+<akap>[poezja_cyt/strofa]</akap>
+
+<poezja_cyt><strofa>To jest poezja/
+cytowana/
+ma być porządnie/
+wyrównana</strofa></poezja_cyt>
+
+<akap>[naglowek_akt, naglowek_scena]</akap>
+
+<naglowek_akt>naglowek_akt</naglowek_akt>
+<naglowek_scena>naglowek_scena</naglowek_scena>
+
+<akap>[Kwestia: naglowek_osoba, kwestia, didask_tekst, didaskalia, strofa, akap]</akap>
+
+<naglowek_osoba>naglowek_osoba</naglowek_osoba>
+
+<kwestia>
+<didask_tekst>didask_tekst</didask_tekst>
+<didaskalia>didaskalia</didaskalia>
+<strofa>Strofa w dramacie/
+jak amen w pacie/
+rzu.</strofa>
+<akap>Powyższy kawałek wiersza jest najzupełniej bez sensu i tak naprawdę wcale nie trzyma rytmu ani rymu. Być może należy skoncentrować się na dramacie prozą, jak ta tutaj niniejsza wypowiedź. </akap></kwestia>
+
+<akap>[didaskalia, osoba]</akap>
+
+<didaskalia>odezwał się <osoba>autor</osoba>.</didaskalia>
+
+<akap>[Wyróżnienia: tytul_dziela, tytul_dziela@typ=1, wyroznienie, slowo_obce]</akap>
+
+<akap>
+<tytul_dziela>tytul_dziela</tytul_dziela>,
+<tytul_dziela typ="1">tytul_dziela@typ=1</tytul_dziela>,
+<wyroznienie>wyroznienie</wyroznienie>,
+<slowo_obce>slowo_obce</slowo_obce>
+</akap>
+
+<akap>[Przypisy: pa, pt, pr, pe]</akap>
+
+<akap>
+<pa>pa - - - przypis <wyroznienie>autorski</wyroznienie></pa>
+<pt>pt - - - przypis tłumacza</pt>
+<pr>pr - - - przypis redakcyjny</pr>
+<pe>pe - - - przypis edytorski</pe>
+</akap>
+
+<akap>[Separatory]</akap>
+
+<akap>[sekcja_swiatlo:]</akap>
+
+<sekcja_swiatlo></sekcja_swiatlo>
+
+<akap>[sekcja_asterysk:]</akap>
+
+<sekcja_asterysk></sekcja_asterysk>
+
+<akap>[separator_linia:]</akap>
+
+<separator_linia></separator_linia>
+
+
+
+<akap>[Komentarze: uwaga, extra]</akap>
+<uwaga>uwaga</uwaga>
+<extra>extra</extra>
+
+<akap>[Nieużywane]</akap>
+
+<wyp_osoba>wyp_osoba</wyp_osoba>
+<wywiad_pyt><akap>wywiad_pyt/akap</akap></wywiad_pyt>
+<wywiad_odp><akap>wywiad_odp/akap</akap></wywiad_odp>
+<mat>mat</mat>
+<www>www</www>
+
+</liryka_l>
+</utwor>
<dc:rights xml:lang="pl">Domena publiczna - Paul Klee zm. 1940</dc:rights>
<dc:date.pd xml:lang="pl">1940</dc:date.pd>
<dc:type>Image</dc:type>
- <dc:format xml:lang="pl">image/png</dc:format>
- <dc:format.dimensions xml:lang="pl">1645 x 2000 px</dc:format.dimensions>
- <dc:format.checksum.sha1 xml:lang="pl">d9ead48f3442ac4e1add602aacdffa4638ae8e21</dc:format.checksum.sha1>
+ <dc:format xml:lang="pl">image/jpeg</dc:format>
+ <dc:format.dimensions xml:lang="pl">329 x 400 px</dc:format.dimensions>
+ <dc:format.checksum.sha1 xml:lang="pl">5ed8e8d24d92017c6341c0b8cfcc414dec55b8bf</dc:format.checksum.sha1>
<dc:date xml:lang="pl">1920</dc:date>
<dc:language xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">lat</dc:language>
</rdf:Description>
<div type="whole"/>
</sem>
<sem type="theme" theme="anioł historii">
- <div type="area" x1="462" y1="212" x2="1283" y2="1730"/>
+ <div type="area" x1="92" y1="42" x2="257" y2="346"/>
</sem>
<sem type="theme" theme="spojrzenie">
- <div type="area" x1="688" y1="500" x2="1054" y2="618"/>
+ <div type="area" x1="138" y1="100" x2="211" y2="124"/>
</sem>
<sem type="object" object="skrzydło">
- <div type="area" x1="468" y1="741" x2="694" y2="1027"/>
- <div type="area" x1="1044" y1="762" x2="1260" y2="1041"/>
+ <div type="area" x1="94" y1="148" x2="139" y2="205"/>
+ <div type="area" x1="209" y1="152" x2="252" y2="1041"/>
</sem>
</picture>
<h2>Spis treści</h2>
<ol></ol>
</div>
+<div id="themes"><ol>
+<li>Miłość platoniczna: <a href="#m1189062500041">1</a> <a href="#m1189062500041">2</a> </li>
+<li>Natura: <a href="#m1189062528872">1</a> </li>
+</ol></div>
<h1>
<span class="author">Adam Asnyk</span><span class="title"><a name="m1189062500041" class="theme-begin" fid="1189062500041">Miłość platoniczna</a>Między nami nic nie było</span>
</h1>
<a name="m1189062500041" class="theme-begin" fid="1189062500041">Miłość platoniczna</a><div class="stanza">
-<p class="verse"><a name="f1" class="target"> </a><a href="#f1" class="anchor">1</a>Między nami nic nie było!</p>
+<a name="sec4"></a><p class="verse"><a name="f1" class="target"> </a><a href="#f1" class="anchor">1</a>Między nami nic nie było!</p>
<p class="verse">Żadnych zwierzeń, wyznań żadnych!</p>
<p class="verse">Nic nas z sobą nie łączyło —</p>
<p class="verse">Prócz wiosennych marzeń zdradnych;</p>
</div>
<div class="stanza">
-<p class="verse"><a name="f5" class="target"> </a><a href="#f5" class="anchor">5</a><a name="m1189062528872" class="theme-begin" fid="1189062528872">Natura</a>Prócz tych woni, barw i blasków,</p>
+<a name="sec5"></a><p class="verse"><a name="f5" class="target"> </a><a href="#f5" class="anchor">5</a><a name="m1189062528872" class="theme-begin" fid="1189062528872">Natura</a>Prócz tych woni, barw i blasków,</p>
<p class="verse">Unoszących się w przestrzeni;</p>
<p class="verse">Prócz szumiących śpiewem lasków</p>
<p class="verse">I tej świeżej łąk zieleni;</p>
</div>
<div class="stanza">
-<p class="verse">Prócz tych kaskad i potoków,</p>
+<a name="sec6"></a><p class="verse">Prócz tych kaskad i potoków,</p>
<p class="verse"><a name="f10" class="target"> </a><a href="#f10" class="anchor">10</a>Zraszających każdy parów,</p>
<p class="verse">Prócz girlandy tęcz, obłoków,</p>
<p class="verse">Prócz natury słodkich czarów;</p>
</div>
<div class="stanza">
-<p class="verse">Prócz tych wspólnych, jasnych zdrojów,</p>
+<a name="sec7"></a><p class="verse">Prócz tych wspólnych, jasnych zdrojów,</p>
<p class="verse">Z których serce zachwyt piło;</p>
<p class="verse"><a name="f15" class="target"> </a><a href="#f15" class="anchor">15</a>Prócz pierwiosnków i powojów,—</p>
<p class="verse">Między nami nic nie było!<span class="theme-end" fid="1189062528872"></span><span class="theme-end" fid="1189062500041"></span></p>
Utwór opracowany został w ramach projektu Wolne Lektury przez fundację Nowoczesna Polska.
-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/).
+Ten utwór nie jest objęty majątkowym prawem autorskim i znajduje się w domenie publicznej, co oznacza że możesz go swobodnie wykorzystywać, publikować i rozpowszechniać. Jeśli utwór opatrzony jest dodatkowymi materiałami (przypisy, motywy literackie etc.), które podlegają prawu autorskiemu, to te dodatkowe materiały udostępnione są na licencji Creative Commons Uznanie Autorstwa – Na Tych Samych Warunkach 3.0 PL (http://creativecommons.org/licenses/by-sa/3.0/).
Tekst opracowany na podstawie: (Asnyk, Adam) El...y (1838-1897), Poezye, t. 3, Gebethner i Wolff, wyd. nowe poprzedzone słowem wstępnym St. Krzemińskiego, Warszawa, 1898
Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.
-Opracowanie redakcyjne i przypisy: Aleksandra Sekuła, Olga Sutkowska
+Opracowanie redakcyjne i przypisy: Adam Fikcyjny, Aleksandra Sekuła, Olga Sutkowska
<dc:subject.period xml:lang="pl">Pozytywizm</dc:subject.period>
<dc:subject.type xml:lang="pl">Liryka</dc:subject.type>
<dc:subject.genre xml:lang="pl">Wiersz</dc:subject.genre>
+<dc:contributor.technical_editor xml:lang="pl">Fikcyjny, Adam</dc:contributor.technical_editor>
<dc:description xml:lang="pl">Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.</dc:description>
<dc:identifier.url xml:lang="pl">http://wolnelektury.pl/katalog/lektura/poezye</dc:identifier.url>
<dc:relation.hasPart xml:lang="pl">http://wolnelektury.pl/katalog/lektura/miedzy-nami-nic-nie-bylo</dc:relation.hasPart>
+<dc:relation.hasPart xml:lang="pl">http://wolnelektury.pl/katalog/lektura/do-mlodych</dc:relation.hasPart>
<dc:source.URL xml:lang="pl">http://www.polona.pl/Content/5164</dc:source.URL>
<dc:source xml:lang="pl">(Asnyk, Adam) El...y (1838-1897), Poezye, t. 3, Gebethner i Wolff, wyd. nowe poprzedzone słowem wstępnym St. Krzemińskiego, Warszawa, 1898</dc:source>
<dc:rights xml:lang="pl">Domena publiczna - Adam Asnyk zm. 1897</dc:rights>
--- /dev/null
+<utwor><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description rdf:about="http://redakcja.wolnelektury.pl/documents/book/do-mlodych/">
+<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Asnyk, Adam</dc:creator>
+<dc:title xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Do młodych</dc:title>
+<dc:contributor.editor xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Sekuła, Aleksandra</dc:contributor.editor>
+<dc:contributor.technical_editor xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Sutkowska, Olga</dc:contributor.technical_editor>
+<dc:publisher xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Fundacja Nowoczesna Polska</dc:publisher>
+<dc:subject.period xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Pozytywizm</dc:subject.period>
+<dc:subject.type xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Liryka</dc:subject.type>
+<dc:subject.genre xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Wiersz</dc:subject.genre>
+<dc:description xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.</dc:description>
+<dc:identifier.url xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">http://wolnelektury.pl/katalog/lektura/do-mlodych</dc:identifier.url>
+<dc:source.URL xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">http://www.polona.pl/Content/8616</dc:source.URL>
+<dc:source xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">El...y (Adam Asnyk), Poezye, t. 3, Gebethner i Wolff, wyd. nowe poprzedzone słowem wstępnym St. Krzemińskiego, Warszawa 1898</dc:source>
+<dc:rights xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">Domena publiczna - Adam Asnyk zm. 1897</dc:rights>
+<dc:date.pd xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">1897</dc:date.pd>
+<dc:format xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">xml</dc:format>
+<dc:type xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">text</dc:type>
+<dc:type xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="en">text</dc:type>
+<dc:date xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">2009-04-07</dc:date>
+<dc:audience xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">L</dc:audience>
+<dc:language xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="pl">pol</dc:language>
+<dc:relation.coverImage.url xmlns:dc="http://purl.org/dc/elements/1.1/">http://redakcja.wolnelektury.pl/media/dynamic/cover/image/35.jpg</dc:relation.coverImage.url>
+<dc:relation.coverImage.attribution xmlns:dc="http://purl.org/dc/elements/1.1/">leboski@Flickr, CC BY 2.0</dc:relation.coverImage.attribution>
+<dc:relation.coverImage.source xmlns:dc="http://purl.org/dc/elements/1.1/">http://redakcja.wolnelektury.pl/cover/image/35</dc:relation.coverImage.source>
+</rdf:Description>
+</rdf:RDF><liryka_l>
+
+<autor_utworu>Adam Asnyk</autor_utworu>
+
+<nazwa_utworu>Do młodych</nazwa_utworu>
+
+
+
+
+<strofa>Szukajcie prawdy jasnego płomienia,/
+Szukajcie nowych, nieodkrytych dróg!/
+Za każdym krokiem w tajniki stworzenia/
+Coraz się dusza ludzka rozprzestrzenia/
+I większym staje się Bóg!</strofa>
+
+
+<strofa>Choć otrząśniecie kwiaty barwnych mitów,/
+Choć rozproszycie legendowy mrok,/
+Choć mgłę urojeń zedrzecie z błękitów, ---/
+Ludziom niebiańskich nie zbraknie zachwytów,/
+Lecz dalej sięgnie ich wzrok.</strofa>
+
+
+<strofa><begin id="b1238764684390"/><motyw id="m1238764684390">Czas, Kondycja ludzka, Przemijanie</motyw>Każda epoka ma swe własne cele/
+I zapomina o wczorajszych snach:/
+Nieście więc wiedzy pochodnię na czele/
+I nowy udział bierzcie w wieków dziele,---/
+Przyszłości podnoście gmach!</strofa>
+
+
+<strofa>Ale nie depczcie przeszłości ołtarzy,/
+Choć macie sami doskonalsze wznieść:/
+Na nich się jeszcze święty ogień żarzy,/
+I miłość ludzka stoi tam na straży,/
+I wy winniście im cześć!</strofa>
+
+
+<strofa>Ze światem, który w ciemność już zachodzi/
+Wraz z całą tęczą idealnych snów,/
+Prawdziwa mądrość niechaj was pogodzi:/
+I wasze gwiazdy, o zdobywcy młodzi,/
+W ciemnościach pogasną znów!<end id="e1238764684390"/></strofa>
+
+</liryka_l></utwor>
\ No newline at end of file
<dc:contributor.editor xml:lang="pl" />
<dc:contributor.editor xml:lang="pl">Sekuła, Aleksandra</dc:contributor.editor>
<dc:contributor.technical_editor xml:lang="pl">Sutkowska, Olga</dc:contributor.technical_editor>
+<dc:contributor.editor xml:lang="pl">Fikcyjny, Adam</dc:contributor.editor>
+<dc:contributor.technical_editor xml:lang="pl">Fikcyjny, Adam</dc:contributor.technical_editor>
<dc:publisher xml:lang="pl">Fundacja Nowoczesna Polska</dc:publisher>
<dc:subject.period xml:lang="pl">Pozytywizm</dc:subject.period>
<dc:subject.type xml:lang="pl">Liryka</dc:subject.type>
# 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 nose.tools import *
-from utils import get_fixture
+from tests.utils import get_fixture
def test_transform():
- WLDocument.from_file(
+ epub = WLDocument.from_file(
get_fixture('text', 'asnyk_zbior.xml'),
provider=DirDocProvider(get_fixture('text', ''))
- ).as_epub(flags=['without_fonts'])
+ ).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)
--- /dev/null
+# -*- 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")
# from nose.tools import set_trace; set_trace()
assert pi.type[0] == u"Image"
- assert pi.mime_type == u'image/png' == wlp.mime_type
+ assert pi.mime_type == u'image/jpeg' == wlp.mime_type
assert wlp.slug == 'angelus-novus'
assert path.exists(wlp.image_path)