From: Robert Błaut Date: Sat, 15 Feb 2014 09:52:13 +0000 (+0100) Subject: Merge recent changes from branch 'fnp/librarian/master' X-Git-Tag: 1.7~104^2~1 X-Git-Url: https://git.mdrn.pl/librarian.git/commitdiff_plain/08a62c79e1e283f446130c02b92aa7920d7d7949?ds=sidebyside;hp=d19499dcc673ef42dcaaab9c9c9965d4a0caac81 Merge recent changes from branch 'fnp/librarian/master' Sync between fork and origin --- diff --git a/librarian/__init__.py b/librarian/__init__.py index bf15d13..b257b79 100644 --- a/librarian/__init__.py +++ b/librarian/__init__.py @@ -72,6 +72,7 @@ XINS = XMLNamespace("http://www.w3.org/2001/XInclude") 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() diff --git a/librarian/cover.py b/librarian/cover.py index d1b2cc0..2f3117c 100644 --- a/librarian/cover.py +++ b/librarian/cover.py @@ -248,9 +248,11 @@ class WLCover(Cover): 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 = { @@ -266,11 +268,25 @@ class WLCover(Cover): 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 @@ -278,46 +294,17 @@ class WLCover(Cover): 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) @@ -333,46 +320,68 @@ class WLCover(Cover): 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 diff --git a/librarian/dcparser.py b/librarian/dcparser.py index a7215a1..12bb24f 100644 --- a/librarian/dcparser.py +++ b/librarian/dcparser.py @@ -10,7 +10,7 @@ import re 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 @@ -242,6 +242,9 @@ class WorkInfo(object): 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 @@ -448,6 +451,9 @@ class BookInfo(WorkInfo): 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), ) diff --git a/librarian/html.py b/librarian/html.py index 70fc6e5..0eeb76b 100644 --- a/librarian/html.py +++ b/librarian/html.py @@ -134,14 +134,17 @@ def extract_fragments(input_filename): 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 @@ -177,25 +180,22 @@ def extract_fragments(input_filename): 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): @@ -225,7 +225,7 @@ def add_anchors(root): 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() diff --git a/librarian/pdf.py b/librarian/pdf.py index 2154985..af258fc 100644 --- a/librarian/pdf.py +++ b/librarian/pdf.py @@ -28,6 +28,7 @@ from librarian.parser import WLDocument 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() @@ -247,11 +248,23 @@ def transform(wldoc, verbose=False, save_tex=None, morefloats=None, 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) diff --git a/librarian/pdf/wl.cls b/librarian/pdf/wl.cls index a9ace8e..8907b08 100644 --- a/librarian/pdf/wl.cls +++ b/librarian/pdf/wl.cls @@ -126,6 +126,8 @@ Letters={SmallCaps,UppercaseSmallCaps} \pagestyle{plain} \usepackage{fancyhdr} +\usepackage{marginnote} + \makeatletter @@ -238,6 +240,13 @@ Letters={SmallCaps,UppercaseSmallCaps} \color{theme} \noindent \rule{\linewidth}{0.4pt} + \ifdefined\sponsors + \marginnote{% + \centering% + \sponsors% + } + \fi + \rightsinfo \vspace{.6em} diff --git a/librarian/pdf/wl2tex.xslt b/librarian/pdf/wl2tex.xslt index ca948da..c120fb0 100644 --- a/librarian/pdf/wl2tex.xslt +++ b/librarian/pdf/wl2tex.xslt @@ -110,6 +110,24 @@ \def\funders{Publikację ufundowali i ufundowały: .} + + + \def\sponsors{ + \scriptsize + + + + + + Sfinansowano ze~środków: + + + + \vspace{1em} + + + } + @@ -404,6 +422,18 @@ + + + + + \includegraphics[height=0.25\textwidth,width=0.25\textwidth,keepaspectratio]{} + + + + + + + diff --git a/librarian/res/sponsors/nck.png b/librarian/res/sponsors/nck.png new file mode 100644 index 0000000..e53f19c Binary files /dev/null and b/librarian/res/sponsors/nck.png differ diff --git a/librarian/res/webtreatsetc-5647576127-ccby.png b/librarian/res/webtreatsetc-5647576127-ccby.png deleted file mode 100644 index 6919e93..0000000 Binary files a/librarian/res/webtreatsetc-5647576127-ccby.png and /dev/null differ diff --git a/librarian/sponsor.py b/librarian/sponsor.py new file mode 100644 index 0000000..15d9107 --- /dev/null +++ b/librarian/sponsor.py @@ -0,0 +1,11 @@ +# -*- 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()) diff --git a/librarian/text.py b/librarian/text.py index 9edfa62..37bd7ed 100644 --- a/librarian/text.py +++ b/librarian/text.py @@ -45,6 +45,8 @@ def transform(wldoc, flags=None, **options): 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) diff --git a/scripts/book2cover b/scripts/book2cover index 758ab0e..444563c 100755 --- a/scripts/book2cover +++ b/scripts/book2cover @@ -20,9 +20,6 @@ class Book2Cover(Book2Anything): 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 diff --git a/scripts/book2html b/scripts/book2html index 5d48eec..2c1d04e 100755 --- a/scripts/book2html +++ b/scripts/book2html @@ -17,7 +17,7 @@ class Book2Html(Book2Anything): 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') diff --git a/scripts/book2pdf b/scripts/book2pdf index 68e2d08..ccb5fac 100755 --- a/scripts/book2pdf +++ b/scripts/book2pdf @@ -12,7 +12,7 @@ class Book2Pdf(Book2Anything): 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', diff --git a/scripts/book2txt b/scripts/book2txt index 1b4c0ef..c706a07 100755 --- a/scripts/book2txt +++ b/scripts/book2txt @@ -13,15 +13,20 @@ class Book2Txt(Book2Anything): 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 diff --git a/setup.py b/setup.py index 7bcbbc2..732f145 100755 --- a/setup.py +++ b/setup.py @@ -29,7 +29,8 @@ setup( 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=[ diff --git a/tests/files/text/asnyk_miedzy_nami_expected.html b/tests/files/text/asnyk_miedzy_nami_expected.html index fd18174..24285f1 100644 --- a/tests/files/text/asnyk_miedzy_nami_expected.html +++ b/tests/files/text/asnyk_miedzy_nami_expected.html @@ -6,32 +6,33 @@
  1. Miłość platoniczna: 1 2
  2. Natura: 1
  3. +
  4. Nicość: 1

Adam AsnykMiłość platonicznaMiędzy nami nic nie było

Miłość platoniczna
-

1Między nami nic nie było!

+ 1

Między nami Nicośćnic nie było!

Żadnych zwierzeń, wyznań żadnych!

Nic nas z sobą nie łączyło —

Prócz wiosennych marzeń zdradnych;

-

5NaturaPrócz tych woni, barw i blasków,

+ 5

NaturaPrócz tych woni, barw i blasków,

Unoszących się w przestrzeni;

Prócz szumiących śpiewem lasków

I tej świeżej łąk zieleni;

Prócz tych kaskad i potoków,

-

10Zraszających każdy parów,

+ 10

Zraszających każdy parów,

Prócz girlandy tęcz, obłoków,

Prócz natury słodkich czarów;

Prócz tych wspólnych, jasnych zdrojów,

Z których serce zachwyt piło;

-

15Prócz pierwiosnków i powojów,—

+ 15

Prócz pierwiosnków i powojów,—

Między nami nic nie było!

diff --git a/tests/files/text/asnyk_miedzy_nami_fragments.html b/tests/files/text/asnyk_miedzy_nami_fragments.html new file mode 100644 index 0000000..944d830 --- /dev/null +++ b/tests/files/text/asnyk_miedzy_nami_fragments.html @@ -0,0 +1,50 @@ +1: Nicość +

nic nie było!

+
+ + +1189062500041: Miłość platoniczna +
+

Między nami nic nie było!

+

Żadnych zwierzeń, wyznań żadnych!

+

Nic nas z sobą nie łączyło —

+

Prócz wiosennych marzeń zdradnych;

+
+
+

Prócz tych woni, barw i blasków,

+

Unoszących się w przestrzeni;

+

Prócz szumiących śpiewem lasków

+

I tej świeżej łąk zieleni;

+
+
+

Prócz tych kaskad i potoków,

+

Zraszających każdy parów,

+

Prócz girlandy tęcz, obłoków,

+

Prócz natury słodkich czarów;

+
+
+

Prócz tych wspólnych, jasnych zdrojów,

+

Z których serce zachwyt piło;

+

Prócz pierwiosnków i powojów,—

+

Między nami nic nie było!

+
+ + +1189062528872: Natura +

Prócz tych woni, barw i blasków,

+

Unoszących się w przestrzeni;

+

Prócz szumiących śpiewem lasków

+

I tej świeżej łąk zieleni;

+
+
+

Prócz tych kaskad i potoków,

+

Zraszających każdy parów,

+

Prócz girlandy tęcz, obłoków,

+

Prócz natury słodkich czarów;

+
+
+

Prócz tych wspólnych, jasnych zdrojów,

+

Z których serce zachwyt piło;

+

Prócz pierwiosnków i powojów,—

+

Między nami nic nie było!

+
diff --git a/tests/files/text/asnyk_zbior.xml b/tests/files/text/asnyk_zbior.xml old mode 100755 new mode 100644 diff --git a/tests/files/text/do-mlodych.xml b/tests/files/text/do-mlodych.xml old mode 100755 new mode 100644 diff --git a/tests/files/text/miedzy-nami-nic-nie-bylo.xml b/tests/files/text/miedzy-nami-nic-nie-bylo.xml index a94b8f0..8036fce 100644 --- a/tests/files/text/miedzy-nami-nic-nie-bylo.xml +++ b/tests/files/text/miedzy-nami-nic-nie-bylo.xml @@ -37,7 +37,7 @@ -Między nami nic nie było!/ +Między nami Nicośćnic nie było!/ Żadnych zwierzeń, wyznań żadnych!/ Nic nas z sobą nie łączyło ---/ Prócz wiosennych marzeń zdradnych; diff --git a/tests/test_html_fragments.py b/tests/test_html_fragments.py new file mode 100644 index 0000000..99bb62d --- /dev/null +++ b/tests/test_html_fragments.py @@ -0,0 +1,20 @@ +# -*- 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')) +