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