fix
[audio.git] / src / youtube / thumbnail.py
1 import yaml
2 from PIL import Image, ImageDraw, ImageFont
3
4
5 def split_to_lines(text, draw, font, max_width):
6     words = text.split()
7     current = ''
8     while words:
9         new_words = []
10         new_words.append(words.pop(0))
11         while len(new_words[-1]) == 1 and words:
12             new_words.append(words.pop(0))
13         new_words = ' '.join(new_words)
14         new_line = ' '.join([current, new_words]).strip()
15         width = draw.textsize(new_line, font=font)[0]
16         if width > max_width:
17             yield current
18             current = new_words
19         else:
20             current = new_line
21     if current:
22         yield current
23
24         
25 def draw_box(img, d, context, get_font_path, scale):
26     newimg = Image.new(
27         'RGBA',
28         (
29             img.size[0] - d.get('x', 0),
30             d.get('max-height', img.size[1] - d.get('y', 0)),
31         )
32     )
33
34     draw = ImageDraw.Draw(newimg)
35     cursor = 0
36     for item in d['items']:
37         if item.get('vskip'):
38             cursor += int(round(item['vskip'] * scale))
39         text = item['text'].format(**context)
40         if not text:
41             continue
42         if item.get('uppercase'):
43             text = text.upper()
44         font = ImageFont.truetype(
45             get_font_path(item['font-family']),
46             int(round(item['font-size'] * scale)),
47             layout_engine=ImageFont.LAYOUT_BASIC
48         )
49         max_width = item.get('max-width', newimg.size[0])
50
51         for line in split_to_lines(text, draw, font, max_width):
52             realheight = draw.textsize(line, font=font)[1]
53             if cursor + realheight > newimg.size[1]:
54                 return False
55             draw.text((0, cursor), line, font=font, fill=item.get('color'))
56             cursor += int(round(item['line-height'] * scale))
57
58     img.paste(newimg, (d.get('x', 0), d.get('y', 0)), newimg)
59     return True
60
61
62 def draw_box_with_scaling(img, d, context, get_font_path):
63     scale = 1.0
64     while scale > 0:
65         if draw_box(img, d, context, get_font_path, scale):
66             return True
67         scale -= 0.05
68     
69
70
71 def create_thumbnail(background_path, defn, context, get_font_path):
72     img = Image.open(background_path)
73     d = yaml.safe_load(defn)
74     for boxdef in d['boxes']:
75         if not draw_box_with_scaling(img, boxdef, context, get_font_path):
76             raise ValueError()
77     return img