return os.path.join(os.path.dirname(__file__), path)
-class OutputFile(object):
- """Represents a file returned by one of the converters."""
-
+class IOFile(object):
+ """ Represents a file fed as input or returned as a result. """
_string = None
_filename = None
+ _filename_tmp = False
+
+ def __init__(self, attachments=None):
+ self.attachments = attachments or {}
def __del__(self):
- if self._filename:
+ if self._filename_tmp:
os.unlink(self._filename)
def __nonzero__(self):
return self._string is not None or self._filename is not None
@classmethod
- def from_string(cls, string):
+ def from_string(cls, string, *args, **kwargs):
"""Converter returns contents of a file as a string."""
- instance = cls()
+ instance = cls(*args, **kwargs)
instance._string = string
return instance
@classmethod
- def from_filename(cls, filename):
+ def from_filename(cls, filename, *args, **kwargs):
"""Converter returns contents of a file as a named file."""
- instance = cls()
+ instance = cls(*args, **kwargs)
instance._filename = filename
return instance
temp.write(self._string)
temp.close()
self._filename = temp.name
+ self._filename_tmp = True
return self._filename
else:
return None
os.makedirs(dirname)
shutil.copy(self.get_filename(), path)
+ def dump_to(self, path, directory=None):
+ """ Path should be name for main file. """
+ self.save_as(path)
+ dirname = os.path.dirname(os.path.abspath(path))
+ for filename, attachment in self.attachments.items():
+ attachment.save_as(os.path.join(dirname, filename))
+
+
+class Format(object):
+ """ Generic format class. """
+ def __init__(self, wldoc, **kwargs):
+ self.wldoc = wldoc
+ self.customization = kwargs
+
+ def build(self):
+ raise NotImplementedError
+
class URLOpener(urllib.FancyURLopener):
version = 'FNP Librarian (http://github.com/fnp/librarian)'
import re
import Image, ImageFont, ImageDraw, ImageFilter
from StringIO import StringIO
-from librarian import get_resource, OutputFile, URLOpener
+from librarian import get_resource, IOFile
class TextBox(object):
def output_file(self, *args, **kwargs):
imgstr = StringIO()
self.save(imgstr, *args, **kwargs)
- return OutputFile.from_string(imgstr.getvalue())
+ return IOFile.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_lineskip = 30
- title_font = ImageFont.truetype(
- get_resource('fonts/DejaVuSerif-Bold.ttf'), 30)
- title_lineskip = 40
- title_box_width = 350
- bar_width = 35
- background_color = '#444'
- author_color = '#444'
- default_background = get_resource('res/cover.png')
- format = 'JPEG'
-
- epoch_colors = {
- u'Starożytność': '#9e3610',
- u'Średniowiecze': '#564c09',
- u'Renesans': '#8ca629',
- u'Barok': '#a6820a',
- u'Oświecenie': '#f2802e',
- u'Romantyzm': '#db4b16',
- u'Pozytywizm': '#961060',
- u'Modernizm': '#7784e0',
- u'Dwudziestolecie międzywojenne': '#3044cf',
- u'Współczesność': '#06393d',
- }
-
- def __init__(self, book_info, format=None, image_cache=None):
- super(WLCover, self).__init__(book_info, format=format)
- self.kind = book_info.kind
- self.epoch = book_info.epoch
- if book_info.cover_url:
- url = book_info.cover_url
- bg_src = None
- if image_cache:
- from urllib import quote
- try:
- bg_src = URLOpener().open(image_cache + quote(url, safe=""))
- except:
- pass
- if bg_src is None:
- bg_src = URLOpener().open(url)
- self.background_img = StringIO(bg_src.read())
- bg_src.close()
- else:
- self.background_img = self.default_background
-
- def pretty_author(self):
- return self.author.upper()
-
- def image(self):
- img = Image.new('RGB', (self.width, self.height), self.background_color)
- draw = ImageDraw.Draw(img)
-
- if self.epoch in self.epoch_colors:
- epoch_color = self.epoch_colors[self.epoch]
- else:
- epoch_color = '#000'
- draw.rectangle((0, 0, self.bar_width, self.height), fill=epoch_color)
-
- if self.background_img:
- src = Image.open(self.background_img)
- trg_size = (self.width - self.bar_width, self.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.crop((0, cut, src.size[0], src.size[1] - cut))
- else:
- resized = (
- src.size[0] * trg_size[1] / src.size[1],
- trg_size[1],
- )
- cut = (resized[0] - trg_size[0]) / 2
- src = src.resize(resized)
- src = src.crop((cut, 0, src.size[0] - cut, src.size[1]))
-
- img.paste(src, (self.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,
- 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.text(self.pretty_title(),
- line_height=self.title_lineskip,
- font=self.title_font,
- color=epoch_color,
- shadow_color=self.title_shadow,
- )
- box_img = box.image()
-
- if self.kind == 'Liryka':
- # top
- box_top = 100
- elif self.kind == 'Epika':
- # bottom
- box_top = self.height - 100 - box_img.size[1]
- else:
- # center
- box_top = (self.height - box_img.size[1]) / 2
-
- box_left = self.bar_width + (self.width - self.bar_width -
- box_img.size[0]) / 2
- draw.rectangle((box_left, box_top,
- box_left + box_img.size[0], box_top + box_img.size[1]),
- fill='#fff')
- img.paste(box_img, (box_left, box_top), box_img)
-
- return img
-
-
-
-class VirtualoCover(Cover):
- width = 600
- height = 730
- author_top = 73
- title_top = 73
- logo_bottom = 25
- logo_width = 250
-
-
-class PrestigioCover(Cover):
- width = 580
- height = 783
- background_img = get_resource('res/cover-prestigio.png')
-
- author_top = 446
- author_margin_left = 118
- author_margin_right = 62
- author_lineskip = 60
- author_color = '#fff'
- author_shadow = '#000'
- author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Italic.ttf'), 50)
-
- title_top = 0
- title_margin_left = 118
- title_margin_right = 62
- title_lineskip = 60
- title_color = '#fff'
- title_shadow = '#000'
- title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Italic.ttf'), 50)
-
- def pretty_title(self):
- return u"„%s”" % self.title
-
-
-class BookotekaCover(Cover):
- width = 2140
- height = 2733
- background_img = get_resource('res/cover-bookoteka.png')
-
- author_top = 480
- author_margin_left = 307
- author_margin_right = 233
- author_lineskip = 156
- author_color = '#d9d919'
- author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 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)
-
- format = 'PNG'
-
-
-class GandalfCover(Cover):
- width = 600
- height = 730
- background_img = get_resource('res/cover-gandalf.png')
- author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 30)
- title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 40)
- logo_bottom = 25
- logo_width = 250
- format = 'PNG'
+ def for_pdf(self):
+ return IOFile.from_filename(get_resource('pdf/cover_image.sty'), {
+ 'cover.png': self.output_file(),
+ })
from tempfile import mkdtemp, NamedTemporaryFile
from shutil import rmtree
-from librarian import RDFNS, WLNS, NCXNS, OPFNS, XHTMLNS, OutputFile
+from librarian import RDFNS, WLNS, NCXNS, OPFNS, XHTMLNS, IOFile
from librarian.cover import WLCover
from librarian import functions, get_resource
zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True))
zip.close()
- return OutputFile.from_filename(output_file.name)
+ return IOFile.from_filename(output_file.name)
from copy import deepcopy
from lxml import etree
-from librarian import functions, OutputFile
+from librarian import functions, IOFile
from .epub import replace_by_verse
result = document.transform(style)
- return OutputFile.from_string(unicode(result).encode('utf-8'))
+ return IOFile.from_string(unicode(result).encode('utf-8'))
# vim:et
_register_function(texcommand)
+def reg_get(format_):
+ def get(context, *args):
+ obj = format_
+ for arg in args:
+ if hasattr(obj, arg):
+ obj = getattr(obj, arg)
+ else:
+ try:
+ obj = obj[arg]
+ except (TypeError, KeyError), e:
+ # Just raise proper AttributeError.
+ getattr(obj, arg)
+ return obj
+ _register_function(get)
import copy
from lxml import etree
-from librarian import XHTMLNS, ParseError, OutputFile
+from librarian import XHTMLNS, ParseError, IOFile
from librarian import functions
from lxml.etree import XMLSyntaxError, XSLTApplyError
add_anchors(result.getroot())
add_table_of_contents(result.getroot())
- return OutputFile.from_string(etree.tostring(result, method='html',
+ return IOFile.from_string(etree.tostring(result, method='html',
xml_declaration=False, pretty_print=True, encoding='utf-8'))
else:
return None
import subprocess
from tempfile import NamedTemporaryFile
-from librarian import OutputFile
+from librarian import IOFile
from librarian.cover import WLCover
from librarian import get_resource
subprocess.check_call(['ebook-convert', epub.get_filename(), output_file.name,
'--no-inline-toc', '--cover=%s' % cover_file.name], **kwargs)
os.unlink(cover_file.name)
- return OutputFile.from_filename(output_file.name)
\ No newline at end of file
+ return IOFile.from_filename(output_file.name)
\ No newline at end of file
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from librarian import ValidationError, NoDublinCore, ParseError, NoProvider
-from librarian import RDFNS
-from librarian.cover import WLCover
+from librarian import RDFNS, IOFile
+from librarian.styles.wolnelektury.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,
- strict=False, meta_fallbacks=None):
- self.edoc = edoc
- self.provider = provider
-
- root_elem = edoc.getroot()
-
- dc_path = './/' + RDFNS('RDF')
+ _edoc = None
+ @property
+ def edoc(self):
+ if self._edoc is None:
+ data = self.source.get_string()
+ if not isinstance(data, unicode):
+ data = data.decode('utf-8')
+ data = data.replace(u'\ufeff', '')
+ try:
+ parser = etree.XMLParser(remove_blank_text=False)
+ self._edoc = etree.parse(StringIO(data.encode('utf-8')), parser)
+ except (ExpatError, XMLSyntaxError, XSLTApplyError), e:
+ raise ParseError(e)
+ return self._edoc
+
+ _rdf_elem = None
+ @property
+ def rdf_elem(self):
+ if self._rdf_elem is None:
+ dc_path = './/' + RDFNS('RDF')
+ self._rdf_elem = self.edoc.getroot().find(dc_path)
+ if self._rdf_elem is None:
+ raise NoDublinCore('Document has no DublinCore - which is required.')
+ return self._rdf_elem
- if root_elem.tag != 'utwor':
+ _book_info = None
+ @property
+ def book_info(self):
+ if not self.parse_dublincore:
+ return None
+ if self._book_info is None:
+ self._book_info = dcparser.BookInfo.from_element(
+ self.rdf_elem, fallbacks=self.meta_fallbacks, strict=self.strict)
+ return self._book_info
+
+ def __init__(self, iofile, provider=None,
+ parse_dublincore=True, # shouldn't it be in a subclass?
+ strict=False, # ?
+ meta_fallbacks=None # ?
+ ):
+ self.source = iofile
+ self.provider = provider
+ self.parse_dublincore = parse_dublincore
+ self.strict = strict
+ self.meta_fallbacks = meta_fallbacks
+ if self.edoc.getroot().tag != 'utwor':
raise ValidationError("Invalid root element. Found '%s', should be 'utwor'" % root_elem.tag)
-
if parse_dublincore:
- self.rdf_elem = root_elem.find(dc_path)
-
- if self.rdf_elem is None:
- raise NoDublinCore('Document has no DublinCore - which is required.')
-
- self.book_info = dcparser.BookInfo.from_element(
- self.rdf_elem, fallbacks=meta_fallbacks, strict=strict)
- else:
- self.book_info = None
+ self.book_info
@classmethod
def from_string(cls, xml, *args, **kwargs):
- return cls.from_file(StringIO(xml), *args, **kwargs)
+ return cls(IOFile.from_string(xml), *args, **kwargs)
@classmethod
def from_file(cls, xmlfile, *args, **kwargs):
-
- # first, prepare for parsing
if isinstance(xmlfile, basestring):
- file = open(xmlfile, 'rb')
- try:
- data = file.read()
- finally:
- file.close()
+ iofile = IOFile.from_filename(xmlfile)
else:
- data = xmlfile.read()
-
- if not isinstance(data, unicode):
- data = data.decode('utf-8')
+ iofile = IOFile.from_file(xmlfile)
+ return cls(iofile, *args, **kwargs)
- data = data.replace(u'\ufeff', '')
-
- try:
- parser = etree.XMLParser(remove_blank_text=False)
- tree = etree.parse(StringIO(data.encode('utf-8')), parser)
-
- return cls(tree, *args, **kwargs)
- except (ExpatError, XMLSyntaxError, XSLTApplyError), e:
- raise ParseError(e)
def swap_endlines(self):
"""Converts line breaks in stanzas into <br/> tags."""
elem.text = chunks.pop(0)
def parts(self):
- if self.provider is None:
- raise NoProvider('No document provider supplied.')
if self.book_info is None:
raise NoDublinCore('No Dublin Core in document.')
+ if self.book_info.parts and self.provider is None:
+ raise NoProvider('No document provider supplied.')
for part_uri in self.book_info.parts:
yield self.from_file(self.provider.by_uri(part_uri),
provider=self.provider)
from librarian.dcparser import Person
from librarian.parser import WLDocument
-from librarian import ParseError, DCNS, get_resource, OutputFile
+from librarian import ParseError, DCNS, get_resource, IOFile, Format
from librarian import functions
-from librarian.cover import WLCover
functions.reg_substitute_entities()
'wl2tex': 'pdf/wl2tex.xslt',
}
-#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
insert_tags(doc,
re.compile("(?<=[^-\s])-(?=[^-\s])"),
"dywiz",
- exclude=[DCNS("identifier.url"), DCNS("rights.license")]
+ exclude=[DCNS("identifier.url"), DCNS("rights.license"), 'www']
)
return p == 0
-def transform(wldoc, verbose=False, save_tex=None, morefloats=None,
- cover=None, flags=None, customizations=None):
- """ produces a PDF file with XeLaTeX
+def load_including_children(wldoc=None, provider=None, uri=None):
+ """ Makes one big xml file with children inserted at end.
+
+ Either wldoc or provider and URI must be provided.
+ """
+
+ if uri and provider:
+ f = provider.by_uri(uri)
+ text = f.read().decode('utf-8')
+ f.close()
+ elif wldoc is not None:
+ text = etree.tostring(wldoc.edoc, encoding=unicode)
+ provider = wldoc.provider
+ else:
+ raise ValueError('Neither a WLDocument, nor provider and URI were provided.')
+
+ text = re.sub(ur"([\u0400-\u04ff]+)", ur"<alien>\1</alien>", text)
+
+ document = WLDocument.from_string(text,
+ parse_dublincore=True, provider=provider)
+ document.swap_endlines()
+
+ for child_uri in document.book_info.parts:
+ child = load_including_children(provider=provider, uri=child_uri)
+ document.edoc.getroot().append(child.edoc.getroot())
+ return document
+
+
+class PDFFormat(Format):
+ """ Base PDF format.
+
+ Available customization:
+ nofootnotes: Doesn't do footnotes.
+ nothemes: Doesn't do themes.
+ defaultleading: Default leading.
+ onehalfleading: Bigger leading.
+ doubleleading: Big leading.
+ nowlfont: Uses standard TeX font instead of JUnicodeWL.
- wldoc: a WLDocument
- verbose: prints all output from LaTeX
- save_tex: path to save the intermediary LaTeX file to
- morefloats (old/new/none): force specific morefloats
- cover: a cover.Cover factory or True for default
- flags: less-advertising,
- customizations: user requested customizations regarding various formatting parameters (passed to wl LaTeX class)
"""
- # Parse XSLT
- try:
- book_info = wldoc.book_info
- document = load_including_children(wldoc)
- root = document.edoc.getroot()
+ cover_class = None
+ tex_passes = 1
+ style = get_resource('pdf/default.sty')
+ cover = None
+
+ @property
+ def has_cover(self):
+ """ For use in XSLT. """
+ return self.cover is not None
- if cover:
- if cover is True:
- cover = WLCover
- bound_cover = cover(book_info)
- root.set('data-cover-width', str(bound_cover.width))
- root.set('data-cover-height', str(bound_cover.height))
- if bound_cover.uses_dc_cover:
- if book_info.cover_by:
- root.set('data-cover-by', book_info.cover_by)
- if book_info.cover_source:
- root.set('data-cover-source',
- book_info.cover_source)
- if flags:
- for flag in flags:
- root.set('flag-' + flag, 'yes')
-
- # check for LaTeX packages
- if morefloats:
- root.set('morefloats', morefloats.lower())
- elif package_available('morefloats', 'maxfloats=19'):
- root.set('morefloats', 'new')
-
- # add customizations
- if customizations is not None:
- root.set('customizations', u','.join(customizations))
-
- # add editors info
+ @property
+ def customization_str(self):
+ """ For use in XSLT. """
+ return u','.join(k for k, v in self.customization.items() if v)
+
+ def get_document(self):
+ document = load_including_children(self.wldoc)
+ root = document.edoc.getroot()
root.set('editors', u', '.join(sorted(
editor.readable() for editor in document.editors())))
parse_creator(document.edoc)
substitute_hyphens(document.edoc)
fix_hanging(document.edoc)
+ return document
- # wl -> TeXML
+ def get_texml(self):
style_filename = get_stylesheet("wl2tex")
- style = etree.parse(style_filename)
-
- texml = document.transform(style)
-
- # TeXML -> LaTeX
+ functions.reg_get(self)
+ try:
+ style = etree.parse(style_filename)
+ texml = self.get_document().transform(style)
+ return texml
+ except (XMLSyntaxError, XSLTApplyError), e:
+ raise ParseError(e)
+
+ def get_tex_dir(self):
+ texml = self.get_texml()
temp = mkdtemp('-wl2pdf')
-
- if cover:
- with open(os.path.join(temp, 'cover.png'), 'w') as f:
- bound_cover.save(f)
-
- del document # no longer needed large object :)
-
+ # Save TeX file
tex_path = os.path.join(temp, 'doc.tex')
- fout = open(tex_path, 'w')
- process(StringIO(texml), fout, 'utf-8')
- fout.close()
- del texml
-
- if save_tex:
- shutil.copy(tex_path, save_tex)
-
- # LaTeX -> PDF
+ with open(tex_path, 'w') as fout:
+ process(StringIO(texml), fout, 'utf-8')
+ if self.save_tex:
+ shutil.copy(tex_path, self.save_tex)
+ # Copy style
shutil.copy(get_resource('pdf/wl.cls'), temp)
- shutil.copy(get_resource('res/wl-logo.png'), temp)
-
+ shutil.copy(self.style, os.path.join(temp, 'style.sty'))
+ # Save attachments
+ if self.cover:
+ self.cover.for_pdf().dump_to(os.path.join(temp, 'makecover.sty'))
+ return temp
+
+ def get_pdf(self):
+ temp = self.get_tex_dir()
+ tex_path = os.path.join(temp, 'doc.tex')
try:
cwd = os.getcwd()
except OSError:
cwd = None
os.chdir(temp)
- if verbose:
- p = call(['xelatex', tex_path])
+ if self.verbose:
+ for i in range(self.tex_passes):
+ p = call(['xelatex', tex_path])
else:
- p = call(['xelatex', '-interaction=batchmode', tex_path], stdout=PIPE, stderr=PIPE)
+ for i in range(self.tex_passes):
+ p = call(['xelatex', '-interaction=batchmode', tex_path],
+ stdout=PIPE, stderr=PIPE)
if p:
raise ParseError("Error parsing .tex file")
pdf_path = os.path.join(temp, 'doc.pdf')
shutil.move(pdf_path, output_file.name)
shutil.rmtree(temp)
- return OutputFile.from_filename(output_file.name)
-
- except (XMLSyntaxError, XSLTApplyError), e:
- raise ParseError(e)
-
-
-def load_including_children(wldoc=None, provider=None, uri=None):
- """ Makes one big xml file with children inserted at end.
-
- Either wldoc or provider and URI must be provided.
- """
-
- if uri and provider:
- f = provider.by_uri(uri)
- text = f.read().decode('utf-8')
- f.close()
- elif wldoc is not None:
- text = etree.tostring(wldoc.edoc, encoding=unicode)
- provider = wldoc.provider
- else:
- raise ValueError('Neither a WLDocument, nor provider and URI were provided.')
-
- text = re.sub(ur"([\u0400-\u04ff]+)", ur"<alien>\1</alien>", text)
-
- document = WLDocument.from_string(text,
- parse_dublincore=True, provider=provider)
- document.swap_endlines()
-
- for child_uri in document.book_info.parts:
- child = load_including_children(provider=provider, uri=child_uri)
- document.edoc.getroot().append(child.edoc.getroot())
- return document
+ return IOFile.from_filename(output_file.name)
+
+ def build(self, verbose=False, save_tex=None, morefloats=None):
+ """ morefloats: new/old/none
+ """
+ self.verbose = verbose
+ self.save_tex = save_tex
+
+ if morefloats is None and package_available('morefloats', 'maxfloats=19'):
+ morefloats = 'new'
+ self.morefloats = morefloats
+
+ book_info = self.wldoc.book_info
+ if self.cover_class:
+ self.cover = self.cover_class(book_info)
+
+ return self.get_pdf()
--- /dev/null
+\newcommand{\makecover}[2]{
+ \pdfpagewidth=#1
+ \pdfpageheight=#2
+
+ \thispagestyle{empty}
+ \newlength{\PictHOffset}
+ \newlength{\PictVOffset}
+ \setlength{\PictHOffset}{1in}
+ \addtolength{\PictHOffset}{\hoffset}
+ \addtolength{\PictHOffset}{\oddsidemargin}
+
+ \setlength{\PictVOffset}{1in}
+ \addtolength{\PictVOffset}{\voffset}
+ \addtolength{\PictVOffset}{\topmargin}
+ \addtolength{\PictVOffset}{\headheight}
+ \addtolength{\PictVOffset}{\headsep}
+ \addtolength{\PictVOffset}{\topskip}
+ \addtolength{\PictVOffset}{-\pdfpageheight}
+
+ \noindent\hspace*{-\PictHOffset}%
+ \raisebox{\PictVOffset}[0pt][0pt]{\makebox[0pt][l]{%
+ \includegraphics[height=\pdfpageheight,width=\pdfpagewidth]{cover.png}}}
+ \clearpage
+
+ \setlength{\pdfpagewidth}{210mm}
+ \setlength{\pdfpageheight}{297mm}
+}
--- /dev/null
+\usepackage[MeX]{polski}
+
+\newcommand{\rightsinfostr}[2][] {
+ \ifx&%
+ #2
+ \else
+ Ten utwór jest udostępniony na licencji
+ \href{#1}{#2}.
+ \fi
+}
+
+
+\renewcommand{\maketitle}{}
+
+\newcommand{\editorialsection}{
+ \begin{figure}[b!]
+ {
+ \footnotesize
+ \color{theme}
+ \noindent \rule{\linewidth}{0.4pt}
+
+ \rightsinfo
+ \vspace{.6em}
+
+ \editors
+
+ \vspace{.6em}
+ \coverby
+
+ \color{black}
+ }
+ \end{figure}
+}
\usepackage{trace}
-\usepackage[MeX]{polski}
-
\usepackage[xetex]{graphicx}
\usepackage{fontspec}
\usepackage{xunicode}
\usepackage[overload]{textcase}
\usepackage{scalefnt}
+% TODO: link color is a style thing
\usepackage[colorlinks=true,linkcolor=black,setpagesize=false,urlcolor=black,xetex]{hyperref}
\ifenablewlfont
\usebox{\ximagebox}%
\raisebox{0pt}[0pt][0pt]{\makebox[0pt][r]{\usebox{\xglyphbox}}}}
-\newcommand{\makecover}[2]{
- \pdfpagewidth=#1
- \pdfpageheight=#2
-
- \thispagestyle{empty}
- \newlength{\PictHOffset}
- \newlength{\PictVOffset}
- \setlength{\PictHOffset}{1in}
- \addtolength{\PictHOffset}{\hoffset}
- \addtolength{\PictHOffset}{\oddsidemargin}
-
- \setlength{\PictVOffset}{1in}
- \addtolength{\PictVOffset}{\voffset}
- \addtolength{\PictVOffset}{\topmargin}
- \addtolength{\PictVOffset}{\headheight}
- \addtolength{\PictVOffset}{\headsep}
- \addtolength{\PictVOffset}{\topskip}
- \addtolength{\PictVOffset}{-\pdfpageheight}
-
- \noindent\hspace*{-\PictHOffset}%
- \raisebox{\PictVOffset}[0pt][0pt]{\makebox[0pt][l]{%
- \includegraphics[height=\pdfpageheight,width=\pdfpagewidth]{cover.png}}}
- \clearpage
-
- \setlength{\pdfpagewidth}{210mm}
- \setlength{\pdfpageheight}{297mm}
-}
-
-
-\renewcommand{\maketitle}{
- {
- \thispagestyle{empty}
- \footnotesize
- \color{theme}
-
- \noindent \begin{minipage}[t]{.35\textwidth}\vspace{0pt}
- \href{http://www.wolnelektury.pl}{\xbox{\includegraphics[width=\textwidth]{wl-logo.png}}}
- \end{minipage}
- \begin{minipage}[t]{.65\textwidth}\vspace{0pt}
-
- \ifflaglessadvertising
- \else
- \href{\bookurl}{Ta lektura}, podobnie jak tysiące innych, jest dostępna on-line na stronie
- \href{http://www.wolnelektury.pl/}{wolnelektury.pl}.
- \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}.
-
- \end{minipage}
- \noindent \rule{\linewidth}{0.4pt}
-
- \vspace{.6em}
- \color{black}
- }
-}
-
-\newcommand{\editorialsection}{
- \begin{figure}[b!]
- {
- \footnotesize
- \color{theme}
- \noindent \rule{\linewidth}{0.4pt}
-
- \rightsinfo
- \vspace{.6em}
-
- Źródło: \href{\bookurl}{\bookurl}
-
- \vspace{.6em}
- \sourceinfo
-
- \description
- \vspace{.6em}
-
- \editors
-
- \vspace{.6em}
- \coverby
-
- \color{black}
- }
- \end{figure}
-}
\newcommand{\typosubsubsection}[1]{%
}
\newcommand{\translator}[1]{%
+% TODO: l10n is a style thing
\subsection*{\typosubsubsection{tłum. #1}}%
}
}
\ifshowfootnotes
- \newcommand{\pa}[1]{\NoCaseChange{\footnote{#1 [przypis autorski]}}}
+ \newcommand{\pa}[1]{\NoCaseChange{\footnote{#1}}}
\newcommand{\pe}[1]{\NoCaseChange{\footnote{#1}}}
\newcommand{\pr}[1]{\NoCaseChange{\footnote{#1}}}
\newcommand{\pt}[1]{\NoCaseChange{\footnote{#1}}}
<xsl:template match="utwor">
<TeXML xmlns="http://getfo.sourceforge.net/texml/ns1">
<TeXML escape="0">
- \documentclass[<xsl:value-of select="@customizations"/>]{wl}
-
- <!-- flags and values set on root -->
-
- \newif\ifflaglessadvertising
- <xsl:for-each select="@*[starts-with(name(), 'flag-')]">
- <cmd>
- <xsl:attribute name="name"><xsl:value-of select="wl:texcommand(name())" />true</xsl:attribute>
- </cmd>
- </xsl:for-each>
-
- <xsl:for-each select="@*[starts-with(name(), 'data-')]">
- <TeXML escape="0">
- \def\<xsl:value-of select="wl:texcommand(name())" />{<TeXML escape="1"><xsl:value-of select="."/></TeXML>}
- </TeXML>
- </xsl:for-each>
- </TeXML>
+ \documentclass[<xsl:value-of select="wl:get('customization_str')"/>]{wl}
+ \usepackage{style}
+ <xsl:if test="wl:get('has_cover')">
+ \usepackage{makecover}
+ </xsl:if>
<xsl:choose>
- <xsl:when test="@morefloats = 'new'">
+ <xsl:when test="wl:get('morefloats') = 'new'">
<TeXML escape="0">
\usepackage[maxfloats=64]{morefloats}
</TeXML>
</xsl:when>
- <xsl:when test="@morefloats = 'old'">
+ <xsl:when test="wl:get('morefloats') = 'old'">
<TeXML escape="0">
\usepackage{morefloats}
</TeXML>
</xsl:when>
- <xsl:when test="@morefloats = 'none'" />
+ <xsl:when test="wl:get('morefloats') = 'none'" />
<xsl:otherwise>
<TeXML escape="0">
\IfFileExists{morefloats.sty}{
<xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" mode='titlepage' />
<env name="document">
- <xsl:if test="@data-cover-width">
+ <xsl:if test="wl:get('has_cover')">
<cmd name="makecover">
- <parm><xsl:value-of select="210 * @data-cover-width div @data-cover-height" />mm</parm>
+ <parm><xsl:value-of select="210 * wl:get('cover', 'width') div wl:get('cover', 'height')" />mm</parm>
<parm>210mm</parm>
</cmd>
</xsl:if>
<TeXML escape="0">
\def\coverby{
- <xsl:if test="@data-cover-by">Okładka na podstawie:
+ <TeXML escape="1">
+ <xsl:if test="wl:get('has_cover') and wl:get('wldoc', 'book_info', 'cover_by')">
+ <!-- FIXME: should be stylable -->
+ Okładka na podstawie:
<xsl:choose>
- <xsl:when test="@data-cover-source">
- \href{\datacoversource}{\datacoverby}
+ <xsl:when test="wl:get('wldoc', 'book_info', 'cover_source')">
+ <cmd name="href"><parm>
+ <xsl:value-of select="wl:get('wldoc', 'book_info', 'cover_source')" />
+ </parm><parm>
+ <xsl:value-of select="wl:get('wldoc', 'book_info', 'cover_by')" />
+ </parm></cmd>
</xsl:when>
<xsl:otherwise>
- \datacoverby{}
+ <xsl:value-of select="wl:get('wldoc', 'book_info', 'cover_by')" />
</xsl:otherwise>
</xsl:choose>
</xsl:if>
+ </TeXML>
}
- \def\editors{<xsl:call-template name="editors" />}
+ \def\editors{<TeXML escape="1"><xsl:call-template name="editors" /></TeXML>}
</TeXML>
<cmd name="editorialsection" />
</env>
+ </TeXML>
</TeXML>
</xsl:template>
<xsl:template match="rdf:RDF" mode="titlepage">
<TeXML escape="0">
- \def\authors{<xsl:call-template name="authors" />}
+ \def\authors{<TeXML escape="1"><xsl:call-template name="authors" /></TeXML>}
\author{\authors}
- \title{<xsl:apply-templates select=".//dc:title" mode="inline" />}
- \def\translatorsline{<xsl:call-template name="translators" />}
-
- \def\bookurl{<xsl:value-of select=".//dc:identifier.url" />}
-
- \def\rightsinfo{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
- \href{http://creativecommons.org/licenses/by-sa/3.0/}{Creative Commons
- Uznanie Autorstwa – Na Tych Samych Warunkach 3.0 PL}.}
- <xsl:if test=".//dc:rights.license">
- \def\rightsinfo{Ten utwór jest udostepniony na licencji
- \href{<xsl:value-of select=".//dc:rights.license" />}{<xsl:value-of select=".//dc:rights" />}.}
- </xsl:if>
+ \title{<TeXML escape="1"><xsl:apply-templates select=".//dc:title" mode="inline" /></TeXML>}
+ \def\translatorsline{<TeXML escape="1"><xsl:call-template name="translators" /></TeXML>}
+
+ \def\bookurl{<TeXML escape="1"><xsl:value-of select=".//dc:identifier.url" /></TeXML>}
- \def\sourceinfo{
+ \def\rightsinfo{<TeXML excape="1">
+ <cmd name="rightsinfostr">
+ <xsl:if test=".//dc:rights.license">
+ <opt><xsl:value-of select=".//dc:rights.license" /></opt>
+ </xsl:if>
+ <parm><xsl:value-of select=".//dc:rights" /></parm>
+ </cmd>
+ </TeXML>}
+
+ <!-- FIXME: should be stylable -->
+ \def\sourceinfo{<TeXML excape="1">
<xsl:if test=".//dc:source">
Tekst opracowany na podstawie: <xsl:apply-templates select=".//dc:source" mode="inline" />
\vspace{.6em}
- </xsl:if>}
- \def\description{<xsl:apply-templates select=".//dc:description" mode="inline" />}
+ </xsl:if>
+ </TeXML>}
+ \def\description{<TeXML excape="1">
+ <xsl:apply-templates select=".//dc:description" mode="inline" /></TeXML>}
</TeXML>
</xsl:template>
--- /dev/null
+\usepackage[MeX]{polski}
+
+\newcommand{\rightsinfostr}[2][] {
+ \ifx&%
+ 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
+ \href{http://creativecommons.org/licenses/by-sa/3.0/}{Creative Commons
+ Uznanie Autorstwa – Na Tych Samych Warunkach 3.0 PL}.
+ \else
+ Ten utwór jest udostępniony na licencji
+ \href{#1}{#2}.
+ \fi
+}
+
+\renewcommand{\maketitle}{
+ {
+ \thispagestyle{empty}
+ \footnotesize
+ \color{theme}
+
+ \noindent \begin{minipage}[t]{.35\textwidth}\vspace{0pt}
+ \href{http://www.wolnelektury.pl}{\xbox{\includegraphics[width=\textwidth]{wl-logo.png}}}
+ \end{minipage}
+ \begin{minipage}[t]{.65\textwidth}\vspace{0pt}
+
+ %\ifflaglessadvertising
+ %\else
+ \href{\bookurl}{Ta lektura}, podobnie jak tysiące innych, jest dostępna on-line na stronie
+ \href{http://www.wolnelektury.pl/}{wolnelektury.pl}.
+ \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}.
+
+ \end{minipage}
+ \noindent \rule{\linewidth}{0.4pt}
+
+ \vspace{.6em}
+ \color{black}
+ }
+}
+
+\newcommand{\editorialsection}{
+ \begin{figure}[b!]
+ {
+ \footnotesize
+ \color{theme}
+ \noindent \rule{\linewidth}{0.4pt}
+
+ \rightsinfo
+ \vspace{.6em}
+
+ Źródło: \href{\bookurl}{\bookurl}
+
+ \vspace{.6em}
+ \sourceinfo
+
+ \description
+ \vspace{.6em}
+
+ \editors
+
+ \vspace{.6em}
+ \coverby
+
+ \color{black}
+ }
+ \end{figure}
+}
+
+
+% Label <pa> as such.
+\let\paorig\pa
+\ifshowfootnotes
+ \renewcommand{\pa}[1]{\paorig{#1 [przypis autorski]}}
+\fi
\ No newline at end of file
--- /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 Image, ImageFont, ImageDraw
+from StringIO import StringIO
+from librarian import get_resource, URLOpener
+from librarian.cover import Cover, TextBox
+
+
+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_lineskip = 30
+ title_font = ImageFont.truetype(
+ get_resource('fonts/DejaVuSerif-Bold.ttf'), 30)
+ title_lineskip = 40
+ title_box_width = 350
+ bar_width = 35
+ background_color = '#444'
+ author_color = '#444'
+ default_background = get_resource('res/cover.png')
+ format = 'JPEG'
+
+ epoch_colors = {
+ u'Starożytność': '#9e3610',
+ u'Średniowiecze': '#564c09',
+ u'Renesans': '#8ca629',
+ u'Barok': '#a6820a',
+ u'Oświecenie': '#f2802e',
+ u'Romantyzm': '#db4b16',
+ u'Pozytywizm': '#961060',
+ u'Modernizm': '#7784e0',
+ u'Dwudziestolecie międzywojenne': '#3044cf',
+ u'Współczesność': '#06393d',
+ }
+
+ def __init__(self, book_info, format=None, image_cache=None):
+ super(WLCover, self).__init__(book_info, format=format)
+ self.kind = book_info.kind
+ self.epoch = book_info.epoch
+ if book_info.cover_url:
+ url = book_info.cover_url
+ bg_src = None
+ if image_cache:
+ from urllib import quote
+ try:
+ bg_src = URLOpener().open(image_cache + quote(url, safe=""))
+ except:
+ pass
+ if bg_src is None:
+ bg_src = URLOpener().open(url)
+ self.background_img = StringIO(bg_src.read())
+ bg_src.close()
+ else:
+ self.background_img = self.default_background
+
+ def pretty_author(self):
+ return self.author.upper()
+
+ def image(self):
+ img = Image.new('RGB', (self.width, self.height), self.background_color)
+ draw = ImageDraw.Draw(img)
+
+ if self.epoch in self.epoch_colors:
+ epoch_color = self.epoch_colors[self.epoch]
+ else:
+ epoch_color = '#000'
+ draw.rectangle((0, 0, self.bar_width, self.height), fill=epoch_color)
+
+ if self.background_img:
+ src = Image.open(self.background_img)
+ trg_size = (self.width - self.bar_width, self.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.crop((0, cut, src.size[0], src.size[1] - cut))
+ else:
+ resized = (
+ src.size[0] * trg_size[1] / src.size[1],
+ trg_size[1],
+ )
+ cut = (resized[0] - trg_size[0]) / 2
+ src = src.resize(resized)
+ src = src.crop((cut, 0, src.size[0] - cut, src.size[1]))
+
+ img.paste(src, (self.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,
+ 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.text(self.pretty_title(),
+ line_height=self.title_lineskip,
+ font=self.title_font,
+ color=epoch_color,
+ shadow_color=self.title_shadow,
+ )
+ box_img = box.image()
+
+ if self.kind == 'Liryka':
+ # top
+ box_top = 100
+ elif self.kind == 'Epika':
+ # bottom
+ box_top = self.height - 100 - box_img.size[1]
+ else:
+ # center
+ box_top = (self.height - box_img.size[1]) / 2
+
+ box_left = self.bar_width + (self.width - self.bar_width -
+ box_img.size[0]) / 2
+ draw.rectangle((box_left, box_top,
+ box_left + box_img.size[0], box_top + box_img.size[1]),
+ fill='#fff')
+ img.paste(box_img, (box_left, box_top), box_img)
+
+ return img
--- /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 ImageFont
+from librarian import get_resource
+from librarian.cover import Cover
+
+
+class VirtualoCover(Cover):
+ width = 600
+ height = 730
+ author_top = 73
+ title_top = 73
+ logo_bottom = 25
+ logo_width = 250
+
+
+class PrestigioCover(Cover):
+ width = 580
+ height = 783
+ background_img = get_resource('res/cover-prestigio.png')
+
+ author_top = 446
+ author_margin_left = 118
+ author_margin_right = 62
+ author_lineskip = 60
+ author_color = '#fff'
+ author_shadow = '#000'
+ author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Italic.ttf'), 50)
+
+ title_top = 0
+ title_margin_left = 118
+ title_margin_right = 62
+ title_lineskip = 60
+ title_color = '#fff'
+ title_shadow = '#000'
+ title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Italic.ttf'), 50)
+
+ def pretty_title(self):
+ return u"„%s”" % self.title
+
+
+class BookotekaCover(Cover):
+ width = 2140
+ height = 2733
+ background_img = get_resource('res/cover-bookoteka.png')
+
+ author_top = 480
+ author_margin_left = 307
+ author_margin_right = 233
+ author_lineskip = 156
+ author_color = '#d9d919'
+ author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 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)
+
+ format = 'PNG'
+
+
+class GandalfCover(Cover):
+ width = 600
+ height = 730
+ background_img = get_resource('res/cover-gandalf.png')
+ author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 30)
+ title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 40)
+ logo_bottom = 25
+ logo_width = 250
+ format = 'PNG'
--- /dev/null
+import shutil
+from librarian import get_resource
+from librarian.pdf import PDFFormat
+from librarian.styles.wolnelektury.cover import WLCover
+
+class WLPDFFormat(PDFFormat):
+ cover_class = WLCover
+ style = get_resource('res/styles/wolnelektury/pdf/wolnelektury.sty')
+
+ def get_tex_dir(self):
+ temp = super(WLPDFFormat, self).get_tex_dir()
+ shutil.copy(get_resource('res/wl-logo.png'), temp)
+ return temp
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
import copy
-from librarian import functions, OutputFile
+from librarian import functions, IOFile
from lxml import etree
import os
license_description = ""
source = ""
contributors = ""
- return OutputFile.from_string((TEMPLATE % {
+ return IOFile.from_string((TEMPLATE % {
'description': description,
'url': url,
'license_description': license_description,
'contributors': contributors,
}).encode('utf-8'))
else:
- return OutputFile.from_string(unicode(result).encode('utf-8'))
+ return IOFile.from_string(unicode(result).encode('utf-8'))
# This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
-from StringIO import StringIO
-from librarian import OutputFile
-from librarian.book2anything import Book2Anything, Option
+from librarian.book2anything import Book2Anything
class Book2Cover(Book2Anything):
assert not WLDocument.from_string(
'<utwor />',
parse_dublincore=False,
- ).as_html()
+ ).as_html().get_string()
--- /dev/null
+import os
+from StringIO import StringIO
+from tempfile import NamedTemporaryFile
+from nose.tools import *
+from librarian import IOFile
+
+def test_iofile_from_string_reusable():
+ some_file = IOFile.from_string("test")
+ some_file.get_file().read()
+ assert_equal(some_file.get_file().read(), "test")
+
+def test_iofile_from_filename_reusable():
+ temp = NamedTemporaryFile(delete=False)
+ try:
+ temp.write('test')
+ temp.close()
+ some_file = IOFile.from_filename(temp.name)
+ some_file.get_file().read()
+ assert_equal(some_file.get_file().read(), "test")
+ finally:
+ os.unlink(temp.name)