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.
 
   6 import Image, ImageFont, ImageDraw, ImageFilter
 
   7 from librarian import get_resource
 
  10 class TextBox(object):
 
  11     """Creates an Image with a series of centered strings."""
 
  17     def __init__(self, max_width, max_height, padding_x=None, padding_y=None):
 
  19             padding_x = self.SHADOW_X + self.SHADOW_BLUR
 
  21             padding_y = self.SHADOW_Y + self.SHADOW_BLUR
 
  23         self.max_width = max_width
 
  24         self.max_text_width = max_width - 2 * padding_x
 
  25         self.padding_y = padding_y
 
  26         self.height = padding_y
 
  27         self.img = Image.new('RGBA', (max_width, max_height))
 
  28         self.draw = ImageDraw.Draw(self.img)
 
  29         self.shadow_img = None
 
  30         self.shadow_draw = None
 
  32     def skip(self, height):
 
  33         """Skips some vertical space."""
 
  36     def text(self, text, color='#000', font=None, line_height=20, 
 
  38         """Writes some centered text."""
 
  40             if not self.shadow_img:
 
  41                 self.shadow_img = Image.new('RGBA', self.img.size)
 
  42                 self.shadow_draw = ImageDraw.Draw(self.shadow_img)
 
  45             line_width = self.draw.textsize(line, font=font)[0]
 
  46             while line_width > self.max_text_width:
 
  47                 parts = line.rsplit(' ', 1)
 
  49                     line_width = self.max_text_width
 
  52                 line_width = self.draw.textsize(line, font=font)[0]
 
  53             line = line.strip() + ' '
 
  55             pos_x = (self.max_width - line_width) / 2
 
  58                 self.shadow_draw.text(
 
  59                         (pos_x + self.SHADOW_X, self.height + self.SHADOW_Y),
 
  60                         line, font=font, fill=shadow_color
 
  63             self.draw.text((pos_x, self.height), line, font=font, fill=color)
 
  64             self.height += line_height
 
  66             text = text[len(line):]
 
  69         """Creates the actual Image object."""
 
  70         image = Image.new('RGBA', (self.max_width,
 
  71                                    self.height + self.padding_y))
 
  73             shadow = self.shadow_img.filter(ImageFilter.BLUR)
 
  74             image.paste(shadow, (0, 0), shadow)
 
  75             image.paste(self.img, (0, 0), self.img)
 
  77             image.paste(self.img, (0, 0))
 
  82     """Abstract base class for cover images generator."""
 
  85     background_color = '#fff'
 
  89     author_margin_left = 20
 
  90     author_margin_right = 20
 
  97     title_margin_left = 20
 
  98     title_margin_right = 20
 
 106     uses_dc_cover = False
 
 116         'JPEG': 'image/jpeg',
 
 120     def __init__(self, book_info):
 
 121         self.author = ", ".join(auth.readable() for auth in book_info.authors)
 
 122         self.title = book_info.title
 
 124     def pretty_author(self):
 
 125         """Allows for decorating author's name."""
 
 128     def pretty_title(self):
 
 129         """Allows for decorating title."""
 
 133         img = Image.new('RGB', (self.width, self.height), self.background_color)
 
 135         if self.background_img:
 
 136             background = Image.open(self.background_img)
 
 137             img.paste(background, None, background)
 
 142             logo = Image.open(get_resource('res/wl-logo.png'))
 
 143             logo = logo.resize((self.logo_width, logo.size[1] * self.logo_width / logo.size[0]))
 
 144             img.paste(logo, ((self.width - self.logo_width) / 2, img.size[1] - logo.size[1] - self.logo_bottom))
 
 146         top = self.author_top
 
 148             self.width - self.author_margin_left - self.author_margin_right,
 
 151         author_font = self.author_font or ImageFont.truetype(
 
 152             get_resource('fonts/DejaVuSerif.ttf'), 30)
 
 153         tbox.text(self.pretty_author(), self.author_color, author_font,
 
 154             self.author_lineskip, self.author_shadow)
 
 155         text_img = tbox.image()
 
 156         img.paste(text_img, (self.author_margin_left, top), text_img)
 
 158         top += text_img.size[1] + self.title_top
 
 160             self.width - self.title_margin_left - self.title_margin_right,
 
 163         title_font = self.author_font or ImageFont.truetype(
 
 164             get_resource('fonts/DejaVuSerif.ttf'), 40)
 
 165         tbox.text(self.pretty_title(), self.title_color, title_font,
 
 166             self.title_lineskip, self.title_shadow)
 
 167         text_img = tbox.image()
 
 168         img.paste(text_img, (self.title_margin_left, top), text_img)
 
 173         return self.mime_types[self.format]
 
 176         return self.exts[self.format]
 
 178     def save(self, *args, **kwargs):
 
 179         return self.image().save(format=self.format, *args, **kwargs)
 
 182 class WLCover(Cover):
 
 183     """Default Wolne Lektury cover generator."""
 
 185     author_font = ImageFont.truetype(
 
 186         get_resource('fonts/JunicodeWL-Regular.ttf'), 20)
 
 188     title_font = ImageFont.truetype(
 
 189         get_resource('fonts/DejaVuSerif-Bold.ttf'), 30)
 
 191     title_box_width = 350
 
 193     background_color = '#444'
 
 194     author_color = '#444'
 
 198         u'Średniowiecze': 30,
 
 205         u'Dwudziestolecie międzywojenne': 240,
 
 206         u'Współczesność': 270,
 
 209     def __init__(self, book_info):
 
 210         super(WLCover, self).__init__(book_info)
 
 211         self.kind = book_info.kind
 
 212         self.epoch = book_info.epoch
 
 213         if book_info.cover_url:
 
 214             from urllib2 import urlopen
 
 215             from StringIO import StringIO
 
 217             bg_src = urlopen(book_info.cover_url)
 
 218             self.background_img = StringIO(bg_src.read())
 
 221     def pretty_author(self):
 
 222         return self.author.upper()
 
 225         from colorsys import hsv_to_rgb
 
 227         img = Image.new('RGB', (self.width, self.height), self.background_color)
 
 228         draw = ImageDraw.Draw(img)
 
 230         if self.epoch in self.epochs:
 
 231             epoch_color = tuple(int(255 * c) for c in hsv_to_rgb(
 
 232                     float(self.epochs[self.epoch]) / 360, .7, .7))
 
 235         draw.rectangle((0, 0, self.bar_width, self.height), fill=epoch_color)
 
 237         if self.background_img:
 
 238             src = Image.open(self.background_img)
 
 239             trg_size = (self.width - self.bar_width, self.height)
 
 240             if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]:
 
 243                     src.size[1] * trg_size[0] / src.size[0]
 
 245                 cut = (resized[1] - trg_size[1]) / 2
 
 246                 src = src.resize(resized)
 
 247                 src = src.crop((0, cut, src.size[0], src.size[1] - cut))
 
 250                     src.size[0] * trg_size[1] / src.size[1],
 
 253                 cut = (resized[0] - trg_size[0]) / 2
 
 254                 src = src.resize(resized)
 
 255                 src = src.crop((cut, 0, src.size[0] - cut, src.size[1]))
 
 257             img.paste(src, (self.bar_width, 0))
 
 260         box = TextBox(self.title_box_width, self.height, padding_y=20)
 
 261         box.text(self.pretty_author(), 
 
 262                  font=self.author_font,
 
 263                  line_height=self.author_lineskip,
 
 264                  color=self.author_color,
 
 265                  shadow_color=self.author_shadow,
 
 269         box.draw.line((75, box.height, 275, box.height), 
 
 270                 fill=self.author_color, width=2)
 
 273         box.text(self.pretty_title(),
 
 274                  line_height=self.title_lineskip,
 
 275                  font=self.title_font,
 
 277                  shadow_color=self.title_shadow,
 
 279         box_img = box.image()
 
 281         if self.kind == 'Liryka':
 
 284         elif self.kind == 'Epika':
 
 286             box_top = self.height - 100 - box_img.size[1]
 
 289             box_top = (self.height - box_img.size[1]) / 2
 
 291         box_left = self.bar_width + (self.width - self.bar_width - 
 
 293         draw.rectangle((box_left, box_top, 
 
 294             box_left + box_img.size[0], box_top + box_img.size[1]),
 
 296         img.paste(box_img, (box_left, box_top), box_img)
 
 302 class VirtualoCover(Cover):
 
 311 class PrestigioCover(Cover):
 
 314     background_img = get_resource('res/cover-prestigio.png')
 
 317     author_margin_left = 118
 
 318     author_margin_right = 62
 
 320     author_color = '#fff'
 
 321     author_shadow = '#000'
 
 322     author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Italic.ttf'), 50)
 
 325     title_margin_left = 118
 
 326     title_margin_right = 62
 
 329     title_shadow = '#000'
 
 330     title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Italic.ttf'), 50)
 
 332     def pretty_title(self):
 
 333         return u"„%s”" % self.title
 
 336 class BookotekaCover(Cover):
 
 339     background_img = get_resource('res/cover-bookoteka.png')
 
 342     author_margin_left = 307
 
 343     author_margin_right = 233
 
 344     author_lineskip = 156
 
 345     author_color = '#d9d919'
 
 346     author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 130)
 
 349     title_margin_left = 307
 
 350     title_margin_right = 233
 
 352     title_color = '#d9d919'
 
 353     title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 140)
 
 358 class GandalfCover(Cover):
 
 361     background_img = get_resource('res/cover-gandalf.png')
 
 362     author_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 30)
 
 363     title_font = ImageFont.truetype(get_resource('fonts/JunicodeWL-Regular.ttf'), 40)