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 from PIL import Image, ImageFont, ImageDraw, ImageFilter, ImageEnhance
 
   8 from StringIO import StringIO
 
   9 from librarian import DCNS, URLOpener
 
  10 from librarian.output import OutputFile
 
  11 from librarian.utils import get_resource
 
  12 from librarian.formats import Format
 
  16     """Gets metrics from an object, scaling it by a factor."""
 
  17     def __init__(self, obj, scale):
 
  19         self._scale = float(scale)
 
  21     def __getattr__(self, name):
 
  22         src = getattr(self._obj, name)
 
  23         if src and self._scale:
 
  24             src = type(src)(self._scale * src)
 
  28 class TextBox(object):
 
  29     """Creates an Image with a series of centered strings."""
 
  35     def __init__(self, max_width, max_height, padding_x=None, padding_y=None):
 
  37             padding_x = self.SHADOW_X + self.SHADOW_BLUR
 
  39             padding_y = self.SHADOW_Y + self.SHADOW_BLUR
 
  41         self.max_width = max_width
 
  42         self.max_text_width = max_width - 2 * padding_x
 
  43         self.padding_y = padding_y
 
  44         self.height = padding_y
 
  45         self.img = Image.new('RGBA', (max_width, max_height))
 
  46         self.draw = ImageDraw.Draw(self.img)
 
  47         self.shadow_img = None
 
  48         self.shadow_draw = None
 
  50     def skip(self, height):
 
  51         """Skips some vertical space."""
 
  54     def text(self, text, color='#000', font=None, line_height=20,
 
  56         """Writes some centered text."""
 
  57         text = re.sub(r'\s+', ' ', text)
 
  59             if not self.shadow_img:
 
  60                 self.shadow_img = Image.new('RGBA', self.img.size)
 
  61                 self.shadow_draw = ImageDraw.Draw(self.shadow_img)
 
  64             line_width = self.draw.textsize(line, font=font)[0]
 
  65             while line_width > self.max_text_width:
 
  66                 parts = line.rsplit(' ', 1)
 
  68                     line_width = self.max_text_width
 
  71                 line_width = self.draw.textsize(line, font=font)[0]
 
  72             line = line.strip() + ' '
 
  74             pos_x = (self.max_width - line_width) / 2
 
  77                 self.shadow_draw.text(
 
  78                         (pos_x + self.SHADOW_X, self.height + self.SHADOW_Y),
 
  79                         line, font=font, fill=shadow_color
 
  82             self.draw.text((pos_x, self.height), line, font=font, fill=color)
 
  83             self.height += line_height
 
  85             text = text[len(line):]
 
  88         """Creates the actual Image object."""
 
  89         image = Image.new('RGBA', (self.max_width,
 
  90                                    self.height + self.padding_y))
 
  92             shadow = self.shadow_img.filter(ImageFilter.BLUR)
 
  93             image.paste(shadow, (0, 0), shadow)
 
  94             image.paste(self.img, (0, 0), self.img)
 
  96             image.paste(self.img, (0, 0))
 
 101     """Base class for cover images generator."""
 
 102     format_name = u"cover image"
 
 106     background_color = '#fff'
 
 107     background_img = None
 
 110     author_margin_left = 20
 
 111     author_margin_right = 20
 
 113     author_color = '#000'
 
 115     author_font_ttf = get_resource('fonts/DejaVuSerif.ttf')
 
 116     author_font_size = 30
 
 119     title_margin_left = 20
 
 120     title_margin_right = 20
 
 124     title_font_ttf = get_resource('fonts/DejaVuSerif.ttf')
 
 129     logo_file = get_resource('res/wl-logo.png')
 
 130     uses_dc_cover = False
 
 141         'JPEG': 'image/jpeg',
 
 145     def __init__(self, doc, format=None, width=None, height=None):
 
 146         self.author = ", ".join(auth for auth in doc.meta.get(DCNS('creator')))
 
 147         self.title = doc.meta.title()
 
 148         if format is not None:
 
 150         scale = max(float(width or 0) / self.width, float(height or 0) / self.height)
 
 154     def pretty_author(self):
 
 155         """Allows for decorating author's name."""
 
 158     def pretty_title(self):
 
 159         """Allows for decorating title."""
 
 163         metr = Metric(self, self.scale)
 
 164         img = Image.new('RGB', (metr.width, metr.height), self.background_color)
 
 166         if self.background_img:
 
 167             background = Image.open(self.background_img)
 
 168             resized = background.resize((1024, background.height*1024/background.width), Image.ANTIALIAS)
 
 169             resized = resized.convert('RGBA')
 
 170             img.paste(resized, (0, 0), resized)
 
 171             del background, resized
 
 174             logo = Image.open(self.logo_file)
 
 175             logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0]), Image.ANTIALIAS)
 
 176             logo = logo.convert('RGBA')
 
 177             img.paste(logo, ((metr.width - metr.logo_width) / 2,
 
 178                              img.size[1] - logo.size[1] - metr.logo_bottom), logo)
 
 180         top = metr.author_top
 
 182             metr.width - metr.author_margin_left - metr.author_margin_right,
 
 186         author_font = ImageFont.truetype(
 
 187             self.author_font_ttf, metr.author_font_size)
 
 188         tbox.text(self.pretty_author(), self.author_color, author_font,
 
 189             metr.author_lineskip, self.author_shadow)
 
 190         text_img = tbox.image()
 
 191         img.paste(text_img, (metr.author_margin_left, top), text_img)
 
 193         top += text_img.size[1] + metr.title_top
 
 195             metr.width - metr.title_margin_left - metr.title_margin_right,
 
 198         title_font = ImageFont.truetype(
 
 199             self.title_font_ttf, metr.title_font_size)
 
 200         tbox.text(self.pretty_title(), self.title_color, title_font,
 
 201             metr.title_lineskip, self.title_shadow)
 
 202         text_img = tbox.image()
 
 203         img.paste(text_img, (metr.title_margin_left, top), text_img)
 
 207         img.save(imgstr, format=self.format, quality=95)
 
 208         OutputFile.from_string(imgstr.getvalue())
 
 211         return self.mime_types[self.format]
 
 214     def format_ext(self):
 
 215         return self.exts[self.format]
 
 217     def save(self, *args, **kwargs):
 
 218         return self.image().save(format=self.format, quality=95, *args, **kwargs)
 
 220     def build(self, *args, **kwargs):
 
 222         self.save(imgstr, *args, **kwargs)
 
 223         return OutputFile.from_string(imgstr.getvalue())