Drop lots of legacy code. Support Python 3.7-3.11.
[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
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     width = 2100
20     height = 2970
21     margin = 100
22     logo_h = 177
23     author_width = 1300
24
25     title_box_top = 262
26
27     color_schemes = [
28         {
29             'rgb': (0xc3, 0x27, 0x21),
30             'text': '#000',
31         },
32         {
33             'rgb': (0xa0, 0xbf, 0x38),
34             'text': '#000',
35         },
36         {
37             'rgb': (0xed, 0xc0, 0x16),
38             'text': '#000',
39         },
40         {
41             'rgb': (0x47, 0x66, 0x75),
42             'text': '#fff',
43         }
44     ]
45     for c in color_schemes:
46         c['luminance'] = luminance(c['rgb'])
47         cim = PIL.Image.new('RGB', (1, 1))
48         cim.putpixel((0, 0), c['rgb'])
49         cim.convert('HSV')
50         c['hsv'] = cim.getpixel((0, 0))
51
52     
53     def set_size(self, final_width, final_height):
54         if final_width is None:
55             self.m = Metric(self, 1)
56         else:
57             if final_width > self.width:
58                 self.m = Metric(self, final_width / self.width)
59             else:
60                 self.m = Metric(self, 1)
61                 self.scale_after = final_width / self.width
62             
63         if final_height is not None:
64             self.height = round(final_height / self.scale_after / self.m._scale)
65
66         self.square_variant = self.height / self.width < 250 / 210
67
68         marquise_square_small = int(self.width / 2) - 300
69         marquise_square_big = int(self.width / 2) - 100
70         marquise_a4_small = 2970 - self.width
71         marquise_a4_big = marquise_a4_small + 100
72         
73         self.marquise_small = int(round(marquise_square_small + (marquise_a4_small - marquise_square_small) * (self.height - self.width) / (2970 - 2100)))
74         self.marquise_big = int(round(marquise_square_big + (marquise_a4_big - marquise_square_big) * (self.height - self.width) / (2970 - 2100)))
75         self.marquise_xl = self.marquise_big + 200
76
77         if self.marquise_small > self.marquise_big:
78             self.marquise_small = self.marquise_big
79
80     def set_color_scheme_from(self, img):
81         self.color_scheme = algo_contrast_or_hue(img, self.color_schemes)
82         self.is_very_bright = is_very_bright(img)
83
84     def image(self):
85         img = PIL.Image.new('RGB', (self.m.width, self.m.height), self.background_color)
86         
87         if self.predesigned:
88             bg = Background(self, crop_to_square=False)
89             bg.apply(
90                 img,
91                 0, 0,
92                 self.m.width, self.m.height
93             )
94         else:
95             bg = Background(self)
96             if self.square_variant:
97                 layout_options = [
98                     (self.m.marquise_small, 1),
99                     (self.m.marquise_big, 2),
100                     (self.m.marquise_big, 3),
101                     (self.m.marquise_big, None),
102                 ]
103             else:
104                 layout_options = [
105                     (self.m.marquise_small, 2),
106                     (self.m.marquise_small, 1),
107                     (self.m.marquise_big, 3),
108                     (self.m.marquise_xl, 4),
109                     (self.m.marquise_xl, None),
110                 ]
111
112             # Trying all the layout options with decreasing scale.
113             title_box = None
114             title_scale = 1
115             while title_box is None:
116                 for marquise_h, lines in layout_options:
117                     title_box_height = marquise_h - self.m.title_box_top - self.m.margin
118                     try:
119                         title_box = TitleBox(
120                             self,
121                             self.m.width - 2 * self.m.margin,
122                             title_box_height,
123                             lines,
124                             scale=title_scale
125                         )
126                     except DoesNotFit:
127                         continue
128                     else:
129                         break
130                 title_scale *= .99
131
132             self.marquise_height = marquise_h
133             marquise = Marquise(self, self.marquise_height)
134
135             bg.apply(
136                 img,
137                 0, marquise.edge_top,
138                 self.m.width, self.m.height - marquise.edge_top
139             )
140             self.set_color_scheme_from(
141                 img.crop((
142                     0, marquise.edge_top,
143                     self.m.width, marquise.edge_top + (
144                         self.m.height - marquise.edge_top
145                     ) / 4
146                 ))
147             )
148
149             marquise.apply(
150                 img, 0, 0, self.m.width
151             )
152             title_box.apply(
153                 img,
154                 marquise.title_box_position[0],
155                 marquise.title_box_position[1],
156             )
157
158             AuthorBox(self, self.m.author_width).apply(
159                 img, self.m.width - self.m.margin - self.m.author_width, self.m.margin
160             )
161             WLLogo(self).apply(img, self.m.margin, self.m.margin, None, self.m.logo_h)
162
163                               
164         for logo in self.additional_logos:
165             LogoSticker(self, logo).apply(img, 0, 0)
166
167
168         return img
169
170
171     
172 class LabelMarquiseCover(MarquiseCover):
173     label_left = 95
174     label_top = 105
175     label_w = 710
176     label_h = 710
177     
178     def image(self):
179         img = super().image()
180         if not self.predesigned:
181             Label(self).apply(
182                 img,
183                 self.m.label_left,
184                 self.marquise_height - self.label_top,
185                 self.m.label_w,
186                 self.m.label_h
187             )
188
189         return img