XHTMLNS = XMLNamespace("http://www.w3.org/1999/xhtml")
NCXNS = XMLNamespace("http://www.daisy.org/z3986/2005/ncx/")
OPFNS = XMLNamespace("http://www.idpf.org/2007/opf")
+PLMETNS = XMLNamespace("http://dl.psnc.pl/schemas/plmet/")
WLNS = EmptyNamespace()
logo_width = 140
bar_width = 35
+ bar_color = '#000'
+ box_position = 'middle'
background_color = '#444'
author_color = '#444'
- default_background = get_resource('res/cover.png')
+ background_img = get_resource('res/cover.png')
format = 'JPEG'
epoch_colors = {
u'Współczesność': '#06393d',
}
- def __init__(self, book_info, format=None, width=None, height=None, with_logo=False):
+ kind_box_position = {
+ u'Liryka': 'top',
+ u'Epika': 'bottom',
+ }
+
+ def __init__(self, book_info, format=None, width=None, height=None):
super(WLCover, self).__init__(book_info, format=format, width=width, height=height)
- self.kind = book_info.kind
- self.epoch = book_info.epoch
- self.with_logo = with_logo
+ # Set box position.
+ self.box_position = book_info.cover_box_position or \
+ self.kind_box_position.get(book_info.kind, self.box_position)
+ # Set bar color.
+ if book_info.cover_bar_color == 'none':
+ self.bar_width = 0
+ else:
+ self.bar_color = book_info.cover_bar_color or \
+ self.epoch_colors.get(book_info.epoch, self.bar_color)
+ # Set title color.
+ self.title_color = self.epoch_colors.get(book_info.epoch, self.title_color)
+
if book_info.cover_url:
url = book_info.cover_url
bg_src = None
bg_src = URLOpener().open(url)
self.background_img = StringIO(bg_src.read())
bg_src.close()
- else:
- self.background_img = self.default_background
def pretty_author(self):
return self.author.upper()
- def image(self):
- metr = Metric(self, self.scale)
- img = Image.new('RGB', (metr.width, metr.height), self.background_color)
- draw = ImageDraw.Draw(img)
-
- if self.epoch in self.epoch_colors:
- epoch_color = self.epoch_colors[self.epoch]
- else:
- epoch_color = '#000'
- draw.rectangle((0, 0, metr.bar_width, metr.height), fill=epoch_color)
-
- if self.background_img:
- src = Image.open(self.background_img)
- trg_size = (metr.width - metr.bar_width, metr.height)
- if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]:
- resized = (
- trg_size[0],
- src.size[1] * trg_size[0] / src.size[0]
- )
- cut = (resized[1] - trg_size[1]) / 2
- src = src.resize(resized, Image.ANTIALIAS)
- src = src.crop((0, cut, src.size[0], src.size[1] - cut))
- else:
- resized = (
- src.size[0] * trg_size[1] / src.size[1],
- trg_size[1],
- )
- cut = (resized[0] - trg_size[0]) / 2
- src = src.resize(resized, Image.ANTIALIAS)
- src = src.crop((cut, 0, src.size[0] - cut, src.size[1]))
+ def add_box(self, img):
+ if self.box_position == 'none':
+ return img
- img.paste(src, (metr.bar_width, 0))
- del src
+ metr = Metric(self, self.scale)
+ # Write author name.
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)
fill=self.author_color, width=metr.box_line_width)
box.skip(metr.box_below_line)
+ # Write title.
title_font = ImageFont.truetype(
self.title_font_ttf, metr.title_font_size)
box.text(self.pretty_title(),
line_height=metr.title_lineskip,
font=title_font,
- color=epoch_color,
+ color=self.title_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
+ # Find box position.
+ if self.box_position == 'top':
box_top = metr.box_top_margin
- elif self.kind == 'Epika':
- # bottom
+ elif self.box_position == 'bottom':
box_top = metr.height - metr.box_bottom_margin - box_img.size[1]
- else:
- # center
+ else: # Middle.
box_top = (metr.height - box_img.size[1]) / 2
box_left = metr.bar_width + (metr.width - metr.bar_width -
box_img.size[0]) / 2
- draw.rectangle((box_left, box_top,
+
+ # Draw the white box.
+ ImageDraw.Draw(img).rectangle((box_left, box_top,
box_left + box_img.size[0], box_top + box_img.size[1]),
fill='#fff')
+ # Paste the contents into the white box.
img.paste(box_img, (box_left, box_top), box_img)
+ return img
+
+ def image(self):
+ metr = Metric(self, self.scale)
+ img = Image.new('RGB', (metr.width, metr.height), self.background_color)
+ draw = ImageDraw.Draw(img)
+
+ draw.rectangle((0, 0, metr.bar_width, metr.height), fill=self.bar_color)
+
+ if self.background_img:
+ src = Image.open(self.background_img)
+ trg_size = (metr.width - metr.bar_width, metr.height)
+ if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]:
+ resized = (
+ trg_size[0],
+ src.size[1] * trg_size[0] / src.size[0]
+ )
+ cut = (resized[1] - trg_size[1]) / 2
+ src = src.resize(resized, Image.ANTIALIAS)
+ src = src.crop((0, cut, src.size[0], src.size[1] - cut))
+ else:
+ resized = (
+ src.size[0] * trg_size[1] / src.size[1],
+ trg_size[1],
+ )
+ cut = (resized[0] - trg_size[0]) / 2
+ src = src.resize(resized, Image.ANTIALIAS)
+ src = src.crop((cut, 0, src.size[0] - cut, src.size[1]))
+
+ img.paste(src, (metr.bar_width, 0))
+ del src
- 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)
+ img = self.add_box(img)
return img
from librarian.util import roman_to_int
from librarian import (ValidationError, NoDublinCore, ParseError, DCNS, RDFNS,
- XMLNS, WLURI)
+ XMLNS, WLURI, WLNS, PLMETNS)
import lxml.etree as etree # ElementTree API using libxml2
from lxml.etree import XMLSyntaxError
Field( DCNS('identifier.url'), 'url', WLURI, strict=as_wluri_strict),
Field( DCNS('rights.license'), 'license', required=False),
Field( DCNS('rights'), 'license_description'),
+
+ Field( PLMETNS('digitisationSponsor'), 'sponsors', multiple=True, default=[]),
+ Field( WLNS('digitisationSponsorNote'), 'sponsor_note', required=False),
)
@classmethod
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),
+ # WLCover-specific.
+ Field( WLNS('coverBarColor'), 'cover_bar_color', required=False),
+ Field( WLNS('coverBoxPosition'), 'cover_box_position', required=False),
)
fragment = Fragment(id=element.get('fid'), themes=element.text)
# Append parents
- if element.getparent().get('id', None) != 'book-text':
- parents = [element.getparent()]
- while parents[-1].getparent().get('id', None) != 'book-text':
- parents.append(parents[-1].getparent())
-
- parents.reverse()
- for parent in parents:
- fragment.append('start', parent)
+ parent = element.getparent()
+ parents = []
+ while parent.get('id', None) != 'book-text':
+ cparent = copy.deepcopy(parent)
+ cparent.text = None
+ parents.append(cparent)
+ parent = parent.getparent()
+
+ parents.reverse()
+ for parent in parents:
+ fragment.append('start', parent)
open_fragments[fragment.id] = fragment
def add_anchor(element, prefix, with_link=True, with_target=True, link_text=None):
+ parent = element.getparent()
+ index = parent.index(element)
+
if with_link:
if link_text is None:
link_text = prefix
anchor = etree.Element('a', href='#%s' % prefix)
anchor.set('class', 'anchor')
anchor.text = unicode(link_text)
- if element.text:
- anchor.tail = element.text
- element.text = u''
- element.insert(0, anchor)
+ parent.insert(index, anchor)
if with_target:
anchor_target = etree.Element('a', name='%s' % prefix)
anchor_target.set('class', 'target')
anchor_target.text = u' '
- if element.text:
- anchor_target.tail = element.text
- element.text = u''
- element.insert(0, anchor_target)
+ parent.insert(index, anchor_target)
def any_ancestor(element, test):
def raw_printable_text(element):
working = copy.deepcopy(element)
for e in working.findall('a'):
- if e.get('class') == 'annotation':
+ if e.get('class') in ('annotation', 'theme-begin'):
e.text = ''
return etree.tostring(working, method='text', encoding=unicode).strip()
from librarian import ParseError, DCNS, get_resource, OutputFile
from librarian import functions
from librarian.cover import DefaultEbookCover
+from .sponsor import sponsor_logo
functions.reg_substitute_entities()
style_filename = get_stylesheet("wl2tex")
style = etree.parse(style_filename)
- texml = document.transform(style)
-
# TeXML -> LaTeX
temp = mkdtemp('-wl2pdf')
+ for sponsor in book_info.sponsors:
+ ins = etree.Element("data-sponsor", name=sponsor)
+ logo = sponsor_logo(sponsor)
+ if logo:
+ fname = 'sponsor-%s' % os.path.basename(logo)
+ shutil.copy(logo, os.path.join(temp, fname))
+ ins.set('src', fname)
+ root.insert(0, ins)
+
+ if book_info.sponsor_note:
+ root.set("sponsor-note", book_info.sponsor_note)
+
+ texml = document.transform(style)
+
if cover:
with open(os.path.join(temp, 'cover.png'), 'w') as f:
bound_cover.save(f)
\pagestyle{plain}
\usepackage{fancyhdr}
+\usepackage{marginnote}
+
\makeatletter
\color{theme}
\noindent \rule{\linewidth}{0.4pt}
+ \ifdefined\sponsors
+ \marginnote{%
+ \centering%
+ \sponsors%
+ }
+ \fi
+
\rightsinfo
\vspace{.6em}
\def\funders{Publikację ufundowali i ufundowały:
<TeXML escape="1"><xsl:value-of select="@funders" /></TeXML>.}
</xsl:if>
+
+ <xsl:if test="@sponsor-note|data-sponsor">
+ \def\sponsors{
+ \scriptsize
+ <xsl:choose>
+ <xsl:when test="@sponsor-note">
+ <TeXML escape="1"><xsl:value-of select="@sponsor-note" /></TeXML>
+ </xsl:when>
+ <xsl:otherwise>
+ Sfinansowano ze~środków:
+ </xsl:otherwise>
+ </xsl:choose>
+
+ \vspace{1em}
+
+ <xsl:apply-templates select="data-sponsor" mode="sponsor" />
+ }
+ </xsl:if>
</TeXML>
<cmd name="editorialsection" />
</xsl:if>
</xsl:template>
+<xsl:template match="data-sponsor" mode="sponsor">
+ <cmd name="par"><parm>
+ <xsl:choose>
+ <xsl:when test="@src">
+ \includegraphics[height=0.25\textwidth,width=0.25\textwidth,keepaspectratio]{<xsl:value-of select="@src" />}
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@name" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </parm></cmd>
+</xsl:template>
<!-- ============== -->
<!-- = ADDED TAGS = -->
--- /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.
+#
+from librarian import get_resource
+
+def sponsor_logo(name):
+ return {
+ 'Narodowe Centrum Kultury': get_resource('res/sponsors/nck.png')
+ }.get(name.strip())
if flags:
for flag in flags:
document.edoc.getroot().set(flag, 'yes')
+ if 'wrapping' in options:
+ options['wrapping'] = str(options['wrapping'])
result = document.transform(style, **options)
help='Set width.'),
Option('-H', '--height', action='store', type='int', dest='height', default=None,
help='Set height.'),
- Option('-l', '--with-logo', dest='with_logo',
- action='store_true', default=False,
- help='Add WL logo in white box.'),
]
@staticmethod
action='store_false', default=True,
help='output raw text for use in templates')
]
- parser_args = [
+ parser_options = [
Option('-i', '--ignore-dublin-core', dest='parse_dublincore',
action='store_false', default=True,
help='don\'t try to parse dublin core metadata')
ext = "pdf"
uses_cover = True
uses_provider = True
- transform_args = [
+ transform_options = [
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',
ext = "txt"
uses_cover = False
uses_provider = False
- parser_args = [
+ parser_options = [
Option('-i', '--ignore-dublin-core', dest='parse_dublincore',
action='store_false', default=True,
help='don\'t try to parse dublin core metadata')
]
- transform_args = [
+ transform_options = [
Option('-w', '--wrap', action='store', type='int', dest='wrapping', default=0,
help='set line wrap column')
]
+ transform_flags = [
+ Option('-r', '--raw', dest='raw-text',
+ action='store_true', default=False,
+ help='Produce raw text, without any surrounding info.')
+ ]
transform = WLDocument.as_text
maintainer_email='radoslaw.czajka@nowoczesnapolska.org.pl',
url='http://github.com/fnp/librarian',
packages=['librarian'],
- package_data={'librarian': ['xslt/*.xslt', 'epub/*', 'mobi/*', 'pdf/*', 'fb2/*', 'fonts/*', 'res/*'] +
+ package_data={'librarian': ['xslt/*.xslt', 'epub/*', 'mobi/*', 'pdf/*', 'fb2/*', 'fonts/*'] +
+ whole_tree(os.path.join(os.path.dirname(__file__), 'librarian'), 'res') +
whole_tree(os.path.join(os.path.dirname(__file__), 'librarian'), 'font-optimizer')},
include_package_data=True,
install_requires=[
<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>
+<li>Nicość: <a href="#m1">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">
-<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>
+<a name="sec4"></a><a name="f1" class="target"> </a><a href="#f1" class="anchor">1</a><p class="verse">Między nami <a name="m1" class="theme-begin" fid="1">Nicość</a>nic nie było!<span class="theme-end" fid="1"></span></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">
-<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>
+<a name="sec5"></a><a name="f5" class="target"> </a><a href="#f5" class="anchor">5</a><p class="verse"><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">
<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>
+<a name="f10" class="target"> </a><a href="#f10" class="anchor">10</a><p class="verse">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">
<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>
+<a name="f15" class="target"> </a><a href="#f15" class="anchor">15</a><p class="verse">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>
</div>
</div>
--- /dev/null
+1: Nicość
+<div class="stanza"><p class="verse">nic nie było!</p>
+</div>
+
+
+1189062500041: Miłość platoniczna
+<div class="stanza">
+<p class="verse">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">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>
+<p class="verse">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>
+<p class="verse">Z których serce zachwyt piło;</p>
+<p class="verse">Prócz pierwiosnków i powojów,—</p>
+<p class="verse">Między nami nic nie było!</p>
+</div>
+
+
+1189062528872: Natura
+<div class="stanza"><p class="verse">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>
+<p class="verse">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>
+<p class="verse">Z których serce zachwyt piło;</p>
+<p class="verse">Prócz pierwiosnków i powojów,—</p>
+<p class="verse">Między nami nic nie było!</p>
+</div>
-<strofa>Między nami nic nie było!/
+<strofa>Między nami <begin id="b1"/><motyw id="m1">Nicość</motyw>nic nie było!<end id="e1"/>/
Żadnych zwierzeń, wyznań żadnych!/
Nic nas z sobą nie łączyło ---/
Prócz wiosennych marzeń zdradnych;</strofa>
--- /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.
+#
+from librarian.html import extract_fragments
+from nose.tools import *
+from utils import get_fixture
+
+
+def test_fragments():
+ expected_output_file_path = get_fixture('text', 'asnyk_miedzy_nami_fragments.html')
+
+ closed_fragments, open_fragments = extract_fragments(
+ get_fixture('text', 'asnyk_miedzy_nami_expected.html'))
+ assert not open_fragments
+ fragments_text = u"\n\n".join(u"%s: %s\n%s" % (f.id, f.themes, f)
+ for f in closed_fragments.values())
+ assert_equal(fragments_text, file(expected_output_file_path).read().decode('utf-8'))
+