wip change fb2 api
[librarian.git] / src / librarian / covers / marquise.py
1 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
3 #
4 import PIL.Image
5 from librarian.cover import Cover, Metric
6 from .utils.color import algo_contrast_or_hue, luminance, is_very_bright
7 from .utils.textbox import DoesNotFit
8 from .widgets.author import AuthorBox
9 from .widgets.background import Background
10 from .widgets.image import WLLogo, Label, LogoSticker
11 from .widgets.marquise import Marquise
12 from .widgets.title import TitleBox
13
14
15 class MarquiseCover(Cover):
16     additional_logos = []
17     square_variant = False
18
19     background_color = '#000'
20     width = 2100
21     height = 2970
22     margin = 100
23     logo_h = 177
24     author_width = 1400
25     sticker_size = 400
26     sticker_padding_x = 30
27     sticker_padding_y = 50
28     sticker_radius = 10
29
30     title_box_top = 262
31
32     color_schemes = [
33         {
34             'rgb': (0xc3, 0x27, 0x21),
35             'text': '#000',
36         },
37         {
38             'rgb': (0xa0, 0xbf, 0x38),
39             'text': '#000',
40         },
41         {
42             'rgb': (0xed, 0xc0, 0x16),
43             'text': '#000',
44         },
45         {
46             'rgb': (0x47, 0x66, 0x75),
47             'text': '#fff',
48         }
49     ]
50     for c in color_schemes:
51         c['luminance'] = luminance(c['rgb'])
52         cim = PIL.Image.new('RGB', (1, 1))
53         cim.putpixel((0, 0), c['rgb'])
54         cim.convert('HSV')
55         c['hsv'] = cim.getpixel((0, 0))
56
57     
58     def set_size(self, final_width, final_height):
59         if final_width is None:
60             self.m = Metric(self, 1)
61         else:
62             if final_width > self.width:
63                 self.m = Metric(self, final_width / self.width)
64             else:
65                 self.m = Metric(self, 1)
66                 self.scale_after = final_width / self.width
67             
68         if final_height is not None:
69             self.height = round(final_height / self.scale_after / self.m._scale)
70
71         self.square_variant = self.height / self.width < 250 / 210
72
73         marquise_square_small = int(self.width / 2) - 300
74         marquise_square_big = int(self.width / 2) - 100
75         marquise_a4_small = 2970 - self.width
76         marquise_a4_big = marquise_a4_small + 100
77         
78         self.marquise_small = int(round(marquise_square_small + (marquise_a4_small - marquise_square_small) * (self.height - self.width) / (2970 - 2100)))
79         self.marquise_big = int(round(marquise_square_big + (marquise_a4_big - marquise_square_big) * (self.height - self.width) / (2970 - 2100)))
80         self.marquise_xl = self.marquise_big + 200
81
82         if self.marquise_small > self.marquise_big:
83             self.marquise_small = self.marquise_big
84
85     def set_color_scheme_from(self, img):
86         self.color_scheme = algo_contrast_or_hue(img, self.color_schemes)
87         self.is_very_bright = is_very_bright(img)
88
89     def image(self):
90         img = PIL.Image.new('RGB', (self.m.width, self.m.height), self.background_color)
91         
92         if self.predesigned:
93             bg = Background(self, crop_to_square=False)
94             bg.apply(
95                 img,
96                 0, 0,
97                 self.m.width, self.m.height
98             )
99         else:
100             bg = Background(self)
101             if self.square_variant:
102                 layout_options = [
103                     (self.m.marquise_small, 1),
104                     (self.m.marquise_big, 2),
105                     (self.m.marquise_big, 3),
106                     (self.m.marquise_big, None),
107                 ]
108             else:
109                 layout_options = [
110                     (self.m.marquise_small, 2),
111                     (self.m.marquise_small, 1),
112                     (self.m.marquise_big, 3),
113                     (self.m.marquise_xl, 4),
114                     (self.m.marquise_xl, None),
115                 ]
116
117             # Trying all the layout options with decreasing scale.
118             title_box = None
119             title_scale = 1
120             while title_box is None:
121                 for marquise_h, lines in layout_options:
122                     title_box_height = marquise_h - self.m.title_box_top - self.m.margin
123                     try:
124                         title_box = TitleBox(
125                             self,
126                             self.m.width - 2 * self.m.margin,
127                             title_box_height,
128                             lines,
129                             scale=title_scale
130                         )
131                     except DoesNotFit:
132                         continue
133                     else:
134                         break
135                 title_scale *= .99
136
137             self.marquise_height = marquise_h
138             marquise = Marquise(self, self.marquise_height)
139
140             bg.apply(
141                 img,
142                 0, marquise.edge_top,
143                 self.m.width, self.m.height - marquise.edge_top
144             )
145             self.set_color_scheme_from(
146                 img.crop((
147                     0, marquise.edge_top,
148                     self.m.width, marquise.edge_top + (
149                         self.m.height - marquise.edge_top
150                     ) / 4
151                 ))
152             )
153
154             marquise.apply(
155                 img, 0, 0, self.m.width
156             )
157             title_box.apply(
158                 img,
159                 marquise.title_box_position[0],
160                 marquise.title_box_position[1],
161             )
162
163             AuthorBox(self, self.m.author_width).apply(
164                 img, self.m.width - self.m.margin - self.m.author_width, self.m.margin
165             )
166             WLLogo(self).apply(img, self.m.margin, self.m.margin, None, self.m.logo_h)
167
168
169         if self.cover_logo:
170             LogoSticker(self, self.cover_logo).apply(
171                 img,
172                 self.m.width - self.m.margin - self.m.sticker_size,
173                 self.m.height - self.m.margin,
174                 self.m.sticker_size, self.m.sticker_size, self.m.sticker_padding_x, self.m.sticker_padding_y, self.m.sticker_radius
175             )
176         for logo in self.additional_logos:
177             LogoSticker(self, logo).apply(img, 0, 0)
178
179
180         return img
181
182
183     
184 class LabelMarquiseCover(MarquiseCover):
185     label_left = 95
186     label_top = 105
187     label_w = 710
188     label_h = 710
189     
190     def image(self):
191         img = super().image()
192         if not self.predesigned:
193             Label(self).apply(
194                 img,
195                 self.m.label_left,
196                 self.marquise_height - self.label_top,
197                 self.m.label_w,
198                 self.m.label_h
199             )
200
201         return img