1 # -*- coding: utf-8 -*-
 
   3 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
 
   4 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 
   7 import Image, ImageFont, ImageDraw, ImageFilter, ImageEnhance
 
   8 from StringIO import StringIO
 
   9 from librarian import get_resource, OutputFile, URLOpener
 
  13     """Gets metrics from an object, scaling it by a factor."""
 
  14     def __init__(self, obj, scale):
 
  16         self._scale = float(scale)
 
  18     def __getattr__(self, name):
 
  19         src = getattr(self._obj, name)
 
  20         if src and self._scale:
 
  21             src = type(src)(self._scale * src)
 
  25 class TextBox(object):
 
  26     """Creates an Image with a series of centered strings."""
 
  32     def __init__(self, max_width, max_height, padding_x=None, padding_y=None):
 
  34             padding_x = self.SHADOW_X + self.SHADOW_BLUR
 
  36             padding_y = self.SHADOW_Y + self.SHADOW_BLUR
 
  38         self.max_width = max_width
 
  39         self.max_text_width = max_width - 2 * padding_x
 
  40         self.padding_y = padding_y
 
  41         self.height = padding_y
 
  42         self.img = Image.new('RGBA', (max_width, max_height))
 
  43         self.draw = ImageDraw.Draw(self.img)
 
  44         self.shadow_img = None
 
  45         self.shadow_draw = None
 
  47     def skip(self, height):
 
  48         """Skips some vertical space."""
 
  51     def text(self, text, color='#000', font=None, line_height=20,
 
  53         """Writes some centered text."""
 
  54         text = re.sub(r'\s+', ' ', text)
 
  56             if not self.shadow_img:
 
  57                 self.shadow_img = Image.new('RGBA', self.img.size)
 
  58                 self.shadow_draw = ImageDraw.Draw(self.shadow_img)
 
  61             line_width = self.draw.textsize(line, font=font)[0]
 
  62             while line_width > self.max_text_width:
 
  63                 parts = line.rsplit(' ', 1)
 
  65                     line_width = self.max_text_width
 
  68                 line_width = self.draw.textsize(line, font=font)[0]
 
  69             line = line.strip() + ' '
 
  71             pos_x = (self.max_width - line_width) / 2
 
  74                 self.shadow_draw.text(
 
  75                         (pos_x + self.SHADOW_X, self.height + self.SHADOW_Y),
 
  76                         line, font=font, fill=shadow_color
 
  79             self.draw.text((pos_x, self.height), line, font=font, fill=color)
 
  80             self.height += line_height
 
  82             text = text[len(line):]
 
  85         """Creates the actual Image object."""
 
  86         image = Image.new('RGBA', (self.max_width,
 
  87                                    self.height + self.padding_y))
 
  89             shadow = self.shadow_img.filter(ImageFilter.BLUR)
 
  90             image.paste(shadow, (0, 0), shadow)
 
  91             image.paste(self.img, (0, 0), self.img)
 
  93             image.paste(self.img, (0, 0))
 
  98     """Abstract base class for cover images generator."""
 
 101     background_color = '#fff'
 
 102     background_img = None
 
 105     author_margin_left = 20
 
 106     author_margin_right = 20
 
 108     author_color = '#000'
 
 110     author_font_ttf = get_resource('fonts/DejaVuSerif.ttf')
 
 111     author_font_size = 30
 
 114     title_margin_left = 20
 
 115     title_margin_right = 20
 
 119     title_font_ttf = get_resource('fonts/DejaVuSerif.ttf')
 
 124     uses_dc_cover = False
 
 135         'JPEG': 'image/jpeg',
 
 139     def __init__(self, book_info, format=None, width=None, height=None):
 
 140         self.author = ", ".join(auth.readable() for auth in book_info.authors)
 
 141         self.title = book_info.title
 
 142         if format is not None:
 
 144         scale = max(float(width or 0) / self.width, float(height or 0) / self.height)
 
 148     def pretty_author(self):
 
 149         """Allows for decorating author's name."""
 
 152     def pretty_title(self):
 
 153         """Allows for decorating title."""
 
 157         metr = Metric(self, self.scale)
 
 158         img = Image.new('RGB', (metr.width, metr.height), self.background_color)
 
 160         if self.background_img:
 
 161             background = Image.open(self.background_img)
 
 162             img.paste(background, None, background)
 
 167             logo = Image.open(get_resource('res/wl-logo.png'))
 
 168             logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0]))
 
 169             img.paste(logo, ((metr.width - metr.logo_width) / 2, img.size[1] - logo.size[1] - metr.logo_bottom))
 
 171         top = metr.author_top
 
 173             metr.width - metr.author_margin_left - metr.author_margin_right,
 
 177         author_font = ImageFont.truetype(
 
 178             self.author_font_ttf, metr.author_font_size)
 
 179         tbox.text(self.pretty_author(), self.author_color, author_font,
 
 180             metr.author_lineskip, self.author_shadow)
 
 181         text_img = tbox.image()
 
 182         img.paste(text_img, (metr.author_margin_left, top), text_img)
 
 184         top += text_img.size[1] + metr.title_top
 
 186             metr.width - metr.title_margin_left - metr.title_margin_right,
 
 189         title_font = ImageFont.truetype(
 
 190             self.title_font_ttf, metr.title_font_size)
 
 191         tbox.text(self.pretty_title(), self.title_color, title_font,
 
 192             metr.title_lineskip, self.title_shadow)
 
 193         text_img = tbox.image()
 
 194         img.paste(text_img, (metr.title_margin_left, top), text_img)
 
 199         return self.mime_types[self.format]
 
 202         return self.exts[self.format]
 
 204     def save(self, *args, **kwargs):
 
 205         return self.image().save(format=self.format, quality=95, *args, **kwargs)
 
 207     def output_file(self, *args, **kwargs):
 
 209         self.save(imgstr, *args, **kwargs)
 
 210         return OutputFile.from_string(imgstr.getvalue())
 
 213 class WLCover(Cover):
 
 214     """Default Wolne Lektury cover generator."""
 
 218     author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf')
 
 219     author_font_size = 20
 
 221     title_font_ttf = get_resource('fonts/DejaVuSerif-Bold.ttf')
 
 224     title_box_width = 350
 
 227     box_bottom_margin = 100
 
 239     background_color = '#444'
 
 240     author_color = '#444'
 
 241     default_background = get_resource('res/cover.png')
 
 245         u'Starożytność': '#9e3610',
 
 246         u'Średniowiecze': '#564c09',
 
 247         u'Renesans': '#8ca629',
 
 249         u'Oświecenie': '#f2802e',
 
 250         u'Romantyzm': '#db4b16',
 
 251         u'Pozytywizm': '#961060',
 
 252         u'Modernizm': '#7784e0',
 
 253         u'Dwudziestolecie międzywojenne': '#3044cf',
 
 254         u'Współczesność': '#06393d',
 
 257     def __init__(self, book_info, format=None, width=None, height=None, with_logo=False):
 
 258         super(WLCover, self).__init__(book_info, format=format, width=width, height=height)
 
 259         self.kind = book_info.kind
 
 260         self.epoch = book_info.epoch
 
 261         self.with_logo = with_logo
 
 262         if book_info.cover_url:
 
 263             url = book_info.cover_url
 
 266                 bg_src = URLOpener().open(url)
 
 267             self.background_img = StringIO(bg_src.read())
 
 270             self.background_img = self.default_background
 
 272     def pretty_author(self):
 
 273         return self.author.upper()
 
 276         metr = Metric(self, self.scale)
 
 277         img = Image.new('RGB', (metr.width, metr.height), self.background_color)
 
 278         draw = ImageDraw.Draw(img)
 
 280         if self.epoch in self.epoch_colors:
 
 281             epoch_color = self.epoch_colors[self.epoch]
 
 284         draw.rectangle((0, 0, metr.bar_width, metr.height), fill=epoch_color)
 
 286         if self.background_img:
 
 287             src = Image.open(self.background_img)
 
 288             trg_size = (metr.width - metr.bar_width, metr.height)
 
 289             if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]:
 
 292                     src.size[1] * trg_size[0] / src.size[0]
 
 294                 cut = (resized[1] - trg_size[1]) / 2
 
 295                 src = src.resize(resized, Image.ANTIALIAS)
 
 296                 src = src.crop((0, cut, src.size[0], src.size[1] - cut))
 
 299                     src.size[0] * trg_size[1] / src.size[1],
 
 302                 cut = (resized[0] - trg_size[0]) / 2
 
 303                 src = src.resize(resized, Image.ANTIALIAS)
 
 304                 src = src.crop((cut, 0, src.size[0] - cut, src.size[1]))
 
 306             img.paste(src, (metr.bar_width, 0))
 
 309         box = TextBox(metr.title_box_width, metr.height, padding_y=metr.box_padding_y)
 
 310         author_font = ImageFont.truetype(
 
 311             self.author_font_ttf, metr.author_font_size)
 
 312         box.text(self.pretty_author(),
 
 314                  line_height=metr.author_lineskip,
 
 315                  color=self.author_color,
 
 316                  shadow_color=self.author_shadow,
 
 319         box.skip(metr.box_above_line)
 
 320         box.draw.line((metr.box_line_left, box.height, metr.box_line_right, box.height),
 
 321                 fill=self.author_color, width=metr.box_line_width)
 
 322         box.skip(metr.box_below_line)
 
 324         title_font = ImageFont.truetype(
 
 325             self.title_font_ttf, metr.title_font_size)
 
 326         box.text(self.pretty_title(),
 
 327                  line_height=metr.title_lineskip,
 
 330                  shadow_color=self.title_shadow,
 
 334             logo = Image.open(get_resource('res/wl-logo-mono.png'))
 
 335             logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0]), Image.ANTIALIAS)
 
 336             alpha = logo.split()[3]
 
 337             alpha = ImageEnhance.Brightness(alpha).enhance(.75)
 
 339             box.skip(metr.logo_top + logo.size[1])
 
 341         box_img = box.image()
 
 343         if self.kind == 'Liryka':
 
 345             box_top = metr.box_top_margin
 
 346         elif self.kind == 'Epika':
 
 348             box_top = metr.height - metr.box_bottom_margin - box_img.size[1]
 
 351             box_top = (metr.height - box_img.size[1]) / 2
 
 353         box_left = metr.bar_width + (metr.width - metr.bar_width -
 
 355         draw.rectangle((box_left, box_top,
 
 356             box_left + box_img.size[0], box_top + box_img.size[1]),
 
 358         img.paste(box_img, (box_left, box_top), box_img)
 
 362                 (box_left + (box_img.size[0] - logo.size[0]) / 2,
 
 363                     box_top + box_img.size[1] - metr.box_padding_y - logo.size[1]), mask=logo)
 
 368 class VirtualoCover(Cover):
 
 377 class PrestigioCover(Cover):
 
 380     background_img = get_resource('res/cover-prestigio.png')
 
 383     author_margin_left = 118
 
 384     author_margin_right = 62
 
 386     author_color = '#fff'
 
 387     author_shadow = '#000'
 
 388     author_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf')
 
 389     author_font_size = 50
 
 392     title_margin_left = 118
 
 393     title_margin_right = 62
 
 396     title_shadow = '#000'
 
 397     title_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf')
 
 400     def pretty_title(self):
 
 401         return u"„%s”" % self.title
 
 404 class BookotekaCover(Cover):
 
 407     background_img = get_resource('res/cover-bookoteka.png')
 
 410     author_margin_left = 307
 
 411     author_margin_right = 233
 
 412     author_lineskip = 156
 
 413     author_color = '#d9d919'
 
 414     author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf')
 
 415     author_font_size = 130
 
 418     title_margin_left = 307
 
 419     title_margin_right = 233
 
 421     title_color = '#d9d919'
 
 422     title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf')
 
 423     title_font_size = 140
 
 428 class GandalfCover(Cover):
 
 431     background_img = get_resource('res/cover-gandalf.png')
 
 432     author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf')
 
 433     author_font_size = 30
 
 434     title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf')