830c7b6c8908aa8f41e77e101b258fee42abf10a
[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         if self.predesigned:
85             bg = Background(self, crop_to_square=False)
86             bg.apply(
87                 img,
88                 0, 0,
89                 self.m.width, self.m.height
90             )
91         else:
92             bg = Background(self)
93             if self.square_variant:
94                 layout_options = [
95                     (self.m.marquise_small, 1),
96                     (self.m.marquise_big, 2),
97                     (self.m.marquise_big, 3),
98                     (self.m.marquise_big, None),
99                 ]
100             else:
101                 layout_options = [
102                     (self.m.marquise_small, 2),
103                     (self.m.marquise_small, 1),
104                     (self.m.marquise_big, 3),
105                     (self.m.marquise_xl, 4),
106                     (self.m.marquise_xl, None),
107                 ]
108
109             # Trying all the layout options with decreasing scale.
110             title_box = None
111             title_scale = 1
112             while title_box is None:
113                 for marquise_h, lines in layout_options:
114                     title_box_height = marquise_h - self.m.title_box_top - self.m.margin
115                     try:
116                         title_box = TitleBox(
117                             self,
118                             self.m.width - 2 * self.m.margin,
119                             title_box_height,
120                             lines,
121                             scale=title_scale
122                         )
123                     except DoesNotFit:
124                         continue
125                     else:
126                         break
127                 title_scale *= .99
128
129             self.marquise_height = marquise_h
130             marquise = Marquise(self, self.marquise_height)
131
132             bg.apply(
133                 img,
134                 0, marquise.edge_top,
135                 self.m.width, self.m.height - marquise.edge_top
136             )
137             self.set_color_scheme_from(
138                 img.crop((
139                     0, marquise.edge_top,
140                     self.m.width, marquise.edge_top + (
141                         self.m.height - marquise.edge_top
142                     ) / 4
143                 ))
144             )
145
146             marquise.apply(
147                 img, 0, 0, self.m.width
148             )
149             title_box.apply(
150                 img,
151                 marquise.title_box_position[0],
152                 marquise.title_box_position[1],
153             )
154
155             AuthorBox(self, self.m.author_width).apply(
156                 img, self.m.width - self.m.margin - self.m.author_width, self.m.margin
157             )
158             WLLogo(self).apply(img, self.m.margin, self.m.margin, None, self.m.logo_h)
159
160                               
161         for logo in self.additional_logos:
162             LogoSticker(self, logo).apply(img, 0, 0)
163
164
165         return img
166
167
168     
169 class LabelMarquiseCover(MarquiseCover):
170     label_left = 95
171     label_top = 105
172     label_w = 710
173     label_h = 710
174     
175     def image(self):
176         img = super().image()
177         if not self.predesigned:
178             Label(self).apply(
179                 img,
180                 self.m.label_left,
181                 self.marquise_height - self.label_top,
182                 self.m.label_w,
183                 self.m.label_h
184             )
185
186         return img